1. 개요
Therac-25는 1982년 캐나다 원자력공사(Atomic Energy of Canada Limited, 이하 AECL)[1]에서 개발한 방사선 치료 기기이다. 하지만 이 기기의 선형가속기를 제어하는 소프트웨어의 결함으로 정상치의 수백, 수천 배에 달하는 방사선이 방출되는 사고가 수 차례나 발생하였고, 이 때문에 수 명의 환자들이 방사선 피폭으로 고통받고 사망하였다.이 사고는 미션 크리티컬(mission critical)한 분야에서의 안전한 프로그래밍 기법의 중요성을 알리는 계기가 되었고, 기술 윤리와 소프트웨어 공학의 관점에서도 큰 영향을 끼쳤다.
2. 상세
AECL은 원가 절감을 위해 (구형 모델인 Therac-6이나 Therac-20에는 있었던) 안전 제어장치와 인터락 하드웨어를 제거하고, DEC PDP-11/23 미니컴퓨터 상의 소프트웨어에서 안전 관련 기능을 제어하도록 설계를 변경하였다. 기기의 운영자는 PDP-11에 연결된 VT100 터미널의 TUI 인터페이스를 통해 방사선의 출력, 종류, 좌표, 방출 시간 등을 설정할 수 있었다.이 소프트웨어는 버그투성이어서 시도때도 없이 오류가 나곤 했는데, 이 경우 "MALFUNCTION [숫자 오류 코드]"라는 에러 메시지가 표시되었다. MALFUNCTION 에러 메시지가 출력되더라도 대부분은 큰 문제가 아니었기 때문에, F 키를 누르면 오류를 무시하고 계속 치료의 진행이 가능했다. 하지만 방사선 감지용 이온 챔버가 포화 상태라든가, 과도한 방사선이 방출되었다든가 하는 치명적인 오류 상황에서도 똑같이 "MALFUNCTION"이라는 오류가 출력된다는 사실은 매뉴얼에도 실려 있지 않았고, 제조사의 사용법 교육에서도 알려 주지 않았다.
이 제어용 소프트웨어는 구버전인 Therac-6에서 사용된 프로그램을 수정한 것으로 PDP-11 어셈블리로 개발되었으며, 이 시대의 전용 프로그램들이 흔히 그랬듯이 별도의 운영체제가 없는 실시간(realtime) 프로그램이었다. 따라서 스케줄러, 인터럽트 핸들러 루틴 등 기초적인 OS의 기능이 프로그램에 내장되어 있었다.
놀라운 것은 이 복잡한 작업에 투입된 프로그래머가 단 한 명이었다는 점이다. 신원이 밝혀지지 않은 이 프로그래머는 혼자서 코드 수정, 추가 기능 개발, 디버깅까지 모든 업무를 처리해야 했다.
한편 이러한 버그의 종류는 한 가지가 아니었으며, 여러 버그가 각기 다른 사고를 발생시켰다.
2.1. 미국 텍사스 타일러, 동부 텍사스 암센터(ETCC)에서의 사례
1986년 3월, Therac-25의 터미널에 처방 데이터를 입력하던 암센터 직원이 실수로 전자선치료(e)가 아닌 광자선치료(x)를 눌렀다. 실수를 알아차린 직원은 바로 윗쪽 방향키(↑)를 눌러 x를 e로 수정한 뒤, 엔터 키를 여러 번 쳐서 입력을 마치고 b 키를 눌러 치료를 개시했다.그러자 잠깐 돌아가던 Therac-25는 MALFUNCTION 54 오류를 출력하면서 작동을 멈췄다. MALFUNCTION 에러 코드가 나오면 전체 시스템을 셧다운시키지 않고 간단히 일시정지시킬 뿐이었으므로 직원은 p 키를 눌러 치료를 계속했다. 이후 환자는 팔이 전기 쇼크로 떨어져나가는 듯한 고통을 느껴, 치료실 문 쪽으로 달려나가 문을 두들겼고, 이를 본 직원은 바로 의사를 불렀다. 이 사고로 환자는 추정 165~250그레이의 방사선에 노출되었으며, 5개월 후 사망하였다.
암센터의 의사는 바로 AECL 엔지니어를 불러 원인 규명을 요구했으나 AECL 측은 방사선 과노출은 있을 수 없으며, 해당 환자는 누전으로 인한 전기 쇼크를 당한 것이라고 주장했다.
4월에도 같은 병원에서 거의 비슷한 사고가 발생했다. 위와 동일한 직원이 똑같이 방향키를 눌러 옵션을 수정하고 치료를 시작했더니, 기계에서 큰 소음이 들렸고 환자는 불타는 듯한 고통을 호소했다. 직원은 다시 의사를 불렀고, 의사는 다시 AECL 엔지니어를 불렀다. AECL 엔지니어는 터미널을 조작했던 암센터 직원의 도움을 받아 겨우 해당 오류를 재현할 수 있었고, 250그레이의 방사선 노출을 확인하였다. 해당 환자는 급성 방사선 피폭으로 3주 후 사망하였다.
AECL은 암센터 직원의 협조를 통해 버그를 재현하는 데에는 성공했으나 원인은 규명하지 못했고, 일선 병원들에 윗쪽 방향키를 뽑아버릴 것을 요청하였다.
2.1.1. 원인
직원의 입력을 받아 전사선을 휘게 만드는 휨자석(Bending Magnet)들의 위치를 설정하는 루틴을 극도로 간략화하여 대강 수도코드로 나타내면 대략 다음과 같았다. (아래에서 등장하는 모든 변수는 전역변수이다)터미널을 통해 모든 값이 설정되고 나면
Dataent
서브루틴이 Magnet
서브루틴을 부르고, Magnet
서브루틴은 자석을 이동시키는 루틴을 작동시킨 뒤 자석이 모두 제자리를 찾을 때까지 루프를 돌면서 Ptime
서브루틴을 여러 번 호출한다. 만약 자석 배열 실행 중에 터미널에서 키보드로 새로운 설정값을 입력하면, 키보드 핸들러가 전역변수인 editing_taking_place
, mode_or_energy_changed
등의 플래그를 설정하여 프로그램에 새로운 값이 입력되었다는 사실을 알리는 구조였다.하지만 이 코드에서는
Ptime
이 한 번 호출되고 나면 bending_magnet
은 0으로 재설정되고, 따라서 Magnet
루프에서의 다음 Ptime
호출 시, Ptime
함수 안의 루프의 if문은 아예 실행되지 않게 된다. 따라서 터미널에서 값을 수정하는 타이밍에 따라 낮은 확률로 수정된 값이 자석 배열에 반영되지 않을 수 있다.#!syntax cpp
void Dataent() { //데이터 입력 처리 서브루틴
if(mode_or_energy_specified) {
calculate_table_index();
do {
fetch_parameter();
output_parameter();
point_to_next_parameter();
} while(!all_parameters_set)
Magnet();
if(mode_or_energy_changed) return;
}
if(data_entry_complete)
Tphase=3;
if(!data_entry_complete) {
if(reset_command_entered) Tphase = 0;
}
return;
}
void Magnet() { //자석 정렬 처리
bending_magnet = 1;
do {
//다음 자석을 설정하는 부분의 코드 생략
Ptime();
if(mode_or_energy_changed)
break;
} while(!all_magnets_set)
return;
}
void Ptime() { //자석이 정렬되기까지 기다리기 위한 서브루틴
do {
if(bending_magnet) {
if(editing_taking_place) {
if(mode_or_energy_changed) {
break;
}
}
}
} while(hysteresis_delay != EXPIRED)
bending_magnet = 0;
return;
}
해당 암센터의 직원은 Therac-25를 사용한 치료를 이미 수백 차례 운용한 경험이 있었다. 직원이 터미널을 조작하는 속도는 숙련도가 쌓이면서 점점 빨라졌고, 그 결과 숨겨져 있던 버그가 발현되게 된 것이다.
한편, 이 버그는 Therac-25와 많은 코드를 공유하고 있던 Therac-20에도 존재했다. 하지만 Therac-20에는 하드웨어적인 인터락 장치가 있어, 이 버그가 발동할 시 시스템의 전원이 자동으로 내려갔기 때문에 Therac-25와는 달리 사고가 발생하지 않았다.
또한 이 문제와는 별개로, 상태 컨트롤 변수인
Tphase
에 대한 race condition 문제도 있었다.3. 참고자료
Levenson, Nancy G. (1995) "Medical Devices: The Therac-25."
[1]
월성 원자력 발전소의 원자로를 제작한 곳이다.