Мастерство встроенного кодирования: проверенные привыки для уменьшения ошибок и повышения безопасности
За прошедшее десятилетие встроенное ПО выросло от нескольких сотен тысяч строк до более чем 100 миллионов в типичном автомобиле. Переход на 32-битные процессоры и обильная память открыли беспрецедентную функциональность — но также усилили риск тонких ошибок, угрожающих безопасности и защите.
Чтобы опередить проблемы, разработчикам необходимо принять систематический подход, предотвращающий ошибки до их попадания в кодовую базу. Две основные категории ошибок заслуживают особого внимания:
- Ошибки кодирования — например, выход за границы массива. Их можно обнаружить статическим анализом.
- Ошибки приложения — выявляются только при проверке поведения ПО на соответствие требованиям.
Внедрение строгого стандарта кодирования, такого как MISRA C/C++, в сочетании с тестированием, основанным на требованиях, дает мощную «унцию профилактики». Эта стратегия сокращает время отладки, сжимает графики и в конечном итоге защищает ROI.
Почему профилактика важна
Человеческие ошибки неизбежны — инженеры спешат, упускают детали или неверно интерпретируют сложные потоки данных. Но когда ПО растет в размере и взаимосвязности, эти простые просчеты могут каскадно перерастать в критические сбои. Дисциплинированный стандарт кодирования помогает обнаружить и устранить многие из этих подводных камней до их проявления.
В экосистеме C/C++ примерно 80% дефектов исходят от неправильного использования всего 20% языка. Запрещая или ограничивая проблемные конструкции, стандарт резко повышает качество кода.
Рассмотрим неопределенное поведение при сдвиге знакового целого вправо. В зависимости от компилятора результат может отличаться, приводя к неконсистентному поведению программы. Аналогично, функция чтения циклического буфера может возвращать значения в неожиданном порядке на некоторых компиляторах. Эти нюансы показаны на рисунках ниже.

Рисунок 1. Зависимое от компилятора поведение в конструкциях C/C++. Источник: LDRA

Рисунок 2. Неопределенное поведение в примере с циклическим буфером. Источник: LDRA
Использование MISRA C/C++
MISRA, впервые опубликованный в 1998 году для автомобильной промышленности, остается золотым стандартом кодирования встроенных систем на C/C++. Он определяет:
- Причину каждого правила с подробными исключениями.
- Четкие примеры неопределенного, неуточненного и определяемого реализацией поведения.
- Руководство по тому, какие правила «Решаемые» (проверяемые инструментами) и какие «Нерешаемые».
В 2016 году MISRA расширился для адресации кода, критичного для безопасности, добавив 14 новых директив. Например, Директива 4.14 явно запрещает конструкции, ведущие к неопределенному поведению, тем самым укрепляя позицию безопасности.

Рисунок 3. Руководство MISRA по неопределенному и неуточненному поведению. Источник: LDRA

Рисунок 4. Роль Директивы 4.14 в предотвращении неопределенного поведения. Источник: LDRA
От кода к приложению: роль требований и тестирования
Даже безупречный код может провалиться, если не соответствует своему назначению. Прослеживаемость требований и модульное тестирование необходимы для проверки того, что ПО ведет себя как задумано.
Двунаправленная прослеживаемость связывает каждую функцию с её низкоуровневым требованием и системными целями. Это отображение рано выявляет недостающие или лишние функции.

Рисунок 5. Двунаправленная прослеживаемость от функции к системному требованию. Источник: LDRA
После определения низкоуровневых требований тестовые случаи естественным образом возникают. Их запуск — идеально с автоматизированными инструментами модульного тестирования — подтверждает, что код выполняет свой контракт.

Рисунок 9. Результаты автоматизированного модульного тестирования. Источник: LDRA
Принятие новых привык кодирования
Современная встроенная разработка требует комплексного подхода:
- Внедрить проверенный стандарт кодирования (MISRA, CERT и др.).
- Отслеживать метрики — плотность дефектов, покрытие кода, уровень соответствия.
- Обеспечить прослеживаемость требований и автоматизированное регрессионное тестирование.
Эти практики дают более чистый, поддерживаемый код, упрощают добавление функций и снижают долгосрочные затраты на обслуживание — выгоды, актуальные как для критичных по безопасности, так и для некритичных доменов.
Принятие этих привык не опционально, если вы целитесь на конкурентное преимущество. Разница между кодовой базой, требующей постоянного тушения пожаров, и той, что эволюционирует плавно, часто заключается просто в дисциплинированном процессе.
Оригинальная статья из EDN.
Встроенный
- Текстовые строки: скрытая уязвимость встроенного ПО
- SOAFEE: облачная-родная архитектура, уполномачивающая программно-определяемые автомобильные платформы
- Pixus представляет rugged 6U OpenVPX фейсплейты с усиленной структурной целостностью
- Kontron представляет COM HPC: новый стандарт высокопроизводительных встроенных вычислений
- GE Digital представляет ПО для предиктивного управления активами, переводящее обслуживание из реактивного в проа…
- Мастерство обучения CMMS: вовлечение команд и повышение удержания
- Защита открытого ПО в IoT: смягчение программных рисков
- Три проверенных шага к защите вашей программной цепи поставок
- Saelig представляет Impact-D 100AL: мощный, компактный встраиваемый ПК с 15-летним жизненным циклом и богатым I/O
- Использование DevOps для преодоления сложностей встроенного ПО