2005 г.
Изучение знаменитых (и не очень знаменитых) ошибок
Глава из книги "Наука отладки"
Мэтт Тэллес, Юань Хсих
Пер. с англ. С. Лунин, науч.ред. С. Брудков
Издательство: КУДИЦ-ОБРАЗ
Полное содержание книги
Глава 2
Yuan Hsieh
- Сценарий
- Распределенные компьютерные системы из «реальной жизни»
- Therac-25
- ИСТОРИЯ
- Kennestone Regional Oncologyl Center, г Кенстоун, округ Мариетта, штат Джорджия (Kennestone, Marietta, Georgia). Июнь 1985
- Ontario Cancer Foundation, Хемилтон, провинция Онтарио, Канада (Hamilton, Ontario, Canada)
- Yakima Valley Memorial Hospital, Якима, штат Вашингтон (Yakima, Washington), декабрь 1985
- East Texas Cancer Center (Восточно-Техасский онкологический центр), г Тайлер, штат Техас (Tyler, Texas). Март 1986
- East Texas Cancer Center (Восточно-Техасский онкологический центр), г Тайлер, штат Техас (Tyler, Texas). Апрель 1986
- Yakima Valley Memorial Hospital, г Якима, штат Вашингтон (Yakima, Washington), Январь 1987
- Выводы
- Зарисовка #1
- Зарисовка #2
- Зарисовка #3
- Зарисовка #4
- Заключение
Представьте, что сейчас 1986 год. И представьте, что вы - ныне покойный Ричард Фейнман (Richard Feynman). Звонит телефон, вы поднимаете трубку. Звонит Уильям Грэхэм (William Graham), исполняющий обязанности администратора в NASA1. Вы знаете, что это не может быть просто дружеский звонок, поскольку космический челнок Челленджер потерпел катастрофу несколько дней назад, 28 января 1986 года. Вы правы. Он звонит для того, чтобы просить вас принять участие в работе комиссии по исследованию причин катастрофы Челленджера. В сущности, NASA просит вас, свежеиспеченного Нобелевского лауреата, выступить в роли главного исследователя неисправностей для одного из самых печальных инцидентов в NASA в современной истории2. Вы принимаете предложение с трепетом. В конце концов, вы можете быть Нобелевским лауреатом, но вы не специалист по ракетам. Вы не участвовали в разработке космического челнока. Вы не знакомы с компонентами и системами космического корабля. Вы не имеете представления, какого рода информация записывалась в ходе рокового полета Челленджера. Вы не представляете себе состояние или полезность документации. Единственный ключ, который у вас есть, - это ваши визуальные наблюдения, что пламя, как кажется, выходило из правого топливного бака. Как вы будете решать задачу по поиску причины катастрофы?
В действительности, доктор Фейнман обнаружил причину инцидента, перемещаясь по всей стране и беседуя с инженерами и персоналом и не поддаваясь политическому давлению. В конечном итоге он провел свой знаменитый настольный эксперимент с образцом от одного из уплотнительных колец с челнока и стаканом ледяной воды для доказательства своей теории3 в ходе пресс-конференции в прямом эфире4.
К счастью, наша работа по устранению ошибок в программных системах намного легче той задачи, с которой столкнулся доктор Фейнман. В большинстве случаев мы можем попытаться воспроизвести ошибку, чтобы удостовериться, что мы полностью понимаем ее причины и следствия. Ученые NASA не имели такого преимущества. Они не могли воспроизвести ошибку, взорвав другой космический корабль. Более того, доктор Фейнман должен был представить формальный отчет о своих открытиях для общественной поверки и, что еще хуже, должен был бороться с политическим давлением со стороны официальных лиц NASA. Когда в последний раз мы должны были делать нечто подобное при устранении ошибок? В индустрии программного обеспечения мы очень мало делаем того, что можно назвать посмертным анализом ошибки. Мы не задаем таких вопросов, как, каким образом ошибка была обнаружена, как она возникла и что мы можем сделать, чтобы предотвратить ее. Если мы все же проводим посмертный анализ, мы так редко документируем наши открытия, что наше знание недоступно другим людям.
Во многих смыслах наша книга - это кульминация многих лет посмертного анализа ошибок, о которых мы узнали и которые провели сами. В главе 12 мы покажем вам, как анализировать ошибки, как увеличить ваше коллективное знание о процессах формирования ошибок и как это знание поможет нам избежать одних и тех же ошибок и создавать лучшее программное обеспечение в будущем. Действительно ли посмертный анализ ошибки поможет нам создать лучший продукт? Мы считаем, что ответ на этот вопрос - да! В 1981 году фирма NEC осуществила план, призванный помочь разработчикам программ и менеджерам проектов учиться на ошибках. Был создан каталог ошибок, наблюдавшихся во многих корпоративных проектах. Это поддержало разработчиков в поисках причин отказов программ и в предотвращении их повторного появления. За 10 лет, прошедших с запуска проекта, разработчики извлекли много уроков и стали способны применять этот опыт для повышения своей производительности и понижения числа ошибок5.
Инициатива NEC началась с каталога проблем и решений. После, обучившись находить причинно-следственные связи между проблемами, разработчики стали способны формулировать контрмеры для решения этих проблем. Мы начнем с того же.
Сценарий
В этой главе мы собрали для изучения набор программных ошибок. Большинство этих ошибок довольно хорошо известны и, как правило, хорошо описаны, поскольку их появление было общественно-значимым событием и многие имели серьезные последствия. Хотя достаточно банальные и избитые, эти ошибки все же способствуют наилучшему изучению конкретных случаев. Проблема состоит в том, что далеко не каждый связан с проектами такого масштаба и столь же критичными для безопасности, так что трудности, которые испытали эти программисты, могут показаться чем-то не связанным с вашими повседневными задачами, поэтому мы также включили несколько примеров ошибок из нашей собственной практики разработки программного обеспечения. Наши персональные ошибки являются более приземленными и не связаны с взрывами и потерями многих миллиардов долларов. Тем не менее в этой книге мы показываем ошибки, созданные другими, для иллюстрации различных положений. Мы показываем эти ошибки не для забавы. Наша цель - изучить события, окружающие эти знаменитые и не очень знаменитые ошибки, для того чтобы мы могли на них учиться.
Распределенные компьютерные системы из «реальной жизни»
Парад ошибок начинается примерами с двух моих предыдущих мест работы. Первая ошибка просуществовала несколько часов и была быстро устранена. Это был урок для второй ошибки. Девять месяцев спустя, вскоре после того как я начал работать в другом месте, я столкнулся с похожей ошибкой. Однако в новой компании проблема проявила себя за два месяца до моего прихода. Серьезность первой ошибки была такова, что она нарушала работу всей системы. Вторая ошибка, хотя и устойчивая, не была фатальной, она просто вызывала легкое раздражение, а не общую катастрофу.
История: компания Y
Был конец 1999 года, и я работал в одной Интернет-компании, которая обслуживала финансовые отчеты в Интернете. Назовем ее компания Y. Архитектура систем, обслуживающих финансовые отчеты, очень проста: Web-запрос приходит через один из многочисленных Web-серверов. Web-сервер передает запрос менеджеру нагрузки, и менеджер нагрузки доставляет запрос к одному из четырех серверов отчетов циклическим образом. Сервер отчета выбирает информацию из базы данных для генерации отчета.
Однажды сигналы тревоги начали сыпаться со всех сторон. Сервера отчетов выходили из строя один за другим. Сервер останавливался, перезапускался несколькими минутами позже и снова останавливался через несколько минут. Это случалось со всеми серверами. Частота поступления запросов не была очень высокой, не была она и какой-то необычной. Перезагрузка машины, кажется, не помогала. Сетевые операторы не имели представления, что вызвало этот хаос, и была вызвана команда разработчиков. Поскольку я разрабатывал и реализовывал коммуникационную инфраструктуру, используемую внутренними серверами, меня попросили помочь. Команда разработчиков подозревала, что модуль распределения нагрузки в коммуникационной инфраструктуре был неисправен и сервера останавливались из-за перегрузки.
Коммуникационная инфраструктура имела встроенные возможности протоколирования и трассировки, которые были деактивированы в конечном продукте. Первое, что я сделал, это включил эти возможности, чтобы определить, что же система делает. Протокол, сформированный сервером отчетов, мало что прояснил для меня. В протоколе была масса сообщений об ошибках, но ни одно из этих сообщений ничего не описывало. Однако сразу перед тем, как сервер отчетов претерпевал сбой и перезапускался, в протокол попадало сообщение, что сервер отчета остановлен. Я спросил у инженера, что означает это сообщение, но его ответ был несколько загадочным. Это было что-то о том, что программа перезапускает себя, когда считает, что есть какая-то проблема. В это время я рассматривал файлы протоколов, полученных от менеджера нагрузки, и увидел, что он все время обращается к одному и тому же серверу. Я немедленно предположил, что механизм распределения нагрузки работает неправильно и что менеджер загрузки перегружает сервер отчета, заставляя его перезапускать себя.
Я проинформировал группу о моих предположениях и вернулся к своему столу, проверять код, выискивая потенциальные ошибки. Ничего не бросалось в глаза, и я подумал о замечании инженера, что сервер отчета разработан так, чтобы перезапускаться, когда он считает, что существует проблема. Я решил выяснить, какого рода проблемы могли бы вынудить сервер перезапуститься. По существу сервер отчетов поддерживал 20 потоков одновременно и каждый запрос к серверу занимал 1 процесс. Ожидалось, что в нормальных условиях каждый процесс заканчивает обработку в течение 1-2 секунд. Данный сервер отчета не считался очень стабильным и имел факты подверженности ошибкам. Поэтому в сервер была встроена логика «безопасности», так, что, если в момент, когда все 20 процессов были заняты, поступал 21-й запрос, этот последний должен был ожидать освобождения потока в течение 5 секунд. Если срок ожидания у этого запроса истекал, сервер отчета считал, что что-то не так и что все 20 потоков зависли. Единственным действием дальше становилась остановка и перезагрузка сервера.
Услышав это, я вернулся к своему столу, чтобы все обдумать. Через несколько минут все встало на свои места. Я проверил свои размышления файлами протоколов и трассировок и сообщил мою гипотезу группе, обслуживающей сервер отчетов.
- Причина того, что менеджер нагрузки работал только с одним сервером отчетов, была не в том, что алгоритм распределения нагрузки был ошибочным. Он работал только с одним, поскольку остальные были недоступны. Они все перезапускались. Это было легко проверить, взглянув на временные отметки файлов протокола с серверов отчета и менеджера нагрузки.
- Протоколы серверов отчета показывали, что запускалась логика «безопасности». Это подразумевало, что у каждого сервера было по 21 конкурирующему процессу. Это означало, что в некоторый момент времени к системе поступило 84 запроса, поскольку к моменту инцидента работали 4 сервера отчетов.
- В конечном итоге один из серверов перезапускался из-за запуска логики «безопасности». Это в результате снижало пропускную способность системы до 63 запросов. Однако скорость поступления запросов оставалась постоянной. Это еще увеличивало нагрузку на оставшиеся 3 сервера, и в конечном итоге другой сервер перезапускался из-за логики «безопасности». Пропускная способность падала до 42 запросов. К тому времени, когда первый сервер заканчивал перезагрузку, останавливался и начинал перезапуск третий. Этот эффект мог сохраняться сколь угодно долго, если ничего не менялось, и скорость поступления запросов и скорость ответа оставались постоянными.
Эта гипотеза соответствовала наблюдениям, что сервера перезапускались последовательно, и тому, что они просто не были способны оставаться функциональными. Однако нам еще следовало определить причину перегрузки 84 запросами. Мы уже проверяли, что нагрузка не была чрезвычайно высокой. Оставалась только одна возможность. Проблема должна была быть связана с допущением того, что каждый запрос обрабатывается за 1-2 секунды и что 21-й запрос должен подождать только 5 секунд. Это выбранное значение, по-видимому, было ошибочно. Это допущение является верным в нормальных условиях работы. В начале этого описания я сказал, что сервер при генерации отчета зависит от базы данных. В данном случае база данных претерпевала что-то вроде деградации (meltdown). Мы так и не смогли определить причины такой деградации. Простая команда SELECT вместо десятых долей секунды занимала до 10 секунд. Сильное падение производительности базы данных проявило ошибку проектирования системы, и ситуация быстро ухудшалась. Поскольку каждый сервер отчета, который останавливался, снижал общую пропускную способность, большая нагрузка оказывалась на оставшиеся рабочие сервера. Если не вмешиваться, то проблема просто бы исчезла, когда нагрузка упала бы до величин, меньших 20 одновременно обрабатываемых запросов ко всей системе.
Как только было определено, что сервер базы данных работает неверно, перезагрузка SQL сервера решила проблему.
История: компания Х
В середине 2000 года я начал работать в другой Интернет-компании, которая занималась картографической и деловой информацией, а также электронной коммерцией. Назовем ее Компанией Х. Системная инфраструктура в Компании Х была сходна с инфраструктурой в Компании Y: Web-сервера обращались к менеджерам нагрузки, которые обращались к агентам баз данных, которые и связывались с базами. Однако существовало небольшое усложнение в том, что Компания Х предоставляла интерфейс интерактивного голосового ответа (Interactive Voice Response, IVR), который позволял клиентам осуществлять доступ к системе через обычный телефон. Инфраструктура, которая реализовывала IVR, просто замещала Web-сервер IVR-сервером, который связывался с менеджером нагрузки таким же образом, как и Web-сервера. Другое отличие состояло в том, что Компания Х задействовала два отдельных центра данных. Обычно IVR-сервер использовал один центр в качестве первичной и активной системы. Когда возникали сложности с активным центром, IVR-сервер переключался на другой, запасной центр.
Однажды руководитель технического отдела Компании Х пришел, чтобы попросить меня поговорить с группой, обслуживающей IVR-сервер, и помочь им исследовать недавний выход службы из строя. Несколькими днями ранее работа IVR-системы была нарушена более чем на восемь часов. Когда я пришел поговорить с группой, выяснилось, что для продолжения работы информации очень мало. Были файлы протокола с IVR-сервера на момент выхода его из строя, но они были удалены из-за проблем коммуникации с Центром сетевых операций. Люди, занимавшиеся обслуживанием системы, подозревали, что ошибка была связана с какой-то малопонятной сетевой проблемой центра данных. Однако это не объясняло, почему не использовался запасной центр. После дальнейших исследований оказалось, что проблема в IVR-сервере возникала при соединении с одним из центров данных. Эта проблема сохранялась в течение двух месяцев.
Поскольку я был не знаком с системой, я попросил показать мне сгенерированные файлы протоколов. Я хотел быть способным зафиксировать поведение системы, когда (и если) сбой возникнет опять. Файл протокола, хотя и полезный для просмотра ошибок, не очень помогает в описании действий системы. Он только отображает статус каждого запроса, после того как он был обработан. Если бы какой-нибудь запрос вызвал ошибку, файл протокола никогда не показал бы состояние IVR-сервера в момент сбоя. Более того, IVR-сервер не выходил из строя при сбое системы. Не было ни одного базового файла, который можно было бы использовать. Однако файл протокола показывал относительно большое число событий при переключении IVR-сервера от одного центра данных к другому. Каждое событие переключения центров данных имело корреляцию с серией из трех запросов со статусом кода ошибки -3, кода, который означал, что время ожидания у запроса истекло, когда IVR-сервер пытался соединиться с менеджером нагрузки. В Компании Х каждый запрос имел уникальный идентификатор (ID), который заносился в файл протокола на любом сервере, к которому этот запрос обращался, так что была возможность проследить путь запроса в системе. Однако IVR-сервер не записывал ID запроса в свой протокол, так что мы не могли выяснить, почему истекло время ожидания.
Наученный опытом, полученным в Компании Y, я спросил о стратегии преодоления ошибок на IVR-сервере. Оказалось, что IVR имеет требования к быстродействию - в отличие от использования Web-сайтов, при использовании телефона люди не любят ждать. Поэтому IVR должен ответить в течение определенного времени. IVR-сервер должен был получить данные от менеджера нагрузки в течение трех секунд. После трех последовательных, закончившихся неудачей попыток сервер автоматически переключался на запасной центр данных. Оставаясь соединенным с запасным центром данных, IVR-сервер через 2 часа пытался восстановить соединение с первичным центром данных. Если попытка оказывалась успешной, сервер вновь переключался на первичный центр данных.
К этому времени у меня были две довольно разумные гипотезы, которые объясняли все наблюдения. (1) Поскольку проблемы на IVR-сервере были при соединении с одним из центров данных, по всей видимости, IVR-сервер работал без резерва. Если бы на центре данных, к которому он был подключен, возникли проблемы, служба IVR оказалась бы недоступной. И (2) причина того, что IVR-сервер не мог поддерживать соединение с центром данных, была точно записана в существующем файле протокола. Это было истечение времени ожидания его запросов. Чтобы определить, почему время ожидания заканчивается так часто, нам следовало проследить путь запросов, которые вызывали его.
Не было, однако, протоколов или другой информации, чтобы проверить мои гипотезы, поэтому я совместно с командой обслуживания IVR-сервера начал работу по генерации более полезного протокола. Как только мы оказались способны проследить поток запросов через систему, сразу стало видно, где здесь узкое место. Один из серверов базы данных тратил необычно долгое время на обработку запросов. Этот сервер делал несколько SQL запросов к базе данных, которая располагалась на той же машине, что и сам сервер. Продолжительность вызовов была существенно выше, чем ожидалось. Поскольку на сервере базы данных была видна необычно высокая загрузка процессора, было сделано предположение, что машина слишком перегружена и, возможно, необходимо дополнительное оборудование. Другая гипотеза состояла в том, что SQL-операторы, как они были реализованы, являлись неэффективными. Это необходимо было проверить. Еще несколько дней исследований выявили, что база данных обновлялась за 2 месяца до инцидента. Однако администратор базы данных забыл переиндексировать базу, и каждый запрос занимал в 10 раз больше времени на обработку, чем обычно. Сильная нагрузка на процессор просто отражала выполнение SQL-операторов на неиндексированной базе. База на «рабочем» центре данных была обновлена и переиндексирована, поэтому никаких неприятностей не возникло. Мы никогда бы не смогли правильно объяснить причины сбоя, поскольку не было информации для исследований. Однако главной причиной сбоя было отсутствие запасного сервера данных.
Выводы
Сходство между предыдущими двумя случаями поразительно. В обоих случаях участвовали системы, которые были работоспособными долгое время. Обе системы работали как положено и не содержали ошибок реализации. Устранение проблемы в обоих случаях (по крайней мере, быстрое) состояло просто в перезапуске или переиндексировании базы данных. Обе проблемы проявились, поскольку ответы баз данных занимали больше времени, чем ожидалось. Причины проблем были хорошо видны в файлах протокола. Однако было трудно отследить в файлах и понять эти причины. Почему?
- Компонентное мышление. - В обоих случаях инженеры концентрировались главным образом на компонентах. Они никогда не рассматривали наблюдения в контексте целой системы. Наладка в распределенной компьютерной среде требует целостного подхода. Каждый компонент системы может влиять на поведение другого компонента и системы в целом. Взаимосвязи между компонентами могут быть очень сложными и часто непонятными интуитивно. Когда вы интересуетесь, почему компонент вышел из строя, может быть, полезно задать вопрос: «Какой другой компонент системы мог вызвать сбой этого компонента?»
- Ориентировка по вторичным признакам. - Несколько лет назад я участвовал в занятиях по лавинной безопасности. Одной темой этих занятий было проведение поиска с помощью радиомаяков. Идея такого поиска заключалась в том, что, если жертву засыпало снегом, ее радиомаяк посылал радиосигнал, который приемник на другом радиомаяке мог принять. В приемнике радиосигнал преобразовывался в звуковой сигнал. Чем громче был звук, тем ближе была жертва. Первоначально у меня были проблемы с поиском. Вместо того чтобы слушать звуковой сигнал и следовать по нему, я искал видимые признаки. Как только мне удалось отбросить все мои прочие чувства и сфокусироваться на звуке, спасение жертвы у меня существенно ускорилось. Тот же самый совет будет полезен при наладке приложений. В обоих наших примерах инженеры игнорировали подсказки, которые давали им их системы. Вместо того чтобы следовать этим подсказкам, они выбрали ориентировку по вторичным признакам и создавали некорректные гипотезы, не объяснявшие всех наблюдений. Все подсказки и все наблюдения нужно принять во внимание при создании гипотезы.
- Игнорирование подсказок. - Важные подсказки игнорировались. В компании Х повышенную нагрузку на процессор у сервера базы данных нужно было исследовать, поскольку скорость поступления запросов не превышала нормальной. Неприемлемое время вызова и обработки запроса у сервера базы записывались в файлы протокола в течение двух месяцев, пока проблема сохранялась. Было множество предупреждений, что нечто плохое потенциально может случиться. Однако из-за недостатка времени, ресурсов и инфраструктуры на эти предупреждения не обращали внимания, пока не случилась катастрофа.
В этих двух инцидентах не были задействованы программные ошибки в традиционном смысле этого слова, хотя даже разработчики были поставлены в тупик. Проблемы, с которыми столкнулись Компании Х и Y, можно было предотвратить, если бы обслуживающий персонал был лучше информирован. Каким образом?
- Лучшие инструменты мониторинга. - В обоих случаях, если бы обслуживающий персонал смог обнаружить и зафиксировать необычное поведение сервера базы данных, возможно, они смогли бы предпринять действия для корректировки сервера базы данных. Проблемы, с которыми мы столкнулись, могли бы никогда не проявиться.
- Следование правилам. - В Компании Х существовало стандартное правило переиндексировать базу данных после ее обновления. В данном случае переиндексирование не было произведено и не было никаких записей об обновлении.
Недостаток записей сделал трудным определение изменений в окружении и в системе для нужд наладки.
Therac-25
Ошибка аппарата Therac-25 была, пожалуй, самой дорогой ошибкой в современной истории. Как известно, с июня 1985 по январь 1987 года шесть пациентов получили передозировку радиации, что привело к гибели троих из них. Здесь мы полностью полагаемся на отчет, опубликованный Leveson и Turner в 1993 году после нескольких лет расследования. Этот отчет содержит наиболее тщательную и детальную оценку причин и следствий инцидента с Therac-25 и охватывает все аспекты безопасности системы. Однако, поскольку наша книга посвящена программным ошибкам, мы ограничим наше изложение и анализ результатами, связанными с программным обеспечением 6.
История
Therac-25 представлял собой компьютеризированную машину для радиационной терапии, построенную компанией Atomic Energy of Canada Limited (AECL). Предшественниками Therac-25 были Therac-6, который представлял собой ускоритель на 6 миллионов электрон-вольт (МэВ), способный испускать только рентгеновские лучи, и Therac-20, рентгеновский излучатель и ускоритель электронов на 20 МэВ. Все три машины использовали мини-компьютер DEC PDP-11. И в Therac-6, и в Therac-20 использовались некоторые аппаратные возможности для предотвращения небезопасных операций. Некоторые программные модули из Therac-6 были использованы вновь в Therac-20 и Therac-25. Кроме того, в Therac-25 были использованы программные модули Therac-20 для электронного режима.
Therac-25 был усовершенствованием Therac-20. Он был способен испускать фотоны или электроны с энергией 25 МэВ с возможностью переключения уровней. Он был меньше, имел больше возможностей и был легче в использовании. Также он был сконструирован так, чтобы компьютерное управление было более полным, чем в его предшественниках. Программное обеспечение, разработанное для Therac-25, было способно осуществлять контроль состояния и управление оборудованием. Поэтому решено было удалить аппаратные средства безопасности и полагаться в этом вопросе на программное обеспечение.
Therac-25 поступил в продажу в конце 1982 года, и 11 таких машин были установлены в Северной Америке, 5 - в США и 6 - в Канаде. Шесть несчастных случаев с большими передозировками произошли между 1985 и 1987 годами7.
Kennestone Regional Oncologyl Center, г. Кенстоун, округ Мариетта, штат Джорджия (Kennestone, Marietta, Georgia). Июнь 1985
Женщина в возрасте 61 года была направлена в онкологический центр для дополнительного лечения аппаратом Therac-25 после хирургического удаления молочной железы. Как считается, пациентка получила одну или две дозы радиации от 15 000 до 20 000 рад (поглощенная доза радиации). Для сравнения, типичная разовая терапевтическая доза радиации составляет до 200 рад. Кенстоунская клиника использовала Therac-25 с 1983 года без происшествий. Техники и фирма AECL не поверили, что эта проблема может быть вызвана Therac-25. В конечном итоге пациентка потеряла грудь, а также возможность пользоваться руками и плечами из-за радиационного поражения.
Ontario Cancer Foundation, Хемилтон, провинция Онтарио, Канада (Hamilton, Ontario, Canada)
Клиника в Хэмилтоне использовала Therac-25 в течение шести месяцев до инцидента с передозировкой. Сорокалетняя женщина поступила в клинику на 24-й сеанс лечения аппаратом Therac-25. Аппарат отключился через пять секунд после того, как оператор запустил его. Операторы были знакомы с частыми неполадками машины. Эти неполадки, вероятно, не имели серьезных последствий для пациента. Поскольку машина показывала, что облучение не было произведено, оператор попытался повторить его. Были произведены пять попыток. После пятой попытки был вызван техник, не обнаруживший проблем в аппарате.
Об инциденте сообщили в AECL, но воспроизвести неполадку и сделать заключение о причинах такого поведения Therac-25 не удалось. Однако благодаря этому сообщению фирма AECL обнаружила некоторые слабости конструкции и потенциальные механические проблемы в позиционировании поворотной платформы Therac-25 и были сделаны исправления. Пациентка умерла через пять месяцев. Результаты вскрытия показали, что смерть наступила от рака, а не от передозировки радиации. Однако вскрытие также выявило серьезные поражения бедра, вызванные радиационным воздействием. Как было позже определено, пациентка получила дозу порядка 13 000 - 17 000 рад8.
Yakima Valley Memorial Hospital, Якима, штат Вашингтон (Yakima, Washington). Декабрь 1985
После лечения аппаратом Therac-25 у женщины развилось сильное покраснение кожи в форме параллельных полос. Персонал клиники считал, что это явление мог вызвать Therac-25. Однако они не смогли воспроизвести конфигурацию оборудования, которая была использована при лечении пациентки. Персонал проинформировал AECL о потенциальной передозировке. В AECL снова посчитали, что передозировка при использовании аппарата Therac-25 невозможна. Персонал клиники не был осведомлен о двух предыдущих случаях и не имел возможности расследовать инцидент, поэтому они не стали продолжать дело после ответа из AECL. Пациентка, вероятно, получила более низкую дозу радиации, чем два предыдущих пострадавших, и она не испытала серьезных последствий в результате передозировки9 .
East Texas Cancer Center (Восточно-техасский онкологический центр), г. Тайлер, штат Техас (Tyler, Texas). Март 1986
Восточно-техасский онкологический центр использовал аппарат Therac-25 с 1984 года и применял его при лечении более 500 пациентов. 21 марта 1986 года пациент (мужчина) был направлен на дополнительное лечение. Оператор, которая осуществляла лечение, была знакома с Therac-25 и хорошо разбиралась в его свойствах и в процессе использования. Когда она вводила данные пациента и врачебные предписания, она допустила ошибку, которую быстро исправила и начала лечение. Спустя мгновение машина отключилась, и дисплей отобразил сообщения об ошибках. Как обычно происходит в программных системах, сообщение об ошибке представляло собой код, который никто не смог бы расшифровать. «Ошибка 54» расшифровывалась в печатном перечне ошибок как «ввод дозы 2». Восточно-техасский онкологический центр не располагал другой документацией, которая объясняла бы смысл выражения «ввод дозы 2». Дисплей показал также очень малую дозу облучения и, поскольку оператор была знакома с капризами Therac-25, она немедленно запустила повторное лечение. Машина снова отключилась с теми же сообщениями об ошибках.
Клиника связалась с AECL по поводу этой проблемы. Техники фирмы, направленные в Восточно-техасский онкологический центр, не смогли воспроизвести неисправность, и AECL все так же считала, что передозировка на Therac 25 невозможна. Пациент умер от осложнений передозировки через пять месяцев после этих событий10 .
East Texas Cancer Center (Восточно-техасский онкологический центр), г. Тайлер, штат Техас (Tyler, Texas). Апрель 1986
Три недели спустя другой мужчина поступил для лечения с помощью Therac 25. Та же женщина-оператор, которая участвовала в прошлом инциденте, была ответственной за лечение. Как и в предыдущем случае, она сделала ошибку при вводе данных, и, почти столь же быстро исправив ошибку, она запустила лечение. Машина ответила сообщением «ошибка 54» и отключилась. Однако пациент уже получил передозировку, и оператор побежала за помощью. Therac-25 был отключен, и клиника проинформировала AECL о втором случае передозировки.
Физик клиники, Фриц Хэгер (Fritz Hager), совместно с оператором научились воспроизводить сообщение «ошибка 54» произвольно. Оказалось, что передозировка возникала, если данные врачебного предписания редактировались в быстром темпе. Люди из AECL наконец смогли воспроизвести ошибку и признали, что передозировка была возможна11.
Как же было возможно, что быстрое редактирование вызывало передозировку? Аппарат Therac-25 мог работать в одном из двух режимов - фотонном или электронном. В электронном режиме оператору было необходимо ввести уровень энергии. Если же был выбран фотонный режим, по умолчанию энергия фотонов принималась за 25 МеV. Ошибки, совершенные оператором в обоих случаях, были одни и те же. Для двоих пациентов, которые получили передозировку, требовался электронный режим. Однако, поскольку большинству пациентов нужен фотонный режим, оператор привыкла выбирать последний. В этих двух случаях оператор первоначально выбирала фотонный режим, а затем исправляла свою ошибку. Физическая калибровка и настройка магнитов занимает около 6 секунд. Программный модуль, который проверяет завершение ввода данных, должен произвести калибровку магнитов, как только были установлены параметры режима и уровня энергии. Другой программный модуль был ответственен за проверку изменения входных данных. Если изменения были произведены, настройка магнита сбрасывалась и калибровка начиналась заново с новыми параметрами. Однако логическая ошибка в этом модуле вызывала то, что он был не способен распознать изменения данных, если они производились в течение восьми секунд после того, как были введены первоначально. (Это чрезвычайно упрощенное описание программного алгоритма. Советую читателю обратиться к отчету Levenson and Clark за дополнительными подробностями.) В этих двух случаях, когда оператор выбирала фотонный режим в первый раз, машина подготавливала к использованию уровень энергии в 25 MeV в фотонном режиме. А когда оператор изменила режим на электронный в течение восьми секунд, параметры магнитов не были сброшены и давалась неверная доза.
Когда в фирме AECL поняли ошибку, фирма немедленно разослала письмо к пользователям, в котором рекомендовала временные меры, заключавшиеся в том, чтобы не редактировать данные в ходе процесса их ввода.
Пациент скончался через три недели после инцидента от передозировки радиации12.
Yakima Valley Memorial Hospital, г. Якима, штат Вашингтон (Yakima, Washington). Январь 1987
К этому времени проблемы с машиной Therac-25 были преданы широкой гласности, по крайней мере в сообществе пользователей. Операторы знали о запрете редактировать данные. В то время когда AECL совместно с Управлением по контролю за продуктами и лекарствами (FDA) активно занималась планом работ по устранению неполадок, случился шестой инцидент. В данном случае пациент должен был получить три дозы облучения. Первые две дозы составляли 4 и 3 рад. Следующая доза составляла 79 рад в фотонном режиме. Первые два сеанса прошли без осложнений. После второй дозы оператор вошел в комнату облучения, чтобы повернуть поворотную платформу, чтобы проверить положение лучевого пучка относительно тела пациента. Оператор нажал кнопку возле поворотной платформы для указания того, что контроль произведен. Установив платформу, оператор запустил процесс лечения. Машина остановилась через 5-6 секунд, и поэтому оператор запустил лечение снова. И снова машина отключилась и отобразила «маловразумительную» причину остановки. Возникло предположение о передозировке, однако дисплей показал только облучение в 7 рад от двух первых сеансов.
Через неделю AECL обнаружила недостаток в программном обеспечении, который смог объяснить это поведение. Этот программный дефект отличался от того, который был обнаружен в Восточно-техасском онкологическом центре в деталях. Однако оба дефекта были вызваны неожиданными зависимостями от скорости в программных модулях. В данном случае существовала разделяемая переменная, названная Class3, содержащая однобайтовое значение. Это значение указывало, соответствуют ли параметры машины параметрам лечения. Если значение Class3 было ненулевым, параметры считались несоответствующими, и пучок лучей подавлялся. Эта переменная инициализировалась в модуле, который готовил машину к лечению. Однако инициализация заканчивалась инкрементированием данной переменной. Поскольку переменная была однобайтовой, через каждые 256 итераций значение ее доходило до нуля, программный модуль, проводивший инициализацию, запускался все время в зависимости от других событий в системе. Если кнопка на поворотной платформе была нажата именно в тот момент, когда переменная Class3 была равна нулю, проверка соответствия не производилась, и машина могла облучить пациента электронным пучком дозой до 25МеV.
Пациент умер в апреле 1987 году от осложнений, вызванных передозировкой. Машина была отозвана вскоре после этого13.
Выводы
За короткую жизнь Therac-25 было обнаружено два программных дефекта:
- Логическая ошибка в обновлении параметров, когда оператор менял состояние машины.
- Проверка безопасности не срабатывала, когда 8-битный счетчик переполнялся и достигал нуля каждые 256 итераций.
Однако с точки зрения безопасности систем самое уязвимое место - это доверие к программному обеспечению. Очевидно, что тот же дефект, который вызвал передозировки в Therac-25, присутствовал и в Therac-20. Та же ошибка в Therac-20 приводила к отключению машины без передозировки, поскольку в Therac-20 применялись независимые аппаратные устройства безопасности, которые предотвращали ее.
Ошибки, связанные с зависимостью от скорости, очень трудно обнаружить и воспроизвести. В первом случае две причины точно должны были присутствовать, чтобы активировать ошибку.
- Оператор должен сделать изменения в параметрах режима и уровня энергии.
- Оператор должен сделать изменения в течение восьми секунд.
В случае второго дефекта все зависит от случайности. Ошибка активируется, если клавиша нажимается в тот момент, когда счетчик достигает нуля. Вот почему, несмотря на врожденный дефект в продукте, было отмечено только шесть несчастных случаев. Если бы ошибка была более явной и ее было бы легче запустить, несомненно, она была бы выявлена на AECL в ходе обычных процедур тестирования и проверки качества, и тогда о ней никогда не узнало бы общество. Если ошибку трудно активировать, ее также трудно будет выявить в ходе обычного процесса тестирования. Трудно найти ошибки, если не можешь их воспроизвести.
Замечание
|
Большинство аварий - аварии системные, то есть они происходят в результате сложных взаимодействий между различными компонентами и процессами. Приписывать аварии какой-либо одной причине - обычно серьезное заблуждение.14
|
Кроме двух главных ошибок, описанных выше, существовали многочисленные другие, которые мы наблюдали в отношении этой истории и которые, возможно, внесли свой вклад в длительное существование этих главных ошибок.
- Аппарат Therac-25 имел много неполадок, которые, по-видимому, не причиняли вреда, и операторы научились игнорировать эти капризы. Это напоминает историю о мальчике, который кричал «волк!». Люди научились не обращать на него внимания. Частые отключения и остановки машины беспокоили операторов. Однако операторы никогда не видели никаких вредных последствий для пациентов из-за этих отключений, поэтому они научились игнорировать их. Нечто, что может иметь серьезные последствия, игнорировалось. Постоянные неполадки также демонстрировали внутреннюю нестабильность и небезопасность машины.
- Когда проявлялась неполадка, Therac-25 выдавал непонятные сообщения, которые не давали обслуживающему персоналу никаких ключей к причинам и следствиям системной ошибки. Конструкция системы не предусматривала адекватной обратной связи, и персонал не мог понять, что происходит. Информация проходила от оператора через программное обеспечение к оборудованию. Когда связь нарушалась, пользователь не имел способов узнать состояние оборудования, поскольку построение программ не позволяло опрашивать оборудование. Ошибка при вводе данных ясно показывает последствия недостатка обратной связи. Например, вместо того, чтобы просто позволить оператору включать пучок лучей после изменения параметров, программы могли быть сделаны так, чтобы облучение не могло начаться до тех пор, пока программа не опросит оборудование о его состоянии и не предоставит эту информацию оператору для проверки.
- Когда клиники связывались с AECL по поводу потенциальных проблем, фирма не принимала эти проблемы всерьез. Поскольку люди из AECL не могли воспроизвести неисправность, они считали, что проблема не может быть связана с машиной. Более того, поскольку Therac-25 базировался на Therac-20 и Therac-6, машинах, прошедших полевые испытания, они были слишком самоуверенны в отношении потенциального риска. Ошибки были трудны для обнаружения, но, проявив достаточно внимания, их можно было найти. Инциденты в г. Якима 1985 и 1987 годов являются тому яркими примерами. Когда о проблеме впервые сообщили в 1985 году, фирма AECL провела только поверхностное расследование. Она сообщила, что ошибка невоспроизводима и, следовательно, проблемы нет. Однако в 1987 году фирма приняла сообщение всерьез и обнаружила возможную причину некорректного поведения в течение нескольких недель. Как только AECL оказалась готова признать, что ее продукт может содержать дефекты, она оказалась способна взглянуть на проблему с другой точки зрения.
- Когда была обнаружена ошибка ввода данных, видимо, не было сделано ни единой попытки оценить безопасность системы в целом. В то время поступили сообщения о пяти случаях, а ошибка ввода данных могла объяснить только два из них - случаи в Восточно-техасском онкологическом центре. Первые три передозировки нельзя объяснить этой ошибкой. Это указывает на то, что в системе были другие дефекты, не принятые в расчет, и машина, по-видимому, могла и далее передозировать облучение по другим причинам. Через восемь месяцев произошел второй случай в г. Якима.
- В Therac-25 было вновь использовано программное обеспечение его предшественников. Повторное использование программ позволило фирме AECL быстрее вывести Therac-25 на рынок, но внушило им ложную самоуверенность по поводу запаса прочности системы. Повторное использование программ приветствуется ведущими специалистами в индустрии в качестве пути к повышению продуктивности и снижению числа дефектов. Это так, но, с другой стороны, это означает, что программные ошибки воспроизводятся. Кроме того, повторное использование создает новые взаимодействия между ранее не связанными компонентами в новом окружении. Эти взаимодействия могут проявить скрытые дефекты и сформировать новые. Дефект, который не проявляет себя в одном случае, может внезапно возникнуть в другой среде. Также, программы обычно плохо документируются, и пользователям трудно понять нюансы повторного использования программных модулей. Массированное использование таких модулей может также привести к неудобному и небезопасному дизайну.
Зарисовка #1
В апреле 2000 года секретный девятистраничный документ НАТО, датированный 23 сентября 1999 года, попал в одну лондонскую издательскую фирму. В ходе Косовского конфликта компьютеры НАТО подвергались атакам со стороны сербов. Ученые НАТО, в поисках путей защиты от дальнейших вирусных атак, создавали вирусы для моделирования различных режимов атаки. Однако эксперимент пошел не так, как планировалось. Экспериментальные вирусы сделали в точности то, что и должны были сделать. Они извлекли документы с жестких дисков инфицированного компьютера и разослали их в качестве невидимых приложений к электронной почте. Так были разосланы секретные документы 15.
Ошибка в данном случае представляла собой не вирус, разработанный и реализованный специалистами НАТО. Ошибки были в сетевом окружении и процессе тестирования, которые и позволили разослать секретные материалы. Разве вам не нравится, когда компьютер делает то, что вы от него требуете?
Ошибка в процессоре Intel Pentium
В 1993 году корпорация Intel представила новый процессор Pentium™, который обещал стать самым лучшим процессором на рынке персональных компьютеров в то время. Через год после выпуска профессор Томас Найсели (Thomas Nicely) из Линчбергского колледжа (Lynchburg College) обнаружил ошибку и сообщил о ней в Intel. Это стало совершенным кошмаром для Intel, и в конечном итоге фирма согласилась заменять микросхему автоматически по требованию. Популярная пресса заставила нас поверить, что эта ошибка вызвана глупостью инженеров Intel, - в конце концов, разве трудно проверить каждое значение по справочной таблице? Как обычно, реальность намного сложнее, чем жирные заголовки газетных статей.
История
Профессор Томас Найсели - математик. В период 1993-1994 годов он работал над исследовательским проектом в области, называемой вычислительной теорией чисел (computational number theory). Одной из его целей было продемонстрировать полезность настольных персональных компьютеров. Для своего исследования он заставил большое число персональных компьютеров вычислять простые числа, пары простых чисел, триплеты и квадраплеты простых чисел для всех положительных целых до 6 x 10 12. Простые числа - это целые, которые делятся только на единицу и сами на себя (например, 3). Пары простых чисел - это два последовательных нечетных целых, которые также являются простыми (например, 3 и 5). Триплеты простых - это три последовательных целых нечетных простых числа (3, 5, 7). При вычислениях его программа предпринимала некоторое число проверок, в которых уже известные, опубликованные в литературе числа просчитывались, чтобы проверить правильность вычислений. Машина с процессором Pentium приняла участие в вычислениях в марте 1994, а 13 июня 1994 года проверка закончилась неожиданным значением. После четырех месяцев поиска профессор Найсели смог определить ошибку в блоке вычисления чисел с плавающей запятой (floating-point unit, FPU) процессора Pentium. В публичном сообщении, сделанном 9 декабря 1994 года, он описал свои трудности и исследования причины ошибок 16.
24 октября 1994 года, после того как профессор окончательно уверился в результатах анализа, он послал в службу технической поддержки Intel сообщение об ошибке. Когда Intel не ответила на сообщение, 30 октября 1994 года профессор Найсели написал письмо некоторым своим коллегам, объявив об открытии ошибки деления чисел с плавающей запятой в процессоре Intel Pentium. В электронном письме он описал свои наблюдения:
Если коротко, FPU процессора Pentium возвращает ошибочное значение для некоторых операций деления. Например,
1/824633702441,0
вычисляется некорректно (ошибочны все цифры после восьмой значащей цифры)17.
Вскоре сообщение об ошибке уже циркулировало на форуме CompuServe и в группах новостей Интернета. Александр Вулф (Alexander Wolf), репортер EE Times, подхватил эту историю и написал статью, которая появилась в номере EE Times от 7 ноября 1994 года. Отвечая на запрос репортера, Intel заявила, что они обнаружили эту ошибку летом 1994 года, и она была исправлена в процессорах, выпущенных позднее. Однако Intel не смогла определить число выпущенных дефектных процессоров, и они попытались сгладить важность этой ошибки.
Смит (Smith), представитель Intel, подчеркнул, что этот дефект не повлияет на среднего пользователя. Говоря о Найсели, Смит сказал: «Это исключительный пользователь. Он круглосуточно проводит вычисления обратных величин. То, что он обнаружил после многих месяцев вычислений, является примером того, что восемь десятичных чисел правильны и только девятая отображается неверно. То есть ошибка у вас будет только в девятом знаке справа от точки. Я думаю, что, даже если вы инженер, вы этого не заметите».18
CNN распространила это заявление 22 ноября 1994 года, и вскоре оно было во всех главных средствах массовой информации, таких, как New York Times и Associated Press. В других интервью Intel повторяла свое раннее заявление о том, что ошибка несущественна для среднего пользователя.
В среду Intel заявила, что они не считают необходимым отзывать процессор, утверждая, что обычный пользователь имеет только один шанс из девяти миллиардов получить неверный результат из-за этой ошибки и таким образом не будет никаких заметных последствий для компьютеров дома и в офисе. Компания заявляет, что она продолжает поставлять сборщикам компьютеров процессоры Pentium, сделанные до того, как проблема была обнаружена19 .
28 ноября 1994 года Тим Коу (Tim Coe) из компании Vitess Semiconductor опубликовал статью в группе новостей comp.sys.intel, в которой он путем анализа восстановил реализацию алгоритма и предложил модель поведения процессора Pentium. Через несколько дней появились аппаратные и программные «заплатки» для ошибки. 3 декабря 1994 года Воэн Пратт (Vaughan R. Pratt) из Стэндфордского университета опубликовал письмо в группах новостей comp.arch и comp.sys.intel, в котором оспаривал точку зрения Intel о том, что вероятность встречи с ошибкой составляет «один раз в 27 000 лет». Он смог продемонстрировать возможность активации ошибки один раз в каждые 3 миллисекунды в достаточно правдоподобном сценарии. А также он продемонстрировал, что достаточно безобидно выглядящее деление 4,999999/14,999999 приводило к отклонению от правильного результата на 0,00000407 при использовании дефектного процессора20.
12 декабря 1994 года фирма IBM выпустила сообщение, в котором также был подвергнут сомнению анализ Intel о том, что вероятность обнаружения ошибки составляет один к девяти миллионам21.
14 декабря 1994 года Intel опубликовала официальное сообщение, которое было датировано 30 ноября 1994 года22.
В этом сообщении рассматривалась данная ошибка, обсуждались ее последствия, и этот документ был, очевидно, источником многих заявлений Intel. В этом отчете определялось, что вероятность встречи с ошибкой составляет «один к девяти миллиардам» и что среднее время появления ошибки - «один раз в 27 000 лет». Далее Intel описывала причину ошибки и алгоритм работы FPU. Инженеры компании избрали в качестве алгоритма деления процессора SRT алгоритм23 по основанию 4 (radix 4 SRT algorithm), чтобы скорость деления могла быть удвоена, по сравнению с 486-м процессором. Полное описание SRT алгоритма, использованного в процессоре Pentium, находится за рамками этой книги. Детали можно найти в работе Edelman, Sharangpani и Bryant24.
Однако главная причина ошибки в том, что SRT-алгоритм требует справочную таблицу для определения частного. Значение в справочной таблице генерируется численно и загружается в программируемый справочный массив. Дефект скрипта привел к тому, что несколько элементов в справочной таблице были пропущены. Когда выполнялась операция деления, которая требовала эти значения, извлекалось неверное число, и точность вычисленного результата частного оказывалась сниженной.
В конечном счете 20 декабря 1994 года фирма Intel объявила, что она начинает заменять процессоры Pentium по требованию. Это существенно снизило ажиотаж вокруг ошибки в процессоре. Однако это не остановило тех, кто анализировал эту ошибку. 19 сентября 1995 года Алан Эдельман (Alan Edelman) опубликовал отчет, в котором провел детальный анализ этой ошибки25.
В своем отчете он определил, что было только два способа, с помощью которых можно было осуществить доступ к ошибочным данным и использовать их при вычислениях. Ошибочное значение извлекалось, только если делитель содержал шесть последовательных бит, от 5-го до 10-го, установленных в единицу. Таким образом, ошибочные табличные значения могли не извлекаться при тестах, основанных на случайной выборке значения; тест, способный выявить ошибку, должен работать лучше, чем простая случайная выборка. Он также показал, что максимальная абсолютная ошибка в данном случае не могла превышать 0,00005.
Вывод
Ошибка в процессоре Pentium - это ошибка, которую легко совершить, но которую трудно обнаружить в силу двух причин. Во-первых, ошибка в результате операции деления, проведенной дефектным процессором, не превышает 0,00005. Как много из нас придрались бы к различию между 0,33332922 и 0,33333329? Более того, если бы мы использовали эти значения в приложениях, которые автоматически округляют их до двух значащих цифр, мы бы никогда и не узнали о таких небольших различиях. Воэн Пратт писал в своем сообщении:
Эта ошибка наиболее коварна: она почти столь же коварна, как если бы вовсе не вызывала тревоги у людей при просмотре ими колонок своих данных. Таким образом, крошечные ошибки в одну стотысячную могут в течение долгого времени проникать в триллионы вычислений, совершаемых по всему миру, и практически нет способа определить их, кроме как осуществляя массированную проверку на ошибку в FPU, которая совершенно не является необходимой для надежно работающего процессора26.
Вторая причина того, что ошибку трудно найти, состоит в том, что дефект проявляется чрезвычайно редко 27.
Шанс того, что неверное значение будет извлечено из справочной таблицы в тестах по случайной выборке, чрезвычайно низок. Intel, используя эти тесты, независимо от других идентифицировала ошибку через год после выпуска процессора. Это ясно говорит о трудностях тестирования и обнаружения этого дефекта.
Для профессора Найсели процесс поиска ошибки начался, поскольку он заметил небольшие несоответствия. Но причина того, что он оказался в состоянии заметить несоответствия, состоит в том, что он поместил операцию проверки вычислений в свой код. Без этих проверок он мог и не заметить проблему так рано, и его вычисления простых чисел могли быть ошибочными. Как только он понял, что проблема существует, он начал систематически убирать ее возможные причины, пока поиск не сузился до FPU процессора Pentium.
Уроков, которые мы можем извлечь за счет Intel, довольно много:
- Тестирование должно не только следовать спецификации, оно для полноты также должно учитывать использованные алгоритмы.
- Мы должны использовать в программах средства для постоянного контроля над правильностью программы. Это даст нам возможность найти потенциальные ошибки настолько рано, насколько возможно.
- Все неожиданные результаты нужно ставить под сомнение и проверять до тех пор, пока мы не объясним их причину. Ошибка может оказаться скрытой.
- Обнаружение потенциальной фундаментальной причины ошибки - это систематический процесс. Сначала нужно построить гипотезу о возможной причине, а затем проводить эксперименты для ее проверки.
- Создание упрощенного воспроизводимого опыта может все изменить. Тот факт, что профессор Найсели смог активировать ошибку простой операцией деления, позволил ему протестировать другие системы и конфигурации, что позволило ему опровергнуть неверные гипотезы.
Зарисовка #2
Согласно легенде, ранние конструкции торпед имели устройства безопасности, предохраняющие подводную лодку от повреждения, когда торпеда была запущена. Торпеда была сконструирована так, чтобы самоуничтожение запускалось, если торпеда поворачивалась на 180? . Идея была в том, что повернувшаяся на 180? торпеда может повредить выпустившую ее лодку. Однажды, капитан подводной лодки решил выпустить торпеду. Однако торпеда застряла в пусковой камере, и ее не смогли удалить. Капитан решил вернуться в порт для ремонта. Когда субмарина развернулась на 180 градусов, торпеда взорвалась и потопила лодку.
В данном случае ошибка заключалась в конструкции торпеды. Когда-то кто-то решил, что неплохо было бы встроить в торпеду некий предохранитель так, чтобы она не смогла потопить запустившую ее лодку. Идея была хорошей, а вот ее реализация - нет. Конструктор не учел тот самый случай, который и потопил субмарину. Однако остается неясным, на самом ли деле на заре конструирования вооружения для субмарин такой случай произошел или это всего лишь легенда. Но он показался нам достаточно реалистичным и поучительным, так что мы решили его привести.
Ariane 5. Ошибка операнда
Если коротко, несчастный случай с Arian 5 был вызван необработанным исключением при преобразовании 64-битного значения с плавающей запятой в 16 битное целое значение со знаком. Значение с плавающей запятой, вызвавшее исключение (или ошибку операнда, как она была названа в официальном сообщении), оказалось больше, чем значение, которое может быть представлено 16 битным целым. Однако более полная версия этой истории гораздо более интересна и поучительна 28.
История
Ракета-носитель Ariane 5 была ответом попыткам Европейского космического агентства (European Space Agency) стать лидером в запусках ракет на коммерческом космическом рынке. Стоившая 7 миллиардов долларов и строившаяся в течение 10 лет, Arian 5 могла вывести на орбиту два трехтонных спутника.
При своем первом полете ракета Ariane 5 взорвалась через 40 секунд после старта утром 4 июня 1996 года. Анализ данных полета быстро показал, что ракета вела себя нормально до того момента, когда она вдруг отклонилась от курса и самоуничтожилась. Погода в то утро была приемлемой, так что она не могла оказать влияние. Полетные данные также показывали, что активная система и первичная Инерционная система ориентировки (Inertial Reference System), которые влияли на управление соплами твердотопливного ускорителя, более или менее одновременно отказали прямо перед разрушением ракеты.
После инцидента была сформирована комиссия по его расследованию. Комиссия для решения своей задачи располагала телеметрическими данными ракеты, данными о траектории с радиолокационных станций, оптическими наблюдениями ракеты и упавших обломков и восстановленной Инерционной системой ориентировки. Кроме того, комиссия располагала отдельными компонентами ракеты и системами программ, использованных в ней, для тестирований и осмотра. Получив эту информацию, комиссия смогла реконструировать последовательность событий 4 июня 1996 года.
- Программный модуль, в котором в итоге возникла ошибка, был унаследован от ракеты-носителя Arian 4. Этот модуль производил выравнивание инерционной платформы для того, чтобы оценить точность измерений, проведенных Инерционной системой ориентировки. После старта данный модуль более не служил в Ariane 5 никаким целям. Однако в Ariane 4 этот модуль работал в течение еще полных 50 секунд. Начальная часть траектории полета Ariane 5 существенно отличалась от траектории Ariane 4, и этот программный модуль никогда соответствующим образом не тестировался.
- Вскоре после старта ошибочный программный модуль попытался посчитать значение, основанное на горизонтальной скорости ракеты. Поскольку для Ariane 5 это значение было существенно больше, чем то, которое ожидалось для Ariane 4, возникла ошибка и на активной, и на запасной Инерционной системе ориентировки. Допустимость такого преобразования не была проверена, поскольку ожидалось, что такого никогда не случится.
- Спецификация обработки ошибок в системе указывала, что контекст ошибок должен быть сохранен в постоянной памяти (ПЗУ) до отключения процессора. После ошибки операнда Инерционная система ориентировки сохранила контекст ошибки, как было установлено. Эти данные были прочитаны бортовым компьютером. На основе этих данных компьютер отдал команду соплам твердотопливного ускорителя и главному двигателю. Команда требовала полного отклонения сопел, что вызвало то, что ракета вышла на запредельную траекторию.
- На новой траектории ракета подверглась запредельной аэродинамической нагрузке и начала разрушаться. Стартовые двигатели отделились от ракеты, что запустило ее самоуничтожение.
Несомненно, глупая ошибка, но интересен такой вопрос: как эта ошибка миновала стадию тестирования? В аэрокосмической индустрии обычно строгие стандарты и скрупулезные процессы и процедуры, направленные на проверку безопасности из-за высокой цены ошибок. Комиссия по расследованию задала тот же вопрос, и команда обслуживания Ariane 5 представила следующие объяснения:
- Команда Ariane 5 решила не защищать некоторые переменные от возможной ошибки операнда, поскольку они считали, что значения этих переменных либо ограничены физическими факторами, либо имеют существенный запас по максимальной величине.
- Команда Ariane 5 решила не включать данные о траектории в функциональные требования для Инерционной системы ориентировки. Следовательно, данные о траектории Ariane 5 не использовались при тестировании.
- Из-за физических законов трудно осуществить реалистичный полетный тест Инерционной системы ориентировки. При функциональном имитационном тестировании полетных программ было решено не включать в тест эту систему главным образом по той причине, что она должна быть проверена при тестировании аппаратного уровня, а также потому, что было бы трудно достигнуть необходимой точности при имитационном тестировании, если бы была использована реальная Инерционная система ориентировки.
Вывод
Как и в случае других ошибок, которые мы обсуждали, дефект на Ariane 5 не был вызван одной причиной. В ходе всей разработки и процессов тестирования существовало много стадий, на которых данный дефект мог быть выявлен.
- Программный модуль был повторно использован в новой среде, где условия функционирования отличались от требований программного модуля. Эти требования не были пересмотрены.
- Система выявила и распознала ошибку. К несчастью, спецификация механизма обработки ошибок была несоответственной и вызвала окончательное разрушение.
- Ошибочный модуль никогда должным образом не тестировался в новом окружении - ни на уровне оборудования, ни на уровне системной интеграции. Следовательно, ошибочность разработки и реализации не была обнаружена.
Отчет комиссии по расследованию содержит следующее наблюдение, которое мы считаем очень подходящим для всей индустрии программного обеспечения, а не только для разработчиков программ для Ariane 5.
Главной задачей при разработке Ariane 5 является уклон в сторону уменьшения случайной аварии. …Возникшее исключение, объясняется не случайной аварией, но ошибкой конструкции. Исключение было обнаружено, но обработано неверно, поскольку была принята точка зрения, что программу следует рассматривать как правильную, пока не показано обратное. …Комиссия придерживается противоположной точки зрения, что программное обеспечение нужно считать ошибочным, пока использование признанных в настоящее время наилучшими практических методов не продемонстрирует его правильность29.
Однако одна из причин того, что комиссия по расследованию смогла успешно определить виновного, - в сборе данных измерений, в имитационных средах и в документации. Без метеорологических данных было бы трудно исключить влияние погоды. Без телеметрии и полетных данных было бы трудно определить временные параметры изменения траектории и ошибку Инерционной системы ориентации, что позволило комиссии быстро сузить область потенциальных дефектов. Послеполетные имитационные исследования были проведены с использованием реальных данных о траектории полета Ariane 5, и моделирование точно воспроизвело цепь событий, приведших к аварии системы. Комиссия смогла воспроизвести ошибку!
Зарисовка #3
Интернет - это великий источник информации. К сожалению, слово «великий» не используется здесь как прилагательное, описывающее качество информации. «Великий» обозначает количество информации - слишком большое количество информации, которая при некоторых обстоятельствах нежелательна и которую мы называем спам. В других случаях доступ к некоторым специфическим формам информации, таким, как порнография, намеренно блокируется и подвергается цензуре по различным причинам. В любом случае существует некоторое число инструментов, чья функция - служить в роли фильтров для удаления потенциально нежелательных электронных писем и блокировать запросы к некоторым Web-сайтам. Эти средства могут быть источниками большого раздражения. Например:
Только что услышал сообщение по сетевому радио CBS о том, что футбольные фанаты по всей стране не смогли узнать по Интернету результаты воскресного Суперкубка. Оказалось, что программы-фильтры доступа в Web, установленные на браузерах (например, в некоторых публичных библиотеках), рассматривали «ХХХ» в словах «Суперкубок XXXIV» как ссылку на порносайт30.
В другом примере чрезмерная цензура потенциально оскорбительных слов может сделать вполне безобидное сообщение неразборчивым и забавным. Так Интернет-служба BBC подвергла цензуре такое предложение:
Я надеюсь, вы сохраните пристрастие к острым словечкам, когда я заскочу к вам в класс в субботу в Сканторп, Эссекс. «I hope you still have your appetite for scraps of dickens when I bump into you in class in Scunthorpe, Essex, on Saturday».
Программа превратила его в
«I hope you still have your appetite for s****s of dickens when I ***p into you in class in S****horpe, Es***, on Sa****ay»31, 32
Однако наибольшее раздражение вызывает программное обеспечение, направленное против спама, которое может удалить важные электронные письма, поскольку ошибочно классифицирует их как спам.
Я недавно провел обновление (?) MS Office до MS Office 2000, который среди прочих возможностей позволяет устанавливать более 8 фильтров электронной корреспонденции. Я радостно начал запускать все эти возможности, включая фильтрацию почты. Сюрприз! Я обнаружил, что 8-10 важных сообщений, которые все являлись ответами на запросы, посланные на адреса из личной адресной книги, были перенесены в папку для почты-спама.
Что же случилось? Я участвовал в благотворительной велосипедной гонке, и мне необходимо было сообщить спонсорам, что мне нужны их деньги. Я выслал им электронные письма с просьбой прислать мне чеки. Конечно, это сообщение содержало, по крайней мере, один знак «$», а также, поскольку я возбудимый человек, содержало, по крайней мере, один двойной восклицательный знак «!!». В конце я просил моего респондента выполнить мою гиперболизированную версию его обещания:
>>Марк, ты не обещал мне 5,000$ или что-то около того?
Мы также встретили здесь волшебную фразу «,000». Недавно замечательные люди из Редмонда33 (Redmond) определили, что, если эти три элемента одновременно присутствуют, значит, вы получили спам. Текущее правило (взятое с их Web-сайта) гласит:
Текст содержит «,000» AND текст содержит «!!» AND текст содержит «$»
Кто бы мог подумать? Даже взглянув на список их фильтров, я далеко не сразу понял, какое правило я нарушил (ОК, иногда я медленно соображаю)34.
Это ошибки программы или ошибки пользователя? Во всех трех случаях программное обеспечение функционирует в точности так, как предписывает алгоритм, и производители не посчитали бы эти случаи за ошибки. Однако иногда действие алгоритма - это не то, что хочет конечный пользователь. Поэтому, как конечные пользователи мы выключаем фильтрующее и противоспамное программное обеспечение или же находим пути обойти неверный алгоритм. В главе 3 мы исследуем природу ошибок, что может помочь нам определить точно, что такое ошибка.
Аппарат для исследования климата Марса
Аппарат для исследования климата Марса (Mars Climate Orbiter) является частью программы Mars Surveyor по изучению и картографированию Марса. Ожидалось, что программа Mars Surveyor продлится в течение 10 лет и в рамках программы будет запускаться одна экспедиция в год. Первыми двумя экспедициями были миссии аппаратов Mars Pathfinder и Mars Global Surveyor в 1996 году. Аппарат Mars Climate Orbiter был запущен 11 декабря 1998 года. Следом за ним был также запущен Mars Polar Lander - 3 января 1999. Оба аппарата были потеряны вскоре после того, как они достигли красной планеты. Эти два космических корабля стоили NASA около 327,6 миллиона долларов, потраченных на их создание и функционирование. Эти аварии заставили NASA пересмотреть свои цели и методы в Марсианской программе, чтобы быть уверенными в успехе будущих миссий. Причину аварии Mars Polar Lander определить все еще нельзя. Однако причина потери Mars Climate Orbiter выяснена, поэтому мы сконцентрируем наше исследование на этом аппарате.
История
Mars Climate Orbiter был запущен 11 декабря 1998 года с помощью ракеты-носителя Delta 11 с космодрома на мысе Канаверал (Canaveral) во Флориде. После девяти с половиной месяцев космического полета 23 сентября 1999 года, его планировалось вывести на орбиту вокруг Марса. Однако, когда пришло назначенное время, что-то произошло.
Сегодня, ранним утром, около 2:00 утра по летнему тихоокеанскому времени, аппарат включил главный двигатель для выхода на орбиту вокруг планеты. Вся информация, приходившая до этого момента с борта космического корабля, выглядела нормально. Запуск двигателя начался как планировалось, за пять минут до того, как аппарат оказался за планетой (если смотреть с Земли). Управление полетом не зафиксировало сигнала, когда ожидалось, что аппарат должен был выйти из-за планеты35.
Mars Climate Orbiter разрабатывался объединенной командой инженеров и ученых в двух местах - Лаборатории реактивных двигателей (Jet Propulsion Laboratory, JPL), расположенной в Пасадене (Pasadena), штат Калифорния, и на заводе Lockheed Martin Astronautics, LMA в Денвере, штат Колорадо. Завод LMA был ответственен за планирование и разработку Mars Climate Orbiter с точки зрения интеграции и тестирования полетных систем, а также за операцию запуска. Лаборатория JPL несла ответственность за управление проектом, за управление разработкой космического аппарата и приборов, за системотехнику, за планирование миссии, навигацию, разработку операционной системы миссии, разработку наземной системы сбора данных и гарантии безопасности 36.
В ходе девяти с половиной месяцев полета наземные службы отслеживали и сравнивали наблюдаемую траекторию аппарата с расчетной. Также наземные службы проверяли все события, происходящие на борту Mars Climate Orbiter. Одним из таких событий было уменьшение углового момента (Angular Momentum Desaturation, AMD). Событие AMD возникало, когда аппарат запускал двигатели малой тяги для устранения углового момента, накопившегося в его маховиках. В основе своей - это калибровочный маневр для поддержания функционирования системных компонентов в заданном диапазоне. Когда возникало событие AMD, происходила следующая последовательность событий:
- Аппарат посылал значимые данные на наземную станцию.
- Данные обрабатывались программным модулем, называвшимся SM-FORCE.
- Результаты работы модуля SM-FORCE помещались в файл, называемый файл AMD.
- Данные файла AMD использовались для вычисления изменения скорости аппарата.
- Вычисленное значение изменения скорости использовалось для моделирования траектории корабля.
Согласно спецификации модуль SM-FORCE должен формировать данные, помещаемые в файл AMD, используя метрические единицы, то есть ньютон-секунды. Однако по тем или иным причинам модуль SM-FORCE на наземной станции выводил данные, используя английские единицы (фунт-секунды) (в официальном отчете причина не указывалась, и мы не будем строить предположения по этому поводу). Программный модуль, который рассчитывал изменение скорости с использованием данных из файла AMD, ожидал, что они будут в метрических единицах, согласно спецификации. На борту аппарата модуль, который создавал файл AMD, использовал метрические единицы. Это привело к различию между траекториями, вычисленными космическим аппаратом и наземной станцией, а именно параметры траектории, вычисленные наземной станцией, были в 4,45 раза меньше, поскольку 1 фунт-секунда равен 4,45 ньютон-секундам.
Корабль периодически передавал вычисленную модель траектории на наземную станцию для сравнения. Теоретически быстрое сопоставление моделей, полученных кораблем и наземной станцией, должно было поднять тревогу. Однако несколько осложняющих факторов помешали наземному персоналу осознать ошибку.
- В программном обеспечении наземной станции было несколько ошибок, и персонал не мог использовать модуль SM-FORCE для расчета траектории корабля. Эти ошибки были исправлены только к четвертому месяцу полета, в районе апреля 1999 года.
- Персонал, ответственный за навигацию, не знал о том, что данные об изменении скорости с борта корабля были доступны для сравнения в течение долгого времени после запуска.
- Линия обзора между аппаратом и Землей не давала персоналу точно моделировать траекторию корабля, используя наблюдения.
Если бы событие AMD возникало нечасто, коэффициент 4,45 мог и не иметь таких серьезных последствий. Однако из-за формы корабля это событие возникало в 10-14 раз чаще, чем ожидалось. Более того, когда были обнаружены различия в моделях наземной станции, космического корабля и данных наблюдений, неофициальный отчет об этих различиях был отправлен по электронной почте, без использования стандартной процедуры для таких случаев. В конечном итоге эти различия не были устранены до потери космического аппарата.
8 сентября 1999 года персонал наземной станции рассчитал маневр для вывода корабля на орбиту Марса. Это вычисление было сделано с использованием неверной модели. Целью этого маневра была коррекция траектории корабля таким образом, чтобы точка наибольшего приближения к Марсу составляла 226 километров. 23 сентября маневр был выполнен. Запуск двигателя произошел в 09:00:46 по Всемирному координированному времени (UTC), более известному как время по Гринвичу (GMT). Спустя четыре минуты и шесть секунд наземная станция потеряла сигнал с аппарата. Потеря сигнала была вызвана тем, что Orbiter находился за планетой. Но это событие произошло на 49 секунд раньше, чем было предсказано моделью траектории. Поскольку была использована ошибочная модель траектории, корабль оказался в действительности ближе к поверхности планеты, чем ожидалось. Действительная точка максимального сближения оказалась приблизительно 57 километров. По оценке, такая высота оказалась слишком мала для аппарата.
Вывод
«Люди иногда делают ошибки», - заявил доктор Эдвард Уэйлер (Edward Weiler), первый заместитель главы NASA по науке о космосе. «Данная проблема - это не ошибка, это неудача системотехники и систем проверки в наших технологиях, предназначенных для обнаружения ошибки. Вот почему мы потеряли космический аппарат» 37.
Одна ошибка была допущена в одной из частей программного обеспечения. Однако целая серия ошибок на протяжении девяти с половиной месяцев полета привела в конечном итоге к потере аппарата. Назвать первоначальную ошибку фундаментальной причиной аварии мешает тот факт, что данный инцидент не был результатом только одной ошибки. Вот три главных составляющих аварии Mars Climate Orbiter:
- Если бы было проведено соответствующее тестирование, такая ошибка могла быть быстро обнаружена и исправлена.
- Однако, поскольку программное обеспечение наземной станции не было хорошо протестировано, после запуска космического аппарата проявились дополнительные ошибки. Эти ошибки отсрочили наблюдения над проблемой. Вместо девяти месяцев из-за дополнительных ошибок персонал имел только пять месяцев на выяснение причины несоответствий.
- Когда несоответствие в итоге было обнаружено, оно так и не было правильно объяснено. Первоначальная причина так и не была выяснена, а это дало бы персоналу информацию о надвигающейся катастрофе.
В отчете комиссии по расследованию первой фазы ее работы также были указаны следующие факторы неудачи и сделаны некоторые дополнительные рекомендации38. Эти факторы и рекомендации главным образом касаются методов и технологий проекта, которые могли быть полезны для программного обеспечения наземных служб.
- Обмен информацией. - Обмен информацией является главной проблемой в проекте большого масштаба, программном или любом другом. В случае с Mars Climate Orbiter команды, работавшие над проектами, не связывались действенным образом друг с другом. Команда наземного обслуживания не сообщала о своем беспокойстве по поводу различия траекторий команде управления космическим аппаратом и органам управления проектом. Важная информация от одной команды не передавалась другим командам, что внесло свой вклад в аварию.
- Обучение и переход от стадии разработки к стадии функционирования. - Одна из причин того, что различия в моделях траекторий так и не были полностью объяснены, пока не случилась катастрофа, состоит в том, что обслуживающий персонал не был полностью обучен. Переход от разработки к функционированию не был тщательно спланирован и осуществлен. В разработке программного обеспечения это можно приравнять к тому, как если бы команда разработчиков перебросила готовую систему команде сетевых операторов, не снабдив их соответствующими инструментами и не проведя обучение особенностям и поведению системы.
- Проанализируй, что может выйти из строя. - Исследователи считают, что предварительный анализ критических условий системы может помочь предотвратить будущие аварии. Это сходно с анализом отказоустойчивости, анализом процедур восстановления после ошибки и планированием нагрузки, проводимым в распределенных компьютерных средах, обычно встречающихся в системе Интернета.
- Самоуверенность. - Комиссия по расследованию обнаружила, что персонал проекта считал посылку космического аппарата на орбиту вокруг Марса легкой задачей, поскольку JPL имела тридцатилетний опыт безошибочной межпланетной навигации. Эта самоуверенность может объяснить недостаток соответствующего тестирования в программных модулях.
Зарисовка #4
Этот инцидент не связан с какой-либо программной ошибкой. В действительности в этой истории программное обеспечение не участвует вовсе. Однако эта история поучительна и забавна и заслуживает места в этой главе. Это укороченная версия истории, в которой описывается субподрядчик, который выполнял работу по прокладке волоконно-оптического кабеля для одной местной телефонной компании. Перед описываемым инцидентом субподрядчик отключил телефонную линию и воспользовался машиной-канавокопателем для прорытия канавы в земле. Однако земля была мокрой от утренней росы, и канавокопатель соскользнул по откосу дороги и столкнул субподрядчика в яму глубиной 10-15 футов. Человек, заметивший этот несчастный случай, подъехал к ближайшему дому, чтобы вызвать по телефону помощь, но телефон не работал. Почему? Потому что телефонная линия была отключена субподрядчиком для проведения работы. Что еще более курьезно, в машине субподрядчика был сотовый телефон, но товарищи раненого по работе совершенно забыли о нем в момент инцидента. В конце концов другой свидетель смог позвонить в службу спасения из другого дома, и инцидент закончился благополучно. Пострадавший был отправлен в больницу без серьезных повреждений. Канавокопатель извлекли из ямы, и общество получило волоконно-оптические кабели 39.
Какой урок мы можем извлечь из этого инцидента, который можно было бы применить к наладке программного обеспечения? Главный вывод - всегда имейте резервную копию. Часто в азарте исправления и изменения кода мы чувствуем такую уверенность в своих изменениях, что принимаем их без должного тестирования. Принятые изменения - это такие модификации, которые трудно вернуть в первоначальное состояние, и мы оказываемся в тупике, если возникает проблема. Это напоминает историю с субподрядчиком, который отключил телефонную линию, не подумав о возможности того, что что-то может случиться, и, когда это действительно случилось, он остался без телефона.
Создание резервной копии кода, комментирование40 кода при его замене вместо удаления, создание копии старой функции с другим именем и использование инструментов управления конфигурацией - вот некоторые советы, которые вы можете использовать, чтобы сохранять пути отступления до того, как вы серьезно протестируете ваши изменения. Однако эти пути бесполезны, если вы не помните, что они у вас есть. В описанной истории помощь могла быть вызвана раньше, если бы кто-нибудь вспомнил о сотовом телефоне в машине. В нашей работе опасность не в том, что мы забудем, что у нас есть путь отступления, а в попытках определить, какая версия старого кода (или какая комбинация версий из разных модулей) соответствует последней работающей версии системы.
Авария на телефонной компании AT&T
Для большинства из нас авария на телефонной станции означает то, что мы не можем пользоваться телефонами: не можем звонить сами, и люди не могут звонить нам. Аварии - это ожидаемый и принимаемый риск в этой индустрии. Ураганы могут повредить телефонные линии, подземные кабели могут быть повреждены из-за человеческой ошибки или землетрясения или же возможны ошибки в программном обеспечении, которое управляет сетью.
К 15 января 1990 года в компании AT&T произошла авария, охватившая всю страну и продолжавшаяся девять часов. Причина состояла в ошибке в программном обеспечении, которое должно было сделать эту систему более эффективной. Восемь лет спустя, 13 апреля 1998 года, на AT&T произошла другая крупная авария в сети ретрансляции кадров (frame relay network), которая затронула банкоматы, операции с кредитными картами и другие службы, связанные с передачей бизнес-данных. Авария длилась 26 часов. И опять ошибка была внесена при обновлении программного обеспечения.
Сделала ли AT&T одну и ту же ошибку дважды или здесь было что-то еще?
История: авария 1990 года
В 1990 году телефонная сеть AT&T состояла из 114 соединенных между собой систем коммутирования вызовов 4ESS (4ESS toll switching systems) (это представление является упрощением). Для нашего обсуждения мы мысленно смоделируем сеть AT&T в виде схемы. В этой схеме существует 114 узлов (точек пересечения) и каждый узел представляет один из 114 коммутаторов 4ESS. Линии, нарисованные между узлами, изображают коммуникационные каналы между ними.
В такой телефонной сети, когда один из узлов сталкивается с проблемой, он посылает сообщение «не беспокоить» всем узлам, с которыми он соединен. Это сообщение информирует соседний узел о том, что данный узел не может обрабатывать новые вызовы и просит соседний узел считать его не работающим. Тем временем аварийный узел активирует процесс восстановления после сбоя, который длится от четырех до шести секунд. По окончании процесса восстановления аварийный узел посылает сообщение, известное как Начальное адресное сообщение (Initial Address Message, IAM), всем соседним узлам, сообщая им о своем новом статусе и требуя направлять вызовы на восстановленный узел.
В середине декабря 1989 года AT&T произвела обновление программного обеспечения на коммутаторах 4ESS с целью увеличения производительности системы и введения быстрого процесса восстановления после ошибки. Приблизительно в 2:30 по Восточному стандартному времени (EST) 15 января 1990 года на 4ESS коммутаторе в Нью-Йорке возникла небольшая аппаратная проблема, и коммутатор начал процесс восстановления, как было описано выше. После того как Нью-Йоркский коммутатор исправил проблему, он послал сообщение IAM для уведомления соседних коммутаторов, что он готов продолжать работу. Однако обновление программ, проведенное в середине декабря, внесло в действия ошибку. Эта ошибка проявилась, когда коммутатор получил два IAM сообщения с интервалом 1/100 секунды. Некоторые данные в коммутаторе оказались искажены, и он прекратил обслуживание, перейдя к инициализации. Когда соседние узлы выходили из строя, они запускали тот же самый процесс восстановления. Поскольку все коммутаторы были одинаковы, та же последовательность событий каскадом распространялась от одного коммутатора к другому и вывела из строя всю систему.
В течение дня инженеры AT&T смогли стабилизировать сеть, уменьшив нагрузку на нее. К 23:30 EST они смогли очистить все звенья сети, и система практически вернулась к нормальному состоянию.
Во вторник, 16 января 1990 года, инженеры AT&T смогли идентифицировать и выделить ошибку, которая была отслежена до набора ошибочных кодов. Этот код активировался в ходе процедуры восстановления коммутатора. Отрывок кода, который вызвал аварию, представлен ниже41:
1.do {
2. ...
3. switch (expression){
4. case (value 0):{
5. if (logical_test){
6. ...
7. break;
8. }else {
9. ...
10. }
11. ...
12. break;
13.}
14. ...
15. }
16. ...
17. }while (expression);
В данном случае виновным оказался оператор break в строке 7. Согласно реализации, если logical_test прошел успешно, программа переходит к строке 6 для выполнения расположенных там операторов. Когда выполнение программы доходит до строки 7, оператор break заставляет программу покинуть блок оператора switch, расположенного между 3 и 15 строками, и исполнять код начиная со строки 16. Однако эта часть исполнения не входила в намерения программиста. Программист желал, чтобы оператор break в седьмой строке прерывал выполнение условного оператора if-then и чтобы после исполнения седьмой строки исполнение продолжилось со строки 11. В таблице 2.1 показаны различия в исполнении программы, как это было задумано и как это было реализовано.
Таблица 2.1. Желаемые и реализованные последовательности инструкций, которые привели к аварии в телефонной сети AT&T 1990 года
ход выполнения | реализация | ожидалось |
шаг 1 | строка 2 | строка 2 |
шаг 2 | строка 3 | строка 3 |
шаг 3 | строка 4 | строка 4 |
шаг 4 | строка 5 | строка 5 |
шаг 5 | строка 6 | строка 6 |
шаг 6 | строка 7 | строка 7 |
шаг 7 | строка 16 | строка 11 (не верно) |
шаг 8 | строка 17 | строка 12 (не верно) |
шаг 9 | - | строка 16 (не верно) |
Вывод: авария 1990 года
Программная ошибка, приведшая к аварии 1990 года, - это типичная ошибка новичка. Но ошибки делаем все мы, и даже ветеран с 20-летним стажем может совершить случайно глупую ошибку. Если ошибки неизбежны, вопрос состоит в том, что мы можем сделать для того, чтобы отыскать ошибку до того, как она станет достоянием общества? У нас нет никакого знания из первых рук о внутренней кухне разработки программ в AT&T в 1990 году. Следовательно, мы не можем оценить вклад других причин. В официальном отчете AT&T говорилось:
Мы считаем, что процессы планирования, разработки и тестирования программ, которые мы используем, базируются на прочных и качественных основах. Все будущие программы будут также скрупулезно тестироваться. Мы используем опыт, приобретенный при решении этой проблемы, для дальнейшего улучшения наших методов42.
Мы не считаем возможным обвинять в аварии 1990 года процесс разработки программного обеспечения в AT&T и не имеем оснований считать, что AT&T не протестировала тщательно обновление для своих программ. Оглядываясь назад, легко говорить, что, если бы разработчики только протестировали свои программы, они сразу увидели бы эту ошибку. Или то, что, если бы они проверили код, они, возможно, обнаружили бы дефект. Проверка кода может быть и помогла бы в данном случае. Однако единственный случай, когда проверка кода помогла бы найти данную ошибку, если бы другой специалист увидел именно эту строку кода и спросил первого программиста, входил ли этот код в его (ее) намерения. А единственная причина, по которой этот специалист мог бы задать такой вопрос, - знакомство со спецификацией к этому конкретному блоку кода.
Ошибки такого рода обычно нелегко выявить при тестировании в обычной тестовой среде. Такую ошибку можно воспроизвести, как только вы поймете ее и создадите последовательность действий для ее активации. Однако шанс создать правильную последовательность событий случайным образом очень мал, особенно если система будет использоваться в крупномасштабной среде реального времени, которую трудно имитировать в лабораторных условиях. Более того, новое программное обеспечение работало правильно примерно в течение месяца, что соответствует нескольким миллиардам обработанных вызовов. В программном обеспечении был дефект, но этот дефект требовал целого набора специфических событий и факторов, для того чтобы пробудиться к жизни.
Эта ошибка требовала, чтобы нагрузка на сеть была продолжительной. Когда нагрузка на сеть уменьшалась, действие дефекта, по существу, исчезало43.
Эта ошибка зависела от временных параметров. Чтобы ошибка активировалась, было необходимо, чтобы были получены два IAM сообщения от одного и того же коммутатора с интервалом менее 10 миллисекунд.
Тот факт, что на всех коммутаторах было установлено одинаковое программное обеспечение, делал систему расширяемой. Однако был риск в том, что, если на всех коммутаторах был один и тот же дефект, они все оказывались чувствительными к одной и той же ошибке.
История: авария 1998 года
13 апреля 1998 года в 2:30 после полудня к коммутатору системы ретрансляции кадров Cisco Stratacom BPX был направлен техник для обновления транк-карты (trunk-card)44. Коммутатор Stratacom BPX содержал две транк-карты, одна из которых была активной, тогда как другая находилась в ждущем режиме и выполняла функцию резерва. Фирма AT&T использовала две процедуры обновления транк-карт. Одна процедура использовалась, если коммутатор был в текущее время подключен к сети и активен, тогда как другая процедура применялась, если коммутатор был изолирован, то есть не соединен с сетью.
Согласно первому сценарию, то есть когда коммутатор считался активным, процедура требовала, чтобы техник заменил сначала карту, находящуюся в ждущем режиме. Как только становилось ясно, что состояние новой карты стабильно, старая активная карта переводилась в ждущий режим, а новая карта становилась активной. Проведя эту операцию, техник мог заменить оставшуюся карту (теперь находящуюся в ждущем режиме). При второй процедуре предполагалось, что коммутатор отключен от сети и техник мог менять обе карты одновременно.
Когда техник прибыл на место, он посчитал, что коммутатор, которому требуется обновление, не подключен к сети, поскольку казалось, что через него не проходил никакой сетевой трафик. Однако коммутатор был подключен к сети и активен. К несчастью для техника и для AT&T, обе карты имели дефекты. Как только карты были установлены и активированы, они немедленно выслали коммутатору поток сообщений об ошибках. Эти сообщения от транк-карт активировали ошибку в программном модуле коммутатора. Этот дефект вызвал распространение передачи сообщений об ошибках к другим коммутаторам сети, ко всем 145. Объем этих посланий был достаточно велик, для того чтобы быстро перегрузить все коммутаторы, что очень действенно вывело из строя всю систему приблизительно к 3:00 пополудни45.
Информации об ошибках в программном обеспечении транк-карт и коммутатора Cisco немного. Элка Ярвис (Alka Jarvis), менеджер по программному обеспечению Cisco Systems, 28 мая 1998 года на заседании сессии Международной недели качества программного обеспечения (International Software Quality Week) прокомментировал, что код, который вызвал аварию в сети AT&T, являлся наследством прошлого46.
Компания AT&T смогла быстро изолировать аварийный коммутатор, к 23:00 он был отключен от сети. Оставшаяся задача состояла в том, чтобы просто перестроить всю сеть, одну часть за другой. К 2:00 пополудни 14 апреля 1998 года 99,9 % сети ретрансляции кадров были снова работоспособны. Однако определение причины аварии заняло у AT&T около недели, и 22 апреля 1998 года фирма выпустила отчет, очерчивающий причину выхода сети из строя.
Вывод: авария 1998 года
Хотя в аварии 1998 года и участвовало программное обеспечение, существует множество причин, внесших свой вклад в данное происшествие. Эта авария отличалась от аварии 1990 года тем, что процедурная ошибка в ходе обновления запустила скрытые программные дефекты. Однако сходств очень много.
- Установка нового программного обеспечения запустила ошибку. Программы, и старые и новые, имели многочисленные скрытые дефекты, которые не были обнаружены в ходе обычных тестовых процедур. Наличие скрытых дефектов изменило функциональную среду и запустило дефект, вызвавший аварию.
- Ошибочный код не был проверен должным образом. При аварии 1990 года это был новый код от AT&T. При аварии 1998 года - старый код от Cisco Systems.
- Программные дефекты в обоих случаях представляли собой проблемы со скрытыми граничными условиями, которые было трудно протестировать и которые, по всей вероятности, так и не были протестированы.
Авария 1998 года в сети AT&T выявила многочисленные просчеты в процедурах и процессах обслуживания сети и продемонстрировала трудности в разработке и поддержании в рабочем состоянии надежной сети. Очевидно, AT&T извлекла урок из своих ошибок и исправила многочисленные процедурные и функциональные недостатки и ввела многочисленные планы для восстановления после аварий, чтобы минимизировать риск другой общесистемной аварии47.
Переполнение буфера
18 июля 2000 года через список рассылки BugTraq было опубликовано детальное описание уязвимости систем безопасности Microsoft Outlook и Outlook Express. BugTruq - это список рассылки (mailing list), посвященный обсуждению компьютерной безопасности. Указанная уязвимость, по существу, обеспечивается ошибкой, обычно называемой в программной индустрии переполнение буфера (buffer overflow). В наиболее простой форме переполнение буфера случается, когда программа старается поместить данные в область памяти, которая слишком мала для их хранения. Следующий отрывок кода представляет собой пример на языке С:
1. char Array[10];
2. strcpy (Array, "Это вызовет переполнение буфера");
Размер массива составляет 10 символов, а сообщение: «Это вызовет переполнение буфера» состоит из 31 символа. Функция strcpy() пытается скопировать 31 символ в область, размеры которой позволяют хранить только 10 символов, и, таким образом, часть сообщения пишется за пределами разрешенной области. Когда такое случается, программа иногда претерпевает сбой. В другой раз программа может продолжать работу в течение некоторого времени без побочных эффектов. Но если сообщение составлено подходящим образом, это может привести к выполнению встроенного в сообщение «вируса», что вызовет заражение компьютера этим вирусом.
До настоящего момента наибольшую заботу о безопасности компьютера вызывали многообразные вирусы, которые требовали от пользователей предпринять некоторые действия для осуществления заражения. К таким вирусам относятся такие, как «ILOVEYOU» в 1999 году и «Melissa» в 1998 году. В обоих случаях для активации вируса пользователя хитростью заставляли запустить программу или открыть файл. Однако ошибка переполнения буфера в Outlook не требует вмешательства конечного пользователя. Для того чтобы вы подверглись атаке, достаточно просто получить электронное письмо, и это делает атаку особенно трудной для отражения.
В отличие от прочих случаев, описанных в этой главе, эта ошибка не ограничивается программой Microsoft Outlook. Как ошибка, переполнение буфера существует последние 10 лет, и это хорошо известное уязвимое место систем безопасности. Вне пределов проблем безопасности сетей переполнение буфера также очень распространенная ошибка - вероятно, каждый создавал такой дефект в какой-то момент своей карьеры. Когда бы вы использовали переменную фиксированного размера, такую, как массив, вы рискуете создать ошибку переполнения буфера. Поскольку эта проблема так распространена, вместо того чтобы детально описывать ошибку в Microsoft Outlook, мы поговорим об ошибке переполнения буфера вообще - как эта ошибка создается, какого рода риск для безопасности она может представлять и что мы можем сделать, чтобы избежать ее.
История
Причину переполнения буфера понять легко. Оно происходит, когда мы пытаемся записать слишком много данных в фиксированную область памяти (буфер). Чтобы понять, как переполнение буфера может вызвать сбой программы или стать уязвимым местом в системе безопасности, мы должны проникнуть во внутреннюю логику работы компьютера.
Когда вы пишете программу на языке высокого уровня, таком, как С, компилятор переводит программу в машинный код. Машинный код, по существу, представляет собой последовательность инструкций низкого уровня и данных. Например, рассмотрим следующую основную программу:
1.main(){
2.printf("Hello World");
3.}
Знаменитая программа «Здравствуй, мир» создает 300 294 байта исполняемого кода при использовании компилятора 'С' GNU (GCC) в Windows NT, и это выглядит примерно так:
0x401000 55 89 e5 83 ec 10 83 3d 00 20 40 00 00 74 01 cc
0x401010 d9 7d fe 66 8b 45 fe 25 c0 f0 ff ff 66 89 45 fe
…
0x401040 48 65 6c 6c 6f 20 57 6f 72 6c 64 00 55 89 55 89
0x401050 e5 e8 94 01 00 00 68 40 10 40 00 e8 92 01 00 00
0x401060 83 c4 …
Что-то довольно непонятное, не так ли? Эти числа - это то, что компьютер использует, когда исполняет написанную вами программу. Числа в этом коде представлены в шестнадцатеричном виде. Первая колонка представляет собой адрес памяти, где находится программа, а оставшиеся колонки показывают содержимое этих адресов памяти. Каждая колонка увеличивает значение адреса на единицу. Таким образом, по адресу 401000 хранится значение 55, по адресу 401001 хранится значение 89 и так далее. Некоторые из этих чисел - команды, которые говорят компьютеру, что делать, другие представляют собой данные, используемые компьютером. В данном случае компьютер берет первое значение, 55, и интерпретирует его как команду выполнить некоторое действие. Затем он переходит к следующему значению - 89. Компьютер знает, что команда 89 требует параметр, который хранится в следующем адресе и имеет значение е5. Таким образом, компьютер выполняет команду 89 е5 и затем переходит к значению, идущему следом за е5. Так компьютер исполняет эти команды по одной за раз. В программе «Здравствуй, мир» строка «Hello, world» представляет собой данные. Если вы можете прочитать шестнадцатеричные значения и знаете коды ASCII, вы можете увидеть, что строка «Hello, world» расположена по адресу 0х401040.
0x401040
48(H)65(e)6c(l)6c(l)6f(o)20()57(W)6f(o)72(r)6c(l)64(d)00
Этот набор чисел выглядит совершенно неотличимым от другого набора чисел. Что же не дает компьютеру рассматривать эти числа, как последовательность команд? Ничего, кроме логики, реализованной в программе и которая является главной проблемой при ошибке переполнения буфера. Если вы совершаете ошибку, компьютер считает эти данные командами и исполняет их как таковые. У него нет способа узнать, что эти значения относятся к данным. Такие команды бессмысленны, и программа, наиболее вероятно, остановится. Таким образом, задача потенциального нарушителя - использовать эту ошибку, чтобы заставить компьютер исполнять те данные, которые он ему предоставит. Следовательно, нарушители, которые хотят атаковать ваш компьютер с помощью ваших программ, должны сделать две вещи:
- Вставить свой код в память.
- Заставить компьютер исполнять этот код.
Ошибка переполнения буфера дает хакеру возможность сделать и то и другое. Поскольку программа не проверяет размер буфера, когда пишет в него, нарушитель может поместить произвольное содержимое в область памяти, идущую за буфером, - и сражение наполовину выиграно. Вторая половина будет выиграна, когда потенциальный хакер определит, как можно заставить компьютер выполнить код, который он вставил в вашу программу48.
Вывод
Из всех случаев, изученных в данной главе, переполнение буфера, вероятно, самый простой для понимания и объяснения. Он все время рядом, но тем не менее он все еще так распространен. Научимся ли мы чему-нибудь, как индустрия? Почему мы продолжаем делать эти ошибки?
- Не помогают языки программирования. Ошибки переполнения буфера приобрели характер эпидемии в тех языках, в которых нет встроенных средств проверки границ, таких, как С и С++. Эти языки дают программистам возможность манипулировать компьютером произвольно. Эта сила доверяется программистам, и от них ждут, что они будут программировать корректно и безопасно. Ожидается также, что они будут проводить явные проверки границ, если это необходимо. С другой стороны, такие языки, как Java, проводят явные проверки границ, и вероятность того, что ошибка переполнения буфера может быть использована, очень низка. Однако язык Java ограничивает возможности программиста. Он отнимает у него возможность напрямую манипулировать пространством памяти машины. Нельзя сказать, что переполнение буфера невозможно в программе на Java. Это просто означает, что программист должен внести серьезные искажения, чтобы вызвать переполнение буфера на Java. Когда это случается, определить причину крайне тяжело. И поскольку большая часть виртуальной машины Java написана на С и С++, всегда существует опасность ошибки переполнения буфера в виртуальной машине Java.
- Программистам нельзя доверять. Программисты - люди, а люди делают ошибки. Я по невнимательности много раз за свою жизнь создавал ошибки переполнения буфера. Много раз ошибки возникали, поскольку я создавал макет программного модуля и проверка границ массивов - это последнее, о чем я думал. Но макет имеет обыкновение находить способ проникать в продукт. В другой раз ошибки возникали, потому что я делал допущения по поводу кода и процесса функционирования и считал, что переполнение буфера невозможно. Как это обычно бывает, как только я делаю какие-либо допущения, что-нибудь показывает мне мою неправоту. Часто ошибки появляются из-за моей лени. Написание кода проверки на переполнение буфера - задача в большинстве случаев легкая, но обработка условий обнаружения нарушений границ может оказаться трудным и надоедливым делом. Могу ли я просто прервать выполнение программы? Или я должен сообщить об ошибке вызываемой функции? Как эта функция будет обрабатывать эту ошибку? Прерываю ли я выполнение программы в вызываемой функции до бесконечности? Проще всего не иметь с этим дела и допустить, что такого не случится. Я займусь этим, если те, кто тестируют программы, докажут, что я не прав.
- Языковые конструкции делают это сложным. Проведение проверки границ в некоторых языках может оказаться трудным делом - на ум приходит использование функции sprintf(). Поскольку sprintf() принимает переменное число аргументов, при использовании этих аргументов от программиста требуется посчитать размеры буфера. Это может легко вызвать переполнение, поскольку вы не понимаете нюансов языка. В С и С++ строка «Hello World» требует 12 символов памяти, потому что язык определяет, что строка должна оканчиваться символом с кодом «0». Иными словами, код «0» добавляется в строке после символа «d». Если вы выделили буфер размером в 11 символов, считая, что он вместит в себя всю строку, то использование любых строковых функций, таких, как strcpy(), приведет к ошибке переполнения буфера, и это ударит по вам, когда вы меньше всего ожидаете.
Легко говорить о способах окончательного избавления от ошибок переполнения буфера. Например, прекратите использовать небезопасные языки, такие, как С и С++, и приучите себя писать программы с позиций «оборонительного программирования»49. Мы понимаем, что это легче сказать, чем сделать. Если мы примем то, что ошибка переполнения буфера неизбежна, и то, что нам необходимо использовать небезопасные языки программирования, тогда единственным решением будет поиск ошибки. Чем раньше мы обнаружим ошибку, тем меньше шанс, что она огорчит нас в будущем.
С точки зрения безопасности ошибка не беспокоит нас, если программа зависает, но беспокоит, если ее можно использовать. Теперь сделаем ударение на том как предотвратить ошибку переполнения буфера от использования злоумышленником. Есть многочисленные исследовательские проекты и коммерческие продукты, касающиеся этой темы, и мы считаем, что читателям нужно ознакомиться с этими документами для получения дальнейшей информации50.
Заключение
Ошибки, обсуждаемые в этой главе, за несколькими исключениями, существовали или существуют в настоящее время. Сейчас тысячи разработчиков программ устраняют ошибки, работая сверхурочно. Некоторые из этих ошибок забавны, тогда как другие - подлые или трагичные. Тем не менее это совсем не забавно, если вы - один из тех, кто работает по выходным, устраняя ошибки. Однако цена, которую платят разработчики программ, мизерна по сравнению с последствиями этих ошибок. В этой главе мы узнали, что цена ошибки может быть астрономической. Миллионы долларов, затраченные на оборудование и работу, исчезают без следа. Человеческая боль и страдания, даже смерть могут быть вызваны простой ошибкой в программе.
Мы узнали, что, несмотря на все тестирование, технологии и процедуры, ошибки неизбежны. И сбои, проявляющиеся в конечном продукте, как правило, являются результатом целой серии ошибок. Обычно не существует какой-то одной причины. Вместо этого есть целая коллекция причин, предотвращающих обнаружение и удаление ошибки до рокового финала. Например, простая ошибка программирования привела к аварии на AT&T в 1990 году. Однако, если бы был проведен тест по исполнению кода в ошибочной его части, дефект был бы выявлен раньше, и это спасло бы AT&T от аварии телефонной сети общенациональных масштабов. Является ли главной причиной ошибки программист, написавший неверный код, или отдел тестирования, который позволил дефекту проникнуть в код конечного продукта?
Мы узнали, что способность находить ошибки также важна, как способность предотвращать их. Чем раньше мы сможем найти ошибку, тем дешевле нам встанет ее исправить. Мы узнали, что программное обеспечение может функционировать в точности так, как указывает спецификация, и все же мы будем считать его дефектным, хотя оно работает именно так, как ему указали. Мы узнали, что мы должны понимать подсказки, которые дает нам система, и должны всегда задаваться вопросами, оценивать и понимать причины, стоящие за этими подсказками. Но что наиболее важно, мы узнали, что перед лицом проблем нам следует быть скромными. Неспособность признать, что ошибки возможны и вера без сомнений в наши создания может сделать нас слепыми перед лицом потенциальных аварий программ. Вот объяснение того, что гордость - один их смертных грехов.
Мы определили много причин тех ошибок, о которых рассказано в этой главе. Эти причины кратко перечислены и обобщены ниже.
- Недостаток понимания инструментов и конструкций языка:
- Случаи - авария 1990 года на AT&T, ошибка переполнения буфера.
- Описание - дефект возникает, когда программист неправильно понимает конструкции языка. При аварии на AT&T 1990 года ошибка заключалась в неверном расположении оператора break. Типичная ошибка переполнения буфера может быть вызвана непониманием средств манипуляции со строками в стандартных функциях С и С++.
- Повторное использование модулей без понимания:
- Случай - Ariane-5.
- Описание - унаследованный программный модуль был использован повторно, но наблюдался недостаток понимания требований этого модуля. Не было сделано попыток оценить правомерность использования.
- Изменения и обновления программ:
- Случаи - аварии на AT&T 1990 и 1998 годов.
- Описание - ошибка проявилась в ходе и после окончания обновления программ, которое было направлено на повышение производительности системы.
- Игнорирование неожиданных результатов:
- Случаи - Mars Climate Orbiter, Therac-25.
- Описание - в обоих случаях была масса предупреждений и подсказок из различных источников о том, что что-то не так. Предупреждения проявлялись как неожиданные результаты и поведение. Но эти предупреждения регулярно игнорировались, и должное расследование не было проведено. В случае Mars Climate Orbiter проблема состояла в несовпадении между рассчитанными моделями с борта космического корабля и с наземной станции. В случае с Therac-25 проблема была в постоянных неполадках и Therac-20, и Therac-25.
- Непонимание подсказок:
- Случаи - Mars Climate Orbiter, Therac-25, Компания Х, Компания Y.
- Описание - ключи, указывающие на потенциальные ошибки, не интерпретировались корректно, что в конечном счете привело ко всем описанным авариям. И в случае с Компанией Х, и в случае с Компанией Y подсказки прекрасно описывали причину симптомов, но эти подсказки первоначально не были приняты во внимание.
- Недостаток подходящих средств мониторинга:
- Случаи - Mars Climate Orbiter, Therac-25, Компания Х, Компания Y.
- Описание - хорошие средства мониторинга могут помочь обслуживающему персоналу обнаружить и решить проблему до того, как она приобретет серьезные последствия. Может быть, несправедливо относить в эту категорию Mars Climate Orbiter, поскольку ученые были ограничены технологиями и законами физики в том, что они могли наблюдать. Тот факт, что они не могли наблюдать действительную траекторию космического аппарата, ограничивал их возможности в определении правильности их модели. В случае Therac-25 сообщения об ошибках не имели достаточно описательного характера, чтобы встревожить операторов потенциальными проблемами в машине. Так же наблюдался явный недостаток обратной связи, позволяющей оператору точно знать, что делает машина. Проблемы в Компаниях Х и Y могли бы быть минимизированы, если бы обслуживающий персонал смог определить падение производительности базы данных до аварии.
- Неверная или бедная спецификация:
- Случай - Ariane 5.
- Описание - спецификация обработки ошибки определяла, чтобы ошибочное значение было помещено в память, используемую навигационным компьютером. Реализация программного обеспечения точно соответствовала спецификации. Навигационный компьютер использовал это значение для осуществления маневра с использованием двигателей, что вызвало катастрофу. В данном случае спецификация обработки ошибки была ошибочна и вызвала крушение.
- Отклонение от спецификации:
- Случай - Mars Climate Orbiter.
- Описание - спецификация для Mars Climate Orbiter была очень ясной: единицы измерения должны быть метрическими, а не английскими.
- Унаследованные ошибки:
- Случаи - Therac-25, авария на AT&T 1998 года.
- Описание - унаследованные программные модули содержали ошибки, которые не были обнаружены в их предыдущем воплощении.
- Отклонение от должной процедуры:
- Случаи - авария на AT&T 1998 года, компания Х.
- Описание - процедуры были разработаны для того, чтобы помешать нам сделать ошибку, а когда процедуре не следуют, а идут коротким путем, возникают нежелательные эффекты.
- Небезопасные средства:
- Случай - переполнение буфера.
- Описание - одной из главных причин ошибки переполнения буфера является «небезопасность» таких программных языков, как С и С++, в которых программа зависит от того, провел ли программист явную проверку границ массивов для предотвращения этих ошибок.
- Человеческий фактор:
- Случай - переполнение буфера.
- Описание - мы все знаем, как предотвращать ошибки, и все же мы продолжаем их делать.
- Эго и самоуверенность:
- Случаи - Therac-25, Mars Climate Orbiter.
- Описание - эго может встать на пути объективной оценки систем. Самоуверенность может заставить нас не подвергать сомнению наши допущения. Результатом такого отношения станет то, что мы не будем обращать внимания на имеющиеся ключи, которые указывают на потенциальные дефекты программ.
- Неполное тестирование:
- Случаи: Ariane 5, Mars Climate Orbiter, Pentium, авария на AT&T 1990 года.
- Описание - легко рассуждать, глядя в прошлое, но большая часть этих ошибок могла бы быть обнаружена при «правильном» тестировании. В случае Ariane 5 неполнота требований и недостаток возможностей по имитации функциональной среды привели к тому, что инженеры вынуждены были отказаться от теста, который обнаружил бы ошибку. Неясно, проводилось ли тестирование в случае с Mars Climate Orbiter, поскольку персонал в ходе космического полета все еще занимался устранением ошибок. В случаях ошибки в процессоре Pentium и аварии на AT&T 1990 года проведенные тесты не принимали во внимание особенности алгоритма, поэтому некоторая часть кода не была протестирована. В случае с ошибкой в Pentium тестирование на основе случайной выборки не было достаточным для оценки всех значений в справочной таблице. При аварии на AT&T 1990 года тест не охватывал сценарий восстановления коммутатора.
Задача
Вы тестировали набор Web-страниц на Web-сайте вашей компании. Вы нажали на ссылку на Web-страницу А, и все прошло великолепно. Вы продолжили тестирование других Web-страниц на сайте и случайно вернулись на Web-страницу А опять. Но в этот раз ее содержимое оказалось устаревшим. Вы недоуменно посмотрели на страницу и перезагрузили ее. Теперь она снова выглядела нормально. Не доверяя тому, что вы видите, вы опять перезагрузили страницу, и страница снова оказалась правильной. Полагая, что три - это число магическое, вы подумали про себя, что если страница и теперь окажется правильной, значит, то, что вы видели, - это просто случайность. Вы перезагрузили страницу еще раз, и оказалось, что ее содержимое снова является устаревшим. Вы боднули головой монитор. После 10 минут, проведенных в перезагрузке этой страницы снова и снова, вы определили, что она загружается правильно 34 раза и неправильно 12 раз. Что может быть причиной ошибки?
Вот некоторые подсказки, которые помогут вам поразмышлять о причине ошибки. Архитектурно есть пять Web-серверов, обрабатывающих запросы браузера. Каждый Web-сервер может связываться с одним из четырех серверов приложений. Сервер приложений извлекает данные из базы данных и реализует бизнес-правила. Для ускорения работы каждый сервер хранит данные в локальном кэше.
1 Национальный комитет no аэронавтике и исследованию космического пространства, НАСА (США). - Примеч. пер.
2 Gleick, James. Genius, The Life and Science of Richard Feynman. Vintage Books, New-York, New York, 1992.
3 Опустив уплотнительное кольцо в ледяную воду, Р. Фейнман продемонстрировал широкоизвестный факт, что при низких температурах жесткость резины увеличивается, что привело к ослаблению уплотнительных свойств прокладки, утечке топлива и в конце концов к взрыву. - Примеч. науч. ред.
4 Feynman, Richard P. The Pleasure of Finding Things Out. Perseus Books, Cambridge, Massachusetts, 1999.
5 Kajihara, J., Amamiya, G., and Saya, T. "Learning from Bugs", IEEE Software, p. 46-54, September 1993.
6 Leveson, Nancy, and Turner, Clark S. "An Investigation of the Therac-25 Accidents", IEEE Computer, Vol. 26, No. 7, p. 18-41, July 1993.
7 Там же.
8 Там же.
9 Там же.
10 Там же.
11 Там же.
12 Там же.
13 Там же.
14 Там же.
15 Nathan, Adam. "NATO Creates Computer Virus That Reveals Its Secrets", The Sunday Times, June 18, 2000. www.thetimes.co.uk/news/pages/sti/2000/06/18/stinwenws01024.html.
16 Nicely, Thomas R. Pentium FPU Bug, Memo available on acavax.lynchburg.edu.
17 Nicely, Thomas R. Letter to Intel, October 30, 1994.
18 Wolfe, Alexander. EE Times, Issue 822, p. 1, November 7, 1994.
19 Markoff, John. "Circuit Flaw Causes Pentium Chip to Miscalculate, Intel Admits," New York Times. November 24, 1994.
20 Pratt, V. R. "A natural scenario with high FDIV bug probability (was: In Intel's Defense...)", comp.arch newsgroup, December 3, 1994, 15:20:17 UTC.
21 IBM, Pentium Study, IBM Memo, December 12, 1994.
22 Sharangpani, H. P., and Barton, M. L. "Statistical Analysis of Floating Point Flaw in the Pentium Processor (1994)", Intel White Paper, November 30, 1994.
23 По первым буквам имен авторов Sweeney, Robertson и Tocher, которые независимо друг от друга опубликовали его в 1994 году. - Примеч. науч. ред.
24 Bryant, R. "Bit-level analysis of an SRT circuit", CMU technical report, CMU-CS-95-140, 1995.
Sharangpani, H. P., and Barton, M. L. "Statistical Analysis of Floating Point Flaw in the Pentium Processor (1994)", Intel White Paper, November 30, 1994.
25 Edelman, Alan. "The Mathematics of the Pentium Division Bug", 1995. Available from www-math.mit.edu/-edelman/.
Also appears in SIAM Review, March 1997.
26 Pratt, V. R. "A natural scenario with high FDIV bug probability (was: In Intel's Defense...)", comp.arch newsgroup, December 3, 1994, 15:20:17 UTC.
27 Edelman, Alan. "The Mathematics of the Pentium Division Bug", 1995. Available from www-math.mit.edu/-edelman/.
Also appears in SIAM Review, March 1997.
28 Lions, J. L., et al. "ARIANE 5 Flight 501 Failure", Report by the Inquiry Board. Paris, July 19, 1996. Available at www.csrin.esa.int/htdocs/tidc/Press/Press96/ariane5rep.html.
29 Lions, J. L., et al. "ARIANE 5 Flight 501 Failure", Report by the Inquiry Board. Paris, July 19, 1996. Available at www.csrin.esa.int/htdocs/tidc/Press/Press96/ariane5rep.html.
30 Wharton, J. "Super Bowl XXXIV Web-Filtered: Adult Porn?", Risk Digest, Vol 20, No. 77. January 26, 2000. catless.ncl.ac.uk/Risks.
31 Программа замещает звездочками части слов, которые интерпретирует как отдельные слова с оскорбительным смыслом - Прим. пер.
32 McWilliams, P. "BBC Censorship", rec.humor.funny, December 3, 1999.
www.netfunny.com/rhf/jokes/99/Dec/censorship.html.
33 Город, где располагается штаб-квартира корпорации Майкрософт - Прим. пер.
34 Cattarin, G. "Junk-Mail Filters", Risks Digest, Vol. 20, No. 89. catless.ncl.ac.uk/Risks.
35 Media Relations Office, Jet Propulsion Laboratory. "NASA's Mars Climate Orbiter Believed to Be Lost". NASA Press Release. September 23, 1999.
36 Mars Climate Orbiter Mishap Investigation Board, Phase 1 Report, November 10, 1999.
37 Isbell, Douglas; Hardin, Mary; and Underwood, Joan. "Mars Climate Orbiter Team Finds Likely Cause of Loss", NASA Press Release 99-113, September 30, 1999.
38 Mars Climate Orbiter Mishap Investigation Board, Phase 1 Report, November 10, 1999.
39 Cook, Michael. "A Self-Referential Risky Accident", Risks Digest, Vol. 20, No. 95.
40 Термин, означающий добавление к части кода специальных символов комментария, чтобы компилятор рассматривал эту часть не как код программы, а как комментарий программиста. - Прим. пер.
41 Neumann, Peter. "Risks to the Public in Computers and Related Systems", ACM SIGSOFT Software Engineering Notes, Vol. 15, No. 2, p. 11ff, April 1990.
42 Neumann, Peter. "Risks to the Public in Computers and Related Systems", ACM SIGSOFT Software Engineering Notes, Vol. 15, No. 2, p. 11ff, April 1990.
43 Neumann, Peter G. Computer Related Risks. Addison-Wesley, New York, New York, 1995.
44 Trunk - устройство или канал, соединяющий две точки, каждая из которых является коммутационным центром или точкой распределения; обычно транк работает с несколькими каналами одновременно. - Примеч. пер.
45 "AT&T Announces Cause of Frame-Relay Network Outage", AT&T Press Release, April 22, 1998. McCartney, Laton. "One Year Later: A Behind-the-Scenes Look at the Causes of and Fallout from AT&T's Devastating Frame-Relay Outage", Network World, March 22, 1999.
46 Bernstein, L., and Yuhas, C. M. "Chinks in the Armor: Will a Hybrid IP/ATM Architecture Protect the Network from Node Failures?" America's Network Magazine, July 1, 1998.
47 Bernstein, L., and Yuhas, C. M. "Chinks in the Armor: Will a Hybrid IP/ATM Architecture Protect the Network from Node Failures?" America's Network Magazine, July 1, 1998
48 "Aleph One", Smashing the Stack for Fun and Profit, Phrack, 7(49), November 1996.
49 Термин "оборонительное программирование" представляет подход, при котором программист при написании кода сразу же принимает все меры, направленные на обнаружение и исправление всех ошибок, которые могут возникнуть при выполнении этого кода. - Примеч. науч. ред.
50 Cowan, C., et al. "Buffer Overflows: Attacks and Defenses for the Vulnerability of the Decade", Proceedings of DARPA Information Survivability Conference and Expo. Available from www.wirex.com/~Crispin.
|
|