Contexto Histórico

O Therac-25 foi um acelerador linear de radioterapia desenvolvido pela Atomic Energy of Canada Limited (AECL) e lançado em 1982. Entre 1985 e 1987, pelo menos seis pacientes receberam overdoses massivas de radiação, resultando em três mortes confirmadas e seqüelas permanentes nos sobreviventes.

Ao contrário de seus predecessores (Therac-6 e Therac-20), que possuíam interlocks de hardware independentes do software, o Therac-25 delegou toda a segurança ao software — escrito em assembly para o PDP-11 e reaproveitado das versões anteriores sem auditoria completa de segurança.

O caso é considerado o estudo de caso mais citado da história da segurança de software e da Interação Humano-Computador. As falhas não eram apenas técnicas: envolviam design de interface deficiente, cultura organizacional de minimização de riscos e normalização do risco pelos operadores ao longo de meses de uso.

A investigação de Leveson & Turner (1993) identificou que os bugs existiam nos sistemas anteriores mas eram inofensivos porque o hardware os mascarava. A decisão de remover os interlocks físicos para reduzir custo foi o ponto de inflexão.


Bugs Implementados na Simulação
MALFUNCTION 54 Race Condition — Cursor-Up Durante Rotação do Disco Óptico ► DETALHES
Descrição técnica

O Therac-25 usava um disco óptico rotativo para posicionar fisicamente o colimador entre o modo Elétron e o modo Raio-X. A rotação levava aproximadamente 8 segundos. Durante esse intervalo, o terminal VT100 continuava aceitando entradas normalmente — inclusive o pressionamento de para editar campos já confirmados.

Quando o operador usava para corrigir o modo após o armamento, o display atualizava imediatamente, mas o disco continuava rotacionando para a posição original (já travada em hardware). Resultado: display mostrava modo E (Elétron) enquanto o hardware disparava feixe X (Raio-X) a 25 MeV — sem o filtro de tungstênio que converte o feixe de alta energia em raios-X terapêuticos.

A variável de controle do modo era compartilhada entre a rotina de display e a rotina de controle do hardware, sem sincronização. A atualização via cursor-up chegava ao display antes de ser processada pelo subsistema de hardware.

Acidente histórico — Tyler, TX (março de 1987):
Um técnico percebeu que havia digitado modo X por engano e usou cursor-up para corrigir para E. O display confirmou E. O hardware, entretanto, havia iniciado o travamento mecânico para X. O feixe disparou em modo X/25 MeV sem o colimador correto. O paciente recebeu estimados 16.000–25.000 rad (dose terapêutica normal: ~200 rad). Sobreviveu com lesões graves permanentes. A mensagem exibida ao operador foi apenas: MALFUNCTION 54.
Como reproduzir na simulação
  1. Preencha todos os 5 campos — use modo X (Raio-X)
  2. Pressione Enter no último campo → sistema entra em ARMADO
  3. Observe o contador do disco óptico (8 segundos) na tela
  4. Pressione para voltar ao campo MODO FEIXE
  5. Digite E (Elétron) e pressione Enter
  6. Navegue até o último campo e confirme → sistema reentra em ARMADO
  7. Pressione BMALFUNCTION 54
A coluna CONFIRMADO ficará em âmbar mostrando o modo físico travado (X), divergindo do PRESCRITO (E). Esta é a representação visual do mismatch que o operador real não conseguia ver no terminal original.
Análise de IHC — três princípios violados simultaneamente:

1. Visibilidade do estado do sistema (Nielsen #1): O display atualizava instantaneamente ao cursor-up, criando a ilusão de que a mudança havia sido aplicada ao hardware. O operador recebeu confirmação visual falsa sobre o estado físico real da máquina.

2. Feedback imediato e acurado: Não havia indicação visual do estado físico do disco óptico — o operador não sabia que o hardware estava em processo de travamento irrevogável. A latência de 8 segundos era invisível na interface.

3. Mensagens de erro acionáveis (Nielsen #9): "MALFUNCTION 54" não informa a causa, o estado resultante nem a ação corretiva. O operador era deixado sem contexto para tomar qualquer decisão segura. A instrução implícita era pressionar [P] para prosseguir.
MALFUNCTION 5 Overflow de Contador — Bug Booleano 1/256 (Yakima) ► DETALHES
Descrição técnica

Uma variável de 1 byte (Class3) era usada como flag booleana para indicar se a verificação de posição do turntable havia sido realizada. O código usava x = x + 1 para sinalizar "verificado" em vez de x = 1.

A cada 256ª execução, o incremento causava overflow de um byte (255 + 1 = 0). O sistema interpretava o valor 0 como "não verificado" e pulava silenciosamente a verificação de posição do colimador. O feixe de 25 MeV disparava com o turntable em posição incorreta (modo campo-luz).

O pior aspecto: após o disparo irregular, o monitor de unidades exibia 0 UM — zero monitor units. O operador interpretava isso como "nada aconteceu", repetia o tratamento, e o paciente recebia nova dose letal.

Na simulação, o intervalo é 1/8 (em vez de 1/256) para que o fenômeno seja observável em uma sessão pedagógica.

Acidente histórico — Yakima, WA (1987):
O contador havia completado múltiplos ciclos ao longo de semanas de uso normal. Na sessão crítica, a verificação foi pulada. O feixe disparou à potência máxima com o turntable em modo campo-luz. O display mostrou "DOSE: 0 MONITOR UNITS" — feedback ativamente enganoso. O operador, acreditando que nenhuma dose foi administrada, pressionou [P] repetidas vezes, administrando múltiplas doses letais. A vítima morreu semanas depois em decorrência das lesões por radiação.
Como reproduzir na simulação
  1. Execute sessões normais e bem-sucedidas com B
  2. Na 8ª execução acumulada, ocorrerá MALFUNCTION 5
  3. O contador não é zerado pelo R de reset — persiste entre sessões. Apenas o botão LIMPAR SESSÃO no painel instrutor zera o contador.
O Painel Instrutor exibirá a nota pedagógica "BUG OVERFLOW 1/256" explicando o que ocorreu internamente no momento da falha.
Análise de IHC — Feedback falso-positivo e normalização do risco:

Este é o caso mais grave de feedback falso-positivo na história do design de interface: a máquina confirmou explicitamente que nenhuma dose foi administrada quando, na realidade, a dose máxima havia sido entregue.

Combinado com a normalização do risco — MALFUNCTIONs eram tão frequentes e geralmente benignas que os operadores desenvolveram o reflexo de pressionar [P] sem avaliar o contexto — o resultado foi que a interface explorou o comportamento aprendido para provocar exposições letais repetidas.

Lição de design: sistemas críticos nunca devem mostrar "0" ou "nenhuma ação" quando uma ação de alto risco foi executada. O silêncio e o zero são estados de confirmação que o usuário usará para tomar decisões.
MALFUNCTION 33 Race Condition — Entrada Rápida / Variável Compartilhada ► DETALHES
Descrição técnica

No Therac-25 real, uma única variável compartilhada era usada tanto para analisar valores de entrada do teclado quanto para rastrear a posição do turntable. Quando o operador navegava rapidamente entre campos, a rotina de tratamento de teclado e a rotina de controle de hardware podiam ler e escrever nessa variável simultaneamente — sem nenhum mecanismo de exclusão mútua.

O resultado era um estado inconsistente: os parâmetros exibidos no display não correspondiam ao que havia sido enviado ao hardware de controle. O sistema aceitava a configuração como válida e armava a máquina com parâmetros desconhecidos.

Contexto histórico — o paradoxo do operador experiente:
Esta condição era mais provável com operadores experientes e rápidos, que completavam o formulário em poucos segundos — o inverso do esperado em segurança. O tempo de confirmação de campo < 400ms era a janela crítica. O treinamento e a experiência tornavam o operador mais suscetível ao bug, não menos.
Como reproduzir na simulação
  1. Preencha cada campo e pressione Enter muito rapidamente
  2. Intervalo crítico: menos de 400ms entre cada confirmação
  3. Após 3 ou mais confirmações rápidas consecutivas, o sistema registra a condição
  4. Pressione Enter no último campo → MALFUNCTION 33
WARN 12 e WARN 13 aparecerão durante o processo. O sistema não bloqueia as entradas em tempo real — apenas registra a condição e falha ao registrar a configuração. Este comportamento é autêntico: o Therac-25 real também não alertava durante a entrada.
Análise de IHC — Design que pune usuários especialistas:

Sistemas seguros devem ser igualmente seguros para iniciantes e especialistas. O Therac-25 violava esse princípio: quanto mais proficiente o operador, maior a probabilidade de ativar a race condition.

Princípio de Nielsen violado (#7 — Flexibilidade e eficiência de uso): atalhos e eficiência para usuários experientes não devem comprometer a segurança do sistema.

A ausência de feedback visual durante a digitação rápida — sem indicador de "processando", sem confirmação progressiva — reforçava o comportamento perigoso de entrada rápida ao não penalizá-lo até o momento do registro final.
MALFUNCTION 44 Comando Fora de Ordem — Estado ≠ ARMADO ► DETALHES
Descrição técnica

O comando EXECUTAR (tecla B) era registrado pelo sistema independentemente do estado atual da máquina. Com latência de processamento ativa, o operador podia pressionar B enquanto o sistema ainda estava em VALIDANDO — antes que a transição para ARMADO fosse concluída.

O sistema recebia um comando válido (B) em um estado inválido para esse comando. Em vez de ignorar silenciosamente ou aguardar, o sistema entrava em FAULT.

Como reproduzir na simulação
  1. Pressione B em qualquer estado que não seja ARMADO
  2. Com latência ativa (slider > 500ms no painel instrutor): pressione B imediatamente após o último Enter, antes que a transição para ARMADO seja concluída
Análise de IHC — Latência e percepção de estado:

Este bug demonstra como latência de interface destrói a percepção do estado do sistema pelo operador. Quando o sistema demora a responder, o operador age com base no estado que imagina que o sistema está, não no estado real.

A solução óbvia de IHC: desabilitar visualmente e funcionalmente a tecla B durante estados que não sejam ARMADO. O Therac-25 não fazia isso — o terminal VT100 aceitava qualquer tecla em qualquer momento, delegando toda a lógica de controle ao software do PDP-11.
MALFUNCTION 21 Transição de Estado Inválida — Inconsistência Interna ► DETALHES
Descrição técnica

Esta falha é acionada quando o sistema tenta fazer uma transição de estado não prevista na máquina de estados finitos (ex.: de IDLE diretamente para ARMED). No Therac-25 real, inconsistências de estado resultavam de corrupção de memória, race conditions e reentrada em rotinas não reentrantes.

Análise de IHC — Falhas internas invisíveis ao operador:

No sistema real, inconsistências de estado raramente eram detectadas e reportadas. O sistema continuava operando em estado desconhecido, potencialmente executando ações com parâmetros corrompidos. A MALFUNCTION 21 na simulação representa esse comportamento de detecção tardia de estado inválido.

Limitações de Arquitetura da Época

Os bugs do Therac-25 não existiam no vácuo: eram consequências diretas de decisões de arquitetura de hardware e software típicas dos anos 1970–1980, muitas das quais eram consideradas práticas normais à época.

PDP-11 / ASSEMBLY (DEC, 1970)

O software foi escrito em assembly PDP-11, operando diretamente sobre registradores e memória. Sem tipos de dados, sem verificação de overflow em tempo de compilação, sem abstrações de segurança. O bug x = x + 1 era sintaticamente válido e invisível ao montador — responsabilidade total do programador.

SISTEMA MONOTAREFA

O PDP-11 rodava um único loop principal de polling. Não havia preempção de processos, nem primitivas de sincronização (mutex, semáforo, monitor). Variáveis compartilhadas entre a rotina de interrupção de teclado e o loop de controle de hardware eram a norma da época — e a fonte direta das race conditions.

TERMINAL VT100 (DEC, 1978)

O VT100 era um terminal passivo: exibia o que o host enviava, aceitava qualquer tecla e a transmitia ao PDP-11 sem validação local. Não havia conceito de "campo bloqueado", "estado de espera" ou feedback de processamento. Se o software não bloqueava, o terminal aceitava — e o software raramente bloqueava.

REMOÇÃO DOS INTERLOCKS DE HARDWARE

O Therac-20 possuía circuitos de hardware independentes que verificavam a posição do colimador antes de liberar o feixe — independentemente do software. O Therac-25 removeu esses circuitos para reduzir custo e complexidade, delegando 100% da responsabilidade de segurança ao software. Essa única decisão arquitetural transformou bugs latentes em falhas letais.

REUSO DE CÓDIGO SEM AUDITORIA

O código foi reaproveitado do Therac-20. Bugs que eram inofensivos no Therac-20 (mascarados pelos interlocks de hardware) tornaram-se letais no Therac-25. A premissa de que "o código já foi testado no produto anterior" impediu a reavaliação de segurança no novo contexto de operação.

CULTURA E PROCESSO ORGANIZACIONAL

Relatórios de acidente anteriores ao caso Tyler foram descartados pela AECL como "erro do operador". Não havia processo formal de análise de falhas de campo nem canal de comunicação entre unidades hospitalares afetadas. Quando o software foi apontado como causa, a AECL resistiu — a reputação do sistema era parte do argumento de defesa perante reguladores.


Princípios de IHC Violados — Síntese

Analisados à luz das heurísticas de Nielsen (1994) e de princípios modernos de design de sistemas críticos:

#1 — VISIBILIDADE DO ESTADO DO SISTEMA

O operador nunca sabia o estado real do hardware — apenas o que o display exibia. Com race conditions e latência, o display era sistematicamente atrasado ou incorreto. O estado físico do disco óptico (rotacionando vs. posicionado) era completamente invisível na interface. (Heurística de Nielsen #1)

#2 — CORRESPONDÊNCIA SISTEMA-MUNDO REAL

O mismatch entre parâmetros do display (PRESCRITO) e o estado físico (CONFIRMADO) não era visível no terminal original. O operador via um único valor — que podia não corresponder ao estado real do hardware. A simulação TELERX-25 torna esse mismatch explícito com a coluna CONFIRMADO em âmbar. (Heurística de Nielsen #2)

#3 — CONTROLE E LIBERDADE DO USUÁRIO

A tecla [P] PROSSEGUIR era apresentada como a principal saída de qualquer MALFUNCTION, mesmo quando prosseguir era a ação mais perigosa possível. A interface não oferecia "saída segura" clara — o [R] RESET era apresentado como opção secundária, menos prominente. A simulação reproduz isso: [P] aparece em âmbar (primário), [R] em verde-escuro (secundário). (Heurística de Nielsen #3)

#5 — PREVENÇÃO DE ERROS

Nenhum mecanismo impedia o operador de: pressionar [B] fora do estado ARMADO; editar campos após armamento; prosseguir após múltiplas MALFUNCTIONs consecutivas do mesmo tipo. O design assumia que o operador sempre tomaria a decisão correta, sem oferece qualquer barreira ou confirmação adicional. (Heurística de Nielsen #5)

#6 — RECONHECIMENTO EM VEZ DE MEMORIZAÇÃO

Operadores precisavam memorizar o significado de cada código de MALFUNCTION. "MALFUNCTION 54" não diz o que aconteceu, o que significa nem o que fazer. A documentação de referência não estava integrada ao terminal — estava em manuais físicos separados, inacessíveis durante a operação. (Heurística de Nielsen #6)

#9 — AJUDAR USUÁRIOS A RECONHECER, DIAGNOSTICAR E RECUPERAR DE ERROS

As mensagens de erro violavam sistematicamente os três requisitos: não descreviam o problema em linguagem clara, não indicavam a causa e não sugeriam uma solução construtiva. "MALFUNCTION XX" era o único feedback disponível para erros potencialmente letais. (Heurística de Nielsen #9)

NORMALIZAÇÃO DO RISCO (Vaughan, 1996)

Além das heurísticas de usabilidade, o caso ilustra o conceito sociológico de normalização do risco: quando desvios de comportamento esperado ocorrem repetidamente sem consequências aparentes, os operadores passam a tratá-los como normais. MALFUNCTIONs eram tão frequentes que os operadores desenvolveram reflexo de pressionar [P] — e a interface reforçava esse reflexo ao apresentar [P] como resposta padrão.


Simplificações e Bugs Não Implementados

Por razões pedagógicas ou de escopo, alguns comportamentos do Therac-25 real não estão presentes na simulação:

DISPLAY "0 DOSE GIVEN" — IMPLEMENTADO

Na 8ª execução acumulada, o sistema não vai para FAULT: exibe "SESSÃO FINALIZADA" normalmente, mas a coluna CONFIRMADO das UNIDADES MON. passa a mostrar 0 enquanto PRESCRITO mantém o valor prescrito (ex.: 200). O mismatch em âmbar indica que "0 UM foram confirmados ao hardware". O operador interpreta isso como nenhuma dose administrada e tende a repetir — exatamente o comportamento observado no acidente de Yakima. O Painel Instrutor exibe a nota pedagógica com o MALFUNCTION 5 real, invisível ao operador.

DIVISÃO POR ZERO / POTÊNCIA MÁXIMA — NÃO IMPLEMENTADO

Um quarto bug descrito em algumas análises: divisão pela variável de potência do feixe poderia causar divisão por zero, levando o sistema a configurar a potência máxima silenciosamente. Este bug não está implementado na simulação atual.

INTERVALO 1/256 SIMPLIFICADO PARA 1/8

O overflow real ocorria a cada 256 execuções — semanas de operação normal, tornando a falha estatisticamente rara e difícil de associar causalmente ao software durante a investigação inicial. Na simulação, o intervalo é 1/8 para que o fenômeno seja observável em uma sessão pedagógica de 30 minutos.

AUSÊNCIA DE CONFIRMAÇÃO RESUMIDA PRÉ-EXECUÇÃO

No terminal VT100 original não havia tela de confirmação com resumo dos parâmetros antes de executar. O operador confiava em memória de curto prazo. A simulação oferece as colunas PRESCRITO/CONFIRMADO como melhoria didática — o terminal real não as tinha.


Referências Originais