Возможно, вы слышали о случае непреднамеренного ускорения Toyota, но, возможно, вы пропустили анализ программного обеспечения, которое подключало педаль газа к дроссельной заслонке. Проще говоря, этого достаточно, чтобы заставить вас купить предкомпьютерный автомобиль.
Еще в 2009 году поступали сообщения о том, что Toyota Camry и Corollas внезапно разгонялись до 90 миль в час без того, чтобы водитель поворачивал педаль газа, и что торможение не помогло. В ходе инцидентов погибло несколько человек, а один водитель был отправлен в тюрьму за убийство трех человек, несмотря на то, что его автомобиль без всякой причины ускорился.
Первое, на что следует обратить внимание, это то, что если вы выросли в эпоху, когда педаль акселератора была связана с дроссельной заслонкой проводом, то вам нужно подумать еще раз. В современных автомобилях в основном используется технология «drive-by-wire», а связь между акселератором и дроссельной заслонкой осуществляется через электронику, управляемую программным обеспечением.
Toyota отозвала автомобили и устранила проблему с ковриком, который, по ее утверждениям, мог засорить педаль газа. Позже программное обеспечение автомобиля было обновлено в рамках очередного отзыва. Первое расследование, проведенное НАСА, не выявило проблем с программным обеспечением, но они не смогли исключить ошибки, которые могли вызвать проблему. Toyota восприняла это как оправдание, и Национальная администрация безопасности дорожного движения закрыла дело.
Однако водитель, получивший травму из-за UA (непреднамеренное ускорение), подал в суд на Toyota в Оклахоме и выиграл 1,5 миллиона долларов после того, как эксперты по встроенному программному обеспечению из Barr Group представили доказательства того, что программное обеспечение было очень плохо спроектировано. Это было еще в октябре 2013 года, но об этом только что сообщили в технической прессе: «Переполнение стека вызывает неожиданное ускорение».
Однако правда еще хуже, чем можно предположить по этой простой причине.
Слайды, использованные в судебной презентации, рассказывают пугающую историю, но такую, которую могло ожидать большинство программистов.
Проверка программного обеспечения, управляющего дроссельной заслонкой, показала, что оно было очень низкого качества. Было использовано более 11 000 глобальных переменных; большинство функций были очень длинными и сложными; а цикломатическая сложность кода была намного больше 50. Фактически функция угла дроссельной заслонки набрала более 100 баллов, что помещает ее в класс неподдерживаемых.
Не было обнаружено ни одного изъяна, который можно было бы окончательно возложить на UA, но было много, которые могли вызвать проблему. В частности, способ использования стека мог привести к переполнению, которое уничтожило важные данные ОС.
Мало того, что использование стека достигало 94% при нормальной работе, код был рекурсивным! Рекурсивного кода обычно избегают во встроенных приложениях, потому что труднее продемонстрировать, что у него есть хорошие шансы на надежную работу. MISRA — Ассоциация надежности программного обеспечения автомобильной промышленности — имеет правило, которое явно запрещает рекурсию. Toyota заявила, что следовала стандартам MISRA, но было обнаружено более 80 000 нарушений.
Не менее тревожным было то, что ничего не было сделано для борьбы с переполнением стека. Область памяти, в которую вырос стек, не была защищена, и мониторинг стека отсутствовал.
Еще более шокирующая ситуация — это то, как операционная система справлялась с задачами и как они были организованы. Оказалось, что задачи могут быть прекращены ОС по ряду причин, и в большинстве случаев смерть задачи не будет замечена остальной частью системы. Обычный способ решения такого рода проблем — использовать сторожевой таймер. Жизненно важные задачи сбрасывают таймер как часть своего обычного выполнения. В случае сбоя сторожевой таймер не сбрасывается и перезапускает всю систему.
Вы можете подумать, что перезапуск так же опасен, как и UA, но сброс происходит за несколько миллисекунд, и самое большее, что заметил бы драйвер, — это кратковременная потеря скорости вращения.
К сожалению, сторожевой таймер не контролировал большинство задач ОС, и, следовательно, система могла продолжать работать. Если, например, задача акселератора была прервана, задача управления дроссельной заслонкой продолжала бы работать и либо придерживалась последней настройки, либо увеличивалась из-за поврежденных общих переменных.
Проблема усугублялась тем, что второй процессор, отслеживающий систему, мог перезагрузить систему, если бы заметил, что дроссельная заслонка была открыта при нажатии на тормоз. Однако, если задача перестала работать при включенном тормозе, система не реагировала на необычное состояние до тех пор, пока тормоз не был полностью отпущен и повторно задействован.
Итак, вы находитесь в машине, которая внезапно обрела собственный разум и набирает скорость. Ваша первая реакция — снять ногу с тормоза?
Есть много других, более второстепенных, но не менее важных моментов, касающихся безопасности программного обеспечения, но, возможно, последним из них было использование одной задачи для реализации отказоустойчивых режимов и сбора диагностических кодов. Если бы это задание было прервано, не было бы никаких записей о том, что произошло.
Настоящая неудача — это отсутствие понимания того, как создавать критически важное для безопасности программное обеспечение. Я очень удивлен, что в такой системе вообще используется многозадачная операционная система. В большинстве случаев критическое программное обеспечение следует создавать без использования асинхронного программирования, событий или рекурсии. Поток управления должен быть детерминированным, и должны быть сторожевые таймеры, чтобы гарантировать, что он работает должным образом в любое время. Если это не работает, перезагрузка почти всегда является подходящим решением.
Этой истории достаточно, чтобы заставить вас думать, что старый добрый проводной кабель имеет много преимуществ.