Latent Fragility · Chapter 2

The Dual Software Race

Concurrent State Bugs in the Therac-25 Radiation Overdoses

In early medical linear accelerators such as the Therac-6 and Therac-20, patient safety relied on physical hardware locks. Whenever a hospital technician selected high-energy X-ray mode, heavy electromechanical interlocks bolted a tungsten-tantalum target and a beam-flattening filter across the path of the 25-megaelectronvolt (MeV) electron beam. The magnetron physically could not fire unless those metal blocks were locked in place. When Atomic Energy of Canada Limited (AECL) designed the dual-mode Therac-25 in the early 1980s, engineers stripped out the mechanical interlocks to reduce hardware expenses. They delegated all safety verification to a 16-bit DEC PDP-11 minicomputer running 20,000 lines of real-time assembly code adapted from earlier machines by a single programmer.

Between June 1985 and January 1987, six cancer patients at clinics in Georgia, Ontario, Washington, and Texas suffered horrific radiation burns. Three of those patients died from radiation poisoning. For years, hospital physicists were baffled because the overdoses could not be reproduced during standard bench testing. In a landmark 1993 forensic investigation, computer scientists Nancy Leveson and Clark Turner analyzed the PDP-11 assembly source code. They proved that the disasters were caused not by a single defect, but by two completely distinct concurrent software bugs hiding inside the control architecture.

The first bug caused the two fatal overdoses at the East Texas Cancer Center in Tyler, Texas, killing Ray Cox in March 1986 and Verdon Kidd in April 1986. Inside the PDP-11 software, the keyboard entry routine (Datent) and the bending-magnet hardware task (Hand) ran asynchronously while sharing unprotected memory variables. When the Tyler operator sat at the VT100 terminal to administer a routine 180-rad low-energy electron treatment, she accidentally typed x for 25 MeV X-ray mode instead of e for electron mode. The instant x was entered, Datent wrote the mode to a shared variable and signaled the Hand subroutine to begin an eight-second mechanical sequence setting the bending magnets for a high-current 25 MeV X-ray beam.

Realizing her typing mistake immediately, the experienced technician pressed the up-arrow key, overwrote x with e on the terminal screen, and hit the return key. She completed the entire cursor edit in less than eight seconds. However, the magnet-setting subroutine had been coded to read the shared beam-energy variable only at the start of its eight-second cycle. Once the magnets were moving, Hand ignored any subsequent keyboard edits made before the eight-second timer expired. The turntable rotated into the low-energy electron position—retracting the tungsten X-ray target from the beam path—while the bending magnets remained locked at full 25 MeV X-ray current. When the technician triggered the beam, the machine fired a raw, unattenuated 25,000-rad electron beam into the patient while displaying a cryptic MALFUNCTION 54 alert on the console.

TYLER CURSOR-EDIT RACE (1986) 1. 'x' starts 8s magnet loop 2. Up-arrow edits 'x' to 'e' 3. Hand task ignores edit Raw 25 MeV Beam (No Target) YAKIMA COUNTER OVERFLOW (1987) 1. 8-bit Class3 increments 2. Tick 256 rolls 255 to 0 3. 'Set' pressed at Class3 = 0 Interlock Bypassed on Tick 256
FIG. 2.1 — TWO DISTINCT THERAC-25 CONCURRENT SOFTWARE BUGS (LEVESON & TURNER, 1993)

The second, completely separate bug caused the fatal January 1987 overdose at Yakima Valley Memorial Hospital in Washington. In the machine's housekeeping task (House), an 8-bit unsigned integer variable named Class3 served as a safety flag verifying whether the collimator turntable was properly aligned. Instead of assigning Class3 a fixed constant of 1 whenever an alignment check was required, the assembly routine incremented the 8-bit counter by 1 on every single pass through the real-time loop:

$$C_{n+1} = (C_n + 1) \bmod 256$$

Because an 8-bit unsigned integer holds a maximum value of $2^8 - 1 = 255$, on every 256th pass through the loop Class3 rolled over from 255 to 0. For several milliseconds every few seconds, Class3 equaled zero—the exact value signaling zero alignment errors. When the Yakima operator pressed the Set button during that brief rollover window, the software bypassed the turntable interlock and fired an unspread electron beam directly into the patient's chest.