10 апреля 1981 года, приблизительно за 20 минут до запланированного запуска первой американской космической транспортной системы, астронавты и технические сециалисты попробовали активизировать программную систему, выполненную с 4-хкратным резервированием и дополнительным запасным компьютером… но не смогли. Фактически, у них не было никакой возможности это сделать, так как компьютер просто не воспринимал команды… Это был баг, очень маленький, очень неуловимый, очень замысловатый и очень древний — это была ошибка в логике инициализации резервной системы. Это была ошибка, которая является кошмаром для программистов и руководителей, из разряда тех, которые «не могут произойти», даже если вы будете следовать всем правилам хорошего проектирования и разработки.
Самые глубокие секреты программной инженерии часто запрещаются к публикации и выявление причин проявления множества ошибок является чрезвычайно сложной задачей. Кроме того, корректную интерпретацию произошедшего может дать очень малое количество людей. Чаще всего получаются обрывочные факты, или множество несвязанных между собой или даже противоречащих друг другу фрагментов, а получить хорошо документированную историю крайне сложно. Peter G. Neumann говорит о том, что за годы его опыта самым эффективным способом является поиск ключевого инженера-разработчика всей системы или ответственного узла и попытка уговорить рассказать о том, что произошло.
Одним из самых известых багов в истории, которые при этом были документированы по описанному сценарию, является подготовка первого полета Шаттла 10 апреля 1981 года, когда контроллеры отменили запуск в космос шаттла Колумбия всего за 20 минут до намеченного времени. По этой причине на это обратила внимание мировая общественность и почувствовалось давление на то, чтобы подробности были раскрыты. Это был баг, который моментально стал извествен всему миру. Через некоторое время после проишествия Neumann нашел одного из разработчиков программного обеспечения, Jack'a Garman'a, и уговорил его раскрыть детали прозошедшего — именно так появилась эта публикация.
Для повышения надежности шаттл использует 4 идентичных компьютера, работающих по одной и той же программе, включенных в мажорирующую схему 2+2 с сильными связями, функционируя в горячем резерве. Это означает, что 4 компьютера разбиты на 2 пары, каждая из которых синхронизируется со своим «напарником». Если состояния пары одинаковы, то это считается корректной работой системы. Если одна из пар расходится во мнениях, то вся пара считается сломанной и управление переходит на вторую пару компьютеров. Синхронизация между парами происходит на каждом цикле внутренних часов. Данная организация позволяет защищаться от аппаратных проблем — например в случае сбоев из-за электромагнитного излучения.
Кроме описанных 4-х компьютеров в системе присутствует также 5-й компьютер, являющийся резервным. Его ПО обладает такой же функциональностью, но написано другими программистами и работающее под другой операционной системой — для обеспечения более высокого диверситета на программном уровне (в данном шаттловом случае компилятор и железо были одинаковыми с основной системой). Данный компьютер включается только в случае, когда обе пары из основной системы 2+2 выходят из строя, при этом резервная система управление и всю ответственность берет полностью на себя. Резервный компьютер программно призван отличаться от основного и в этом плане его назначение — избежать программных ошибок.
В описанной истории инженеры обнаружили баг временной синхронизации между описываемыми компьютерами. С вероятностью 1/67, которая срабатывает на каждом запуске системы, могло получится так, что часы синхронизации пары 2+2 и резерва окажутся на 1 такт впереди общих тактов синхронизации. Именно это произошло на реальной системе при пробной попытке запуска челнока за 30 часов до предполагаемого старта. В случае рассинхронизации получалось так, что старта основной системы не было, а резервный компьютер никогда бы не получил сигнала о том, что 2+2 вышли из строя. Следует заметить, что тысячи часов тестирования не смогли выявить описанную проблему.
В комплексе имелось три ленты времени — данные телеметрии, время для основной системы и время резервной. При подготовке к запуску заранее стартовала телеметрия (как обратный отсчет) и относительно её синхронизировались обе системы (основная и резервная), но время самого запуска в них системе вычислялось математически и по общим тактам. Далее, если происходила рассинхронизация, то получалось так, что время, которое оставалось до старта по расчету во время ожидания сигнала, становилось отрицательным (пришло время старта, ожидаем sync, а его нет). Число беззнаковое, поэтому оно становилось равным FF, т.е. конечному числу, а по меркам реальной системы — равному бесконечности.
Баг был внесен за 2 года до старта, когда в процедуры добавили действия, которые немного отсрочили время, когда начнется синхронизация. Впоследствии через год это нашли и добавили константу ожидания, чтобы все стало в порядке. Но описанных действий как раз таки оказалось достаточно для того, чтобы получить фальшстарт каждые 1 из 67 случаев, а также для того, чтобы тестирование не смогло обнаружить проблему.
В описанном я бы назвал несколько причин:
- Синхронизация между двумя разными системами (основная работала в виде асинхронной с приоритетами (fixed priority executives), а резервная периодическая синхронная (cyclic executive)).
- Разница сред тестирования и эксплуатации (таймеров).
- Отстутствие аналитической проверки при внесении изменений, влияющих на время синхронизации.
И да, доказательство корректности имело все шансы поразить проблему точно в цель (;