Что такое MISRA и как её готовить

    Рисунок 1


    Пожалуй, каждый разработчик программ для микроконтроллеров наверняка хотя бы раз слышал про специальные стандарты кодирования, призванные помочь повысить безопасность и переносимость вашего кода. Одним из таких стандартов является MISRA. В этой статье мы рассмотрим подробнее, что же представляет собой этот стандарт, какова его философия и как использовать его в ваших проектах.

    Многие наши читатели слышали о том, что PVS-Studio поддерживает классификацию своих предупреждений согласно стандарту MISRA. На данный момент PVS-Studio покрывает более 100 правил MISRA C: 2012 и MISRA С++: 2008.

    Данная статья имеет цель убить сразу трёх зайцев:

    1. Рассказать, что такое MISRA для тех, кто ещё не знаком с данным стандартом;
    2. Напомнить миру embedded-разработки о том, на что мы способны;
    3. Помочь глубже войти в курс дела новым сотрудникам, которые тоже будут в дальнейшем разрабатывать наш MISRA-анализатор;

    Надеюсь, у меня получится сделать это интересно. Итак, приступим!

    История появления MISRA


    История MISRA началась достаточно давно. Тогда, в начале 90-x, правительственная программа Великобритании под показательным названием «Safe IT» выделяла финансирование для различных проектов, так или иначе связанных с безопасностью электронных систем. Сам проект MISRA (Motor Industry Software Reliability Association) был основан ради создания руководства по разработке ПО для микроконтроллеров в наземных транспортных средствах – в машинах, в общем.

    Получив финансирование от государства, коллектив MISRA взялся за работу, и уже к ноябрю 1994 года выпустил своё первое руководство: "Development guidelines for vehicle based software". Это руководство еще не было привязано к конкретному языку, но должен признать: работа была проделана внушительная и касалась она, наверное, всех мыслимых аспектов разработки встраиваемого ПО. Кстати, недавно разработчики этого руководства отпраздновали 25-летие столь важной для них даты.

    Когда финансирование от государства закончилось, члены MISRA решили продолжить работать вместе на неформальной основе – так продолжается и по сей день. По сути, MISRA (как организация) – это содружество заинтересованных сторон из различных авто- и авиастроительных индустрий. Сейчас этими сторонами являются:

    • Bentley Motor Cars
    • Ford Motor Company
    • Jaguar Land Rover
    • Delphi Diesel Systems
    • HORIBA MIRA
    • Protean Electric
    • Visteon Engineering Services
    • The University of Leeds
    • Ricardo UK
    • ZF TRW

    Весьма сильные игроки рынка, не правда ли? Не удивительно, что их первый связанный с языком стандарт – MISRA C – стал общепринятым среди разработчиков критически важных встраиваемых систем. Чуть позже появился и MISRA C++. Постепенно версии стандартов обновлялись и дорабатывались, чтобы охватывать новые возможности языков. На момент написания этой статьи актуальными версиями являются MISRA C: 2012 и MISRA C++: 2008.

    Философия и примеры правил


    Самые отличительные черты MISRA – это невероятное внимание к деталям и крайняя дотошность в обеспечении безопасности. Авторы не просто собрали в одном месте все пришедшие в голову «дыры» C и C++ (как, например, авторы CERT) – они тщательно проработали международные стандарты этих языков и выписали все мыслимые и немыслимые способы ошибиться. А потом взяли и сверху дописали правил про читаемость кода – это чтобы в уже чистый код было сложнее внести новую ошибку.

    Чтобы понять масштаб серьезности, рассмотрим несколько правил, взятых из стандарта.

    С одной стороны, есть много хороших, годных правил, которым стоит следовать вообще всегда, независимо от того, для чего предназначен ваш проект. По большей части они призваны устранить неопределенное/неуточненное/зависимое_от_реализации поведение. Например:

    • Не используйте значение неинициализированной переменной
    • Не используйте указатель на FILE после закрытия потока
    • Все non-void функции должны возвращать значение
    • Счетчик цикла не должен иметь floating-point тип
    • … и т.д.

    С другой стороны, есть правила, польза которых лежит на поверхности, но которые (с точки зрения обыкновенных проектов) нарушить уже не так грешно:

    • Не используйте goto и longjmp
    • Каждый switch должен заканчиваться default
    • Не пишите недостижимый код
    • Не используйте вариативные функции
    • Не используйте адресную арифметику (кроме [] и ++)
    • ...

    Такие правила тоже неплохи, и в сочетании с предыдущими уже дают ощутимый прирост к безопасности, но достаточно ли этого для высокоответственных встраиваемых систем? Они используются не только в автомобильной промышленности, но и в авиастроительной, аэрокосмической, военной, в медицинской.

    Мы не хотим, чтобы из-за программной ошибки какой-нибудь рентгеновский аппарат облучал пациентов дозой в 20 000 рад, поэтому обычных «повседневных» правил уже недостаточно. На кону стоят человеческие жизни и огромные деньги, и необходимо включать дотошность. Здесь и выходят на сцену остальные правила MISRA:
    • Суффикс 'L' в литерале всегда должен быть заглавным (маленькую 'l' можно перепутать с единичкой)
    • Не используйте оператор «запятая» (она увеличивает шанс допустить ошибку)
    • Не используйте рекурсию (небольшой по размеру стек микроконтроллера может легко переполниться)
    • Тела операторов if, else, for, while, do, switch должны быть завернуты в фигурные скобки (потенциально можно допустить ошибку при неправильном выравнивании кода)
    • Не используйте динамическую память (потому что есть шанс неудачного выделения её из кучи, особенно в микроконтроллерах)
    • … и много, много таких правил.

    Зачастую у людей, которые сталкиваются с MISRA, складывается мнение, что философия стандарта заключается в «запретить вон то и запретить вот это». На самом деле, это так, но лишь отчасти.

    Да, стандарт действительно имеет много подобных правил, но его цель – не запретить всё возможное, а перечислить все-все-все способы как-то нарушить безопасность кода. Для большинства правил вы сами выбираете, стоит им следовать или нет. Объясню это дело поподробнее.

    В MISRA C правила делятся на три основных категории: Mandatory, Required и Advisory. Mandatory – это правила, которые нельзя нарушать ни под каким предлогом. Например, в этот раздел входит правило «не используйте значение неинициализированной переменной». Required-правила менее строги: они допускают возможность отклонения, но только если эти отклонения тщательно документируются и письменно обосновываются. Остальные правила входят в категорию Advisory – это правила, которым следовать не обязательно.

    В MISRA C++ немного по-другому: там отсутствует категория Mandatory, и большинство правил принадлежит к категории Required. Поэтому, по сути, вы имеете право нарушить любое правило – только не забывайте документировать отклонения. Также там есть категория Document – это обязательные к выполнению правила (отклонения не допускаются), которые связаны с общими практиками вроде «Каждое использование ассемблера должно быть задокументировано» или «подключаемая библиотека должна соответствовать MISRA C++».

    Что еще есть


    На самом деле, MISRA состоит не только из набора правил. По сути, это методичка по написанию безопасного кода для микроконтроллеров, и поэтому там полно всяких полезностей. Давайте же рассмотрим их детально.

    Во-первых, стандарт содержит достаточно тщательное описание подоплёки: ради чего создавался стандарт, почему был выбран именно C или C++, достоинства и недостатки этих языков.

    Про достоинства этих языков мы прекрасно знаем. Да и про недостатки, в общем-то, тоже :) Чего только стоят высокая сложность, неполная спецификация стандартом и синтаксис, позволяющий ну очень легко ошибиться, а потом долго искать ошибку. Например, можно случайно написать так:

    for (int i = 0; i < n; ++i);
    {
      do_something();
    }

    Всё-таки есть шанс, что человек не заметит лишней точки с запятой, верно? Еще можно написать вот так:
    void SpendTime(bool doWantToKillPeople)
    {
      if (doWantToKillPeople = true)
      {
        StartNuclearWar();
      }
      else
      {
        PlayComputerGames();
      }
    }

    Хорошо, что и первый, и второй случай легко отлавливаются правилами MISRA (первое — MISRA C: 13.4/MISRA C++: 6.2.1, второе — MISRA C: 13.4/MISRA C++: 6.2.1).

    Помимо описания проблематики, в стандарте содержится большое количество советов о том, что нужно знать перед началом работы: о том, как наладить процесс разработки по MISRA, об использовании статических анализаторов для проверки кода на соответствие, о том, какие документы нужно вести, как их заполнять, и так далее и тому подобное.

    Также в конце имеются приложения, в которых содержатся: краткий список и сводная таблица правил, небольшой перечень уязвимостей C/С++, пример документации отклонения от правила, а также несколько чек-листов, призванных помочь вам не запутаться во всей этой бюрократии.

    Как видите, MISRA – это не просто набор правил, а практически целая инфраструктура по написанию безопасного кода для встраиваемых систем.

    Использование в ваших проектах


    Представим ситуацию: вы собрались написать программу для какой-нибудь очень нужной и ответственной встраиваемой системы. Или у вас уже есть программа, но её нужно «перенести» на MISRA. Как же проверять ваш код на соответствие стандарту? Неужели придется делать это вручную?

    Ручная проверка кода – занятие непростое и потенциально невыполнимое. Мало того, что каждому ревьюверу пришлось бы тщательно высматривать каждую строчку кода, так еще и знать стандарт придется чуть ли не наизусть. Ужас!

    Поэтому сами разработчики MISRA для проверки вашего кода советуют использовать статический анализ. Ведь, по сути, статический анализ – это автоматизированный процесс code review. Вы просто запускаете анализатор на вашей программе и через несколько минут получаете отчет о потенциальных нарушениях стандарта. То, что нужно, ведь так? Вам останется лишь просмотреть лог и исправить срабатывания.

    Следующий вопрос: в какой момент начинать использовать MISRA? Ответ простой: чем раньше, тем лучше. В идеале – до того, как вы вообще начнёте писать код, потому что MISRA предполагает следование стандарту в течение всей жизни вашего кода.

    Конечно, не всегда имеется возможность писать по MISRA с самого начала. Например, часто бывает, что проект уже частично или полностью реализован, но заказчик пожелал, чтобы проект соответствовал стандарту. В таком случае вам придется заняться основательным рефакторингом уже имеющегося кода.

    Вот тут-то и всплывает подводный камень. Я бы даже сказал, всплывает подводный валун. Что будет, если вы возьмёте статический анализатор и проверите «обыкновенный» проект на соответствие стандарту MISRA? Спойлер: вы можете испугаться.

    Рисунок 5


    Конечно, пример на картинке преувеличен. Здесь показан результат проверки достаточного большого проекта, который на самом деле никогда и не задумывался для работы на микроконтроллерах. Тем не менее, при проверке уже имеющегося кода вы вполне можете увидеть одну, две, пять, а то и десять тысяч срабатываний анализатора. И во всей этой куче будут теряться новые срабатывания, которые были выданы на только что написанный или изменённый код.

    Что же с этим делать? Неужели придется отложить все дела и сесть за исправление всех старых срабатываний?

    Мы знаем, что при первой проверке проекта достаточно часто появляется много срабатываний, и разработали решение, которое поможет вам получать пользу от анализатора сразу, не останавливая работу. Называется это решение «suppress-базы».

    Suppress-базы – это механизм PVS-Studio, который позволяет массово подавить сообщения анализатора. Если вы впервые проверяете проект и обнаруживаете там несколько тысяч срабатываний – вы просто добавляете их в suppress-базу, и при следующем прогоне анализатор выдаст вам ноль предупреждений.

    Таким образом, вы сможете продолжить писать и изменять код в обычном режиме, и при этом будете видеть предупреждения только о тех ошибках, которые были внесены в проект только что. При этом вы будете получать максимальную пользу от анализатора сразу здесь и сейчас, не отвлекаясь на разгребание старых ошибок. Несколько кликов – и анализатор внедрен! Почитать подробную инструкцию об этом вы можете здесь.

    У вас наверняка может возникнуть вопрос: «Подождите, а что делать со спрятанными срабатываниями?» Ответ довольно простой: не забывать о них и потихонечку исправлять. Можно, например, заложить suppress-базу в систему контроля версий и допускать только те коммиты, которые не увеличивают количество срабатываний. Таким образом, постепенно ваш «подводный валун» рано или поздно сточится и от него не останется и следа.

    Окей, анализатор внедрен и теперь мы готовы продолжать. Что делать дальше? Понятное дело – работать с кодом. Но что нужно, чтобы можно было заявить о соответствии стандарту? Как доказать, что ваш проект соответствует MISRA?

    Дело в том, что не существует какого-то специального «аттестата» о том, что ваш код соответствует MISRA. Как оговаривает сам стандарт, отслеживание кода на соответствие должно выполняться двумя сторонами: заказчиком ПО и поставщиком ПО. Поставщик разрабатывает ПО, соответствующее стандарту, и заполняет необходимые документы. Заказчик же со своей стороны должен удостовериться, что данные из этих документов соответствуют действительности.

    Если же вы сами являетесь и заказчиком, и разработчиком собственного ПО, то ответственность за соответствие стандарту будет лежать только на ваших плечах :)

    В общем, для доказательства соответствия вашего проекта вам понадобятся документы-пруфы. Список документов, которые должны подготовить разработчики проекта, может варьироваться, но MISRA предлагает некоторый набор в качестве эталонного. Рассмотрим этот набор поподробнее.

    Для заявления о соответствии стандарту вам понадобится несколько вещей:

    • Собственно проект, код которого соответствует Mandatory и Required правилам
    • План обеспечения соответствия (guide enforcement plan)
    • Документация по всем предупреждениям компиляторов и статических анализаторов
    • Документация по всем отклонениям от Required-правил
    • Резюме о соответствии правилам (guideline compliance summary)

    Первый из них – план обеспечения соответствия. Это ваша самая главная таблица, и именно она будет отсылать проверяющего ко всем остальным документам. В первом её столбике находится список MISRA-правил, в остальных – отмечается, были ли выявлены какие-то отклонения от этих правил. Выглядит эта таблица примерно так:

    Рисунок 8

    Стандарт рекомендует собирать ваш проект несколькими компиляторами, а также использовать два и более статических анализатора для проверки вашего кода на соответствие. Если компилятор или анализатор выдает какое-то предупреждение, связанное с правилом – вы должны отметить это в таблице и задокументировать: почему срабатывание нельзя устранить, является ли оно ложным, и т.д.

    Если какое-то из правил нельзя проверить статическим анализатором, то вам необходимо провести процедуру code review. Эта процедура также должна быть задокументирована, после чего в план обеспечения соответствия должна быть добавлена ссылка на эту документацию.

    Если компилятор или статический анализатор оказываются правы, или если в процессе code review обнаружились действительные нарушения правила, то вы должны либо исправить их, либо задокументировать. Опять же, прикрепив ссылку на документацию в таблицу.

    Таким образом, план обеспечения соответствия – это документ, по которому можно будет найти документацию к любому отклонению, выявленному в вашем коде.

    Теперь немного про саму документацию отклонений от правил. Как я уже упомянул, такая документация необходима только для Required-правил, потому что правила уровня Mandatory нарушать нельзя, а Advisory-правилам можно не следовать и без всякой документации.

    Если вы решили отклониться от правила, то документация должна содержать:

    • Номер нарушенного правила
    • Точную локацию отклонения
    • Обоснованность отклонения
    • Доказательство того, что отклонение не нарушает безопасность
    • Потенциальные последствия для пользователя

    Как видите, такой подход к документации заставляет серьезно задуматься, стоит ли нарушение того. Это сделано специально, чтобы нарушать Required-правила было неповадно :)

    Теперь про резюме о соответствии правилам. Эту бумагу, пожалуй, будет заполнить легче всего:

    Рисунок 2

    Центральный столбик заполняется до того, как приступить к работе с кодом, а самый правый – после того, как ваш проект готов.

    Вполне резонно задать вопрос: зачем нужно указывать категории правил, если они уже указаны в самом стандарте? Дело в том, что стандарт допускает «повышение» правила в более строгую категорию. Например, заказчик может попросить вас перенести какое-нибудь Advisory-правило в категорию. Такое «повышение» должно быть сделано до работы с кодом, и резюме о соответствии правилам позволяет явно это отметить.

    С последним столбиком всё просто: достаточно только отметить, используется ли правило, и если да, то имеются ли от него отклонения.

    Вся эта таблица нужна для того, чтобы можно было быстро посмотреть, какие приоритеты имеют правила, и соответствует ли им код. Если вам вдруг станет интересно узнать точную причину отклонения, вы всегда можете обратиться к плану обеспечения соответствия и найти нужную вам документацию.

    Рисунок 3

    Итак, вы написали код, тщательно следуя правилам MISRA. Вы составили план обеспечения соответствия и задокументировали всё, что можно было задокументировать, а также заполнили резюме о соответствии правилам. Если это действительно так, то вы получили очень чистый, очень читаемый и очень надёжный код, который вы теперь ненавидите :)

    Где теперь будет жить ваша программа? В аппарате МРТ? В обыкновенном датчике скорости или в системе автопилота какого-нибудь космического спутника? Да, вы прошли через серьёзный бюрократический путь, но это – мелочи. Разве можно не быть дотошным, когда на кону стоят настоящие человеческие жизни?

    Если вы справились и смогли дойти до победного конца, то я искренне вас поздравляю: вы пишете качественный безопасный код. Спасибо!

    Будущее стандартов


    Напоследок хочется немного рассказать о будущем стандартов.

    На данный момент MISRA живёт и развивается. Например, в начале 2019 года был анонсирован «The MISRA C:2012 Third Edition (First Revision)» – обновленный и дополненный новыми правилами стандарт 2012 года. Тогда же объявили о грядущем выходе «MISRA C:2012 Amendment 2 – C11 Core» – стандарте 2012 года, в который будут добавлены правила, впервые охватывающие версии языка Си 2011 и 2018 годов.

    Не стоит на месте и MISRA C++. Как известно, последний стандарт MISRA C++ датируется 2008 годом, поэтому наиболее старшая версия языка, которую он охватывает – это C++03. Из-за этого появился еще один стандарт, аналогичный MISRA, и называется он AUTOSAR C++. Он изначально задумывался как продолжение MISRA С++ и имел своей целью охватить более поздние версии языка. В отличие от своего вдохновителя, AUTOSAR C++ обновляется два раза в год и на данный момент поддерживает C++14. Планируется дальнейшее обновление до C++17, а затем C++20, и так далее.

    К чему я начал про какой-то другой стандарт? Дело в том, что чуть меньше года назад обе организации объявили об объединении своих стандартов в один. Теперь MISRA C++ и AUTOSAR C++ станут единым стандартом, и теперь они будут развиваться вместе. Я думаю, это отличная новость для разработчиков, пишущих под микроконтроллеры на C++, и не менее отличная новость для разработчиков статических анализаторов. Любимой работы наперёд еще много! :)

    Заключение


    Сегодня мы с вами многое узнали про MISRA: почитали историю её возникновения, изучили примеры правил и философию стандарта, рассмотрели всё необходимое для использования MISRA в ваших проектах, и даже немного заглянули в будущее. Надеюсь, теперь вы лучше понимаете, что такое MISRA и как её готовить!

    По старой традиции оставлю здесь ссылку на наш статический анализатор PVS-Studio. Он способен находить не только отклонения от стандарта MISRA, но еще и огромнейший спектр ошибок и уязвимостей. Если вам интересно самостоятельно попробовать PVS-Studio – скачайте демонстрационную версию и проверьте свой проект.

    На этом моя статья подходит к концу. Желаю всем читателям счастливого Нового года и веселых новогодних выходных!

    Дополнительные ссылки


    1. Георгий Грибков – "Безопасность на максималках — как писать надежный C/C++ код для встраиваемых систем".
    2. PVS-Studio — статический анализатор кода.
    3. PVS-Studio & KEIL 5 совместная работа.



    Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: George Gribkov. What Is MISRA and how to Cook It.
    PVS-Studio
    Static Code Analysis for C, C++, C# and Java

    Комментарии 46

      +2
      Мы не хотим, чтобы из-за программной ошибки какой-нибудь рентгеновский аппарат облучал пациентов дозой в 20 000 рад, поэтому обычных «повседневных» правил уже недостаточно. На кону стоят человеческие жизни и огромные деньги, и необходимо включать дотошность. Здесь и выходят на сцену остальные правила MISRA:
      Можно ли соблюсти все правила MISRA и все равно облучить пациентов повышенной дозой?

        +2
        Можно. Но суть в том чтобы снизить вероятность такого события. А остальное, это уже к страховому агентству :).
        +1
        for (int i = 0; i < n; ++i);
        {
          do_something();
        }

        Ну прямо с субботней проверки лабораторных, только было что-то типа этого


        if (true && false);
        {
          //делать что-то если true
        }

        Студентка час искала, почему у неё внутрь скобок заходит..., потом позвала меня, я в начале тоже в шоке был :), но быстро догадался…
        Для этого сказал, чтобы перед точкой запятой пробел ставили всегда, тогда её видно из далека.


        if (true && false) ;
        {
          //делать что-то если true
        }

        Ну и конечно статический анализатор помогает, даже казалось бы в общем-то очень понятных вещах, иногда находит то, чего так просто и не поймешь.

          +4
          А крамольная идея открывающую фигурную скобку всегда располагать на первой строке (тогда неправильная точка с запятой будет бросаться в глаза) вам (со студенткой) никак не подходит?

          Кстати, у этой «концепции» есть и другие преимущества, например, более компактное представление кода, уменьшение длины простыней и больше кода одновременно на экране для (визуального) анализа.
            0

            А это по ~фэншую~ MISRA?

          0
          Спасибо. Ничего нового конечно. Те, кто проверял свой код на соответствие MISRA средствами того же IAR'а в курсе большинства правил. Не все, правда, вычищали код до соответствия. Да и не всегда это возможно. Особенно в среде любителей готовых библиотек (ASF, HAL, etc..)

          Однако, справедливости ради, -Wall -Wextra -Wpedantic покажут практически тоже. А вот эту троицу настоятельно рекомендую всем. Как минимум для своего кода (а лучше и для библиотек — чтоб потом сюрпризом не было).

          P.S.
          В одной из stm32'ых программ была забавная конструкция
          while(any_condition);
          {
          }


          Все съели. И только GCC с этой троицей ругнулся. Ошибок нет, но неаккуратность присутствует.
            +2
            Каждый switch должен заканчиваться default

            Вы привели замечательный пример устарелости MISRA.
            Намного лучше не писать default, а перечислять все варианты enum'а, чтобы получить ошибку, если кто-то добавит новый элемент (предупреждения ведь интерпретируются как ошибки, мы ведь надёжное ПО пишем, правда?)
            enum class E
            {
                Var1,
                Var2,
                Var3
            };
            
            int foo(E e)
            {
                switch(e)
                {
                    case E::Var1: return 1;
                    case E::Var2: return 2;
                }
                return -1;
            }
            

            Godbolt

            Приходилось мне переводить MISRA C++ для создания стандарта предприятия. Крайне двойственные ощущения: есть хорошие, действительно важные правила, которые следует использовать, а есть вот такая устарелая некомпетентность с default.
            В целом, конечно, прочитать нужно, но считать эти правила чем-то непогрешимым не следует.
            Правила CERT понравились куда больше.
              0
              Вы не поняли целевое назначение данного вида требования. Писать дефолтный обработчик нужно для контроля в рантайме. И вот это уже про надежное ПО.
                +1
                Так поясните мне, пожалуйста, с помощью понятного примера.
                  +1
                  1. enum не всегда подходит: иногда удобно делать switch по константам, связанным с содержимым аппаратных регистров.

                  2. Память контроллера, не поверите, может быть испорчена в рантайме. Или даже не память — некорректное значение может прийти через USART, например.

                  Вкратце: не всегда switch делается по членам enum'а.
                    +1
                    enum не всегда подходит: иногда удобно делать switch по константам, связанным с содержимым аппаратных регистров

                    Да вот только правило говорит нам, что следует всегда использовать default. Я и указываю на излишнюю догматичность и даже вредность этого правила для случая enum.
                    Память контроллера, не поверите, может быть испорчена в рантайме

                    Действительно не поверю. Если программа гадит не пойми куда, то речи о надёжном ПО быть не может. И уж точно от этого не спастись с помощью default.
                    Или даже не память — некорректное значение может прийти через USART, например

                    Конечно, всё, что пришло извне, должно подвергаться проверке. Только при чём тут default?
                      +3
                      Да вот только правило говорит нам, что следует всегда использовать default.


                      Лучше перебдеть.

                      Действительно не поверю. Если программа гадит не пойми куда ...


                      Хехе, сразу видно программиста не-железячника.

                      Программа тут не при чем. Может прилететь частица и изменить бит в памяти (нормальная ситуация для аппаратуры на спутниках), может случиться скачок/просадка напряжения и повредить участок FLASH/EEPROM (например, если BOD настроен неверно и запись в NVM прошла при нештатном напряжении) или область RAM, может быть повреждена линия к внешней микросхеме памяти, и еще куча вариантов.

                      Только при чём тут default?


                      Это самый простой способ отловить неожиданное значение.
                        –2
                        Лучше перебдеть

                        Покажите пример с «лучше». Пока что только я показал пример, почему лучше без default :)
                        Хехе, сразу видно программиста не-железячника

                        Это очевидно — в этой ветке я единственный, кто последовательно, логично и с примерами отстаивает свою позицию.
                        Программа тут не при чем. Может прилететь частица и изменить бит в памяти (нормальная ситуация для аппаратуры на спутниках), может случиться скачок/просадка напряжения и повредить участок FLASH/EEPROM (например, если BOD настроен неверно и запись в NVM прошла при нештатном напряжении) или область RAM, может быть повреждена линия к внешней микросхеме памяти, и еще куча вариантов.

                        Иными словами, вы назвали следующие смертные грехи: 1) неправильный выбор элементной базы для указанных в ТЗ условий эксплуатации, 2) нарушение технологии, 3) неправильная эксплуатация, повлекшая физическое повреждение, а так же 4) ещё какую-то кучу.
                        Вы правда хотите какой-то программой исправить вышеозначенные косяки аппаратуры, на которой эта же программа и исполняется? Если да, то я прошу вас назвать вуз, который выдал вам диплом, а так же организацию, в которой вы работаете.
                        Это самый простой способ отловить неожиданное значение

                        Вы таки продемонстрируете код?
                          +4
                          я единственный, кто последовательно, логично и с примерами отстаивает свою позицию.


                          Переход на аргументы ad hominem плохо сочетается с перечисленными добродетелями. :)

                          Вы правда хотите какой-то программой исправить вышеозначенные косяки аппаратуры, на которой эта же программа и исполняется?


                          В реальном мире никогда не будет идеальных условий эксплуатации — просто примите это. Причем чем специфичнее применение, тем условия, как правило, тяжелее. И программа, по-возможности, должна если и не спасать положение целиком, то хотя бы минимизировать финальный ущерб. Это достигается не только программными средствами, конечно; критичные функции всегда защищаются аппаратно, насколько это возможно.

                          Кстати, от выбивания битов в памяти не имунна полностью и «правильная» по вашему мнению элементная база. Да, в технологии с диэлектрическими подложками тиристорного эффекта не будет, но вероятность повреждения памяти заряженными частицами все равно есть. Предлагаете делать на лампах? Вот они да, 100% защищены от всех эффектов.

                          Сразу скажу, что свинцовая или какая-то другая защита не спасет. Попадание частицы с космическим уровнем энергии в материал вызывает лавину вторичных частиц, что усугубляет проблему. Как видите, есть много нюансов.

                          Жизнь сложна и многообразна; защищаться воплями «ЭТО НАРУШЕНИЕ ПРАВИЛ ЭКСПЛУАТАЦИИ ВЫ САМИ ВИНОВАТЫ!!!1111» — так себе вариант. Надо предполагать работу аппаратуры и в нештатных условиях. В том числе и не предусмотренных ТЗ, но вероятных, в том числе и в результате возможного раздолбайства.

                          Вы таки продемонстрируете код?


                          Эээ, вам написать switch, в котором в блоке default осуществляется вызов обработчика ошибки?
                            –1
                            В реальном мире...

                            Т.е. вы таки собираетесь с помощью default исправлять аппаратуру, на котором этот default исполняется?
                            Ну, в самом деле, назовите уже вуз и место работы — надо же знать куда нельзя отправлять учиться детей, и с чьей продукцией нельзя связываться.
                            Эээ, вам написать switch, в котором в блоке default осуществляется вызов обработчика ошибки?

                            И показать, почему это будет лучше, чем без default.
                              +3
                              По-моему, вы не читаете мои сообщения. Использование default — маленький кирпичик, который может повысить надежность.

                              Кстати, если вы так настаиваете, я тоже перейду на личности и спрошу — а вы сами далеко ушли от Arduino? Похоже нет.

                              И показать, почему это будет лучше, чем без default.


                              То, что наличие дополнительной обработки ошибок лучше, чем ее отсутствие, не очевидно?
                                –3
                                По-моему, вы не читаете мои сообщения

                                К сожалению, я их читаю. К сожалению, потому что ничего в них дельного нет.
                                Использование default — маленький кирпичик, который может повысить надежность

                                Очередное голословное утверждение без кода.
                                Кстати, если вы так настаиваете, я тогда тоже перейду на личности и с прошу — а вы сами далеко ушли от Arduino? Похоже нет.

                                Я не в курсе, что здесь личного. К Arduino я никогда не прикасался и не собираюсь.
                                То, что наличие дополнительной обработки ошибок лучше, чем ее отсутствие, не очевидно?

                                Вы не показываете, где есть эта самая дополнительная обработка.
                        0

                        До c++17 можно было написать что-то вроде
                        auto invalid = E(42);
                        И попасть в default ветку. И это не было UB!
                        Конечно, никто так не пишет, но память в МК — не идеал надёжности

                          +1
                          До c++17 можно было написать что-то вроде
                          auto invalid = E(42);
                          И попасть в default ветку. И это не было UB!

                          Это и сейчас можно. Поэтому необходимо в правилах предупредить именно эту ошибку, а не залеплять потом дыры с помощью default. Например, как в CERT.
                          Но и в CERT не идеально. На современных плюсах я бы предпочёл что-то вроде
                          enum class variants
                          {
                              var1,
                              var2,
                              var3,
                              ...
                              varN
                          };
                          
                          std::optional<variants> from_int(int value)
                          {
                              const auto e{static_cast<variants>(value)};
                              switch(e)
                              {
                                  case variants::var1: [[fallthrough]];
                                  case variants::var2: [[fallthrough]];
                                  case variants::var3: [[fallthrough]];
                                  ...
                                  case variants::varN:
                                      return e;
                              }
                              return {};
                          }

                          память в МК — не идеал надёжности

                          И вы считаете, что такие МК можно использовать в критических системах, для которых и создана MISRA?
                            +2

                            Назовите хоть один МК, который полностью защищён от повреждений памяти.

                              0
                              Понятия не имею есть ли такие. Какое это имеет значение? Вы тоже собираетесь исправлять аппаратные проблемы программой, которая на этой же аппаратуре исполняется?
                                +1
                                Вы тоже собираетесь исправлять аппаратные проблемы программой, которая на этой же аппаратуре исполняется?


                                Ну, вообще говоря отчасти это так и делается. Например если нет аппаратного скраббера, то делается программный, кроме того многие ошибки в аппаратуре только выставляются, но обрабатываются софтом.

                                Как верно выше заметили — безсбойной аппаратуры не бывает, но число и опасность сбоев можно сильно понизить за счет ряда решений, в основном аппаратных, но софт — тоже всегда часть этого решения.

                                Но вообще я другое хотел спросить. Вот вы выше привели пример, как бы вы использовали switch, в нем нет дефолта, да, но простите — в чем было бы отличие, если бы все-таки был default, в котором как раз было бы return {}?
                                  0
                                  Ну, вообще говоря отчасти это так и делается. Например если нет аппаратного скраббера, то делается программный

                                  Вы не видите разницы между программной эмуляцией и исправлением непредсказуемых косяков железа с помощью программы, на этом же железе исполняемой?
                                  кроме того многие ошибки в аппаратуре только выставляются, но обрабатываются софтом

                                  Так выставляемые ошибки предсказуемы! И потому, внезапно, являются частью гарантий/контракта этой аппаратуры. А вы тут пропагандируете новое слово в надёжности, когда исправляются непредсказуемые ошибки, нарушающие гарантии.
                                  Как верно выше заметили — безсбойной аппаратуры не бывает, но число и опасность сбоев можно сильно понизить за счет ряда решений, в основном аппаратных, но софт — тоже всегда часть этого решения.

                                  Я уже устал отвечать на подобные размышлизмы. Знаю одно — я не хотел бы быть вашим коллегой.
                                  Но вообще я другое хотел спросить. Вот вы выше привели пример, как бы вы использовали switch, в нем нет дефолта, да, но простите — в чем было бы отличие, если бы все-таки был default, в котором как раз было бы return {}?

                                  А вы пробовали не спрашивать, а прочитать ветку, в которую постите? Вот прямо мой первый комментарий, там ясно написано:
                                  чтобы получить ошибку, если кто-то добавит новый элемент
                                    0
                                    Вы не видите разницы между программной эмуляцией и исправлением непредсказуемых косяков железа с помощью программы, на этом же железе исполняемой?


                                    Гм, вот что я точно не вижу, так это суть выражаемой в данном предложении мысли. Можете развернуть ее более понятно?

                                    А вы тут пропагандируете новое слово в надёжности, когда исправляются непредсказуемые ошибки, нарушающие гарантии


                                    Щито? Во-первых я тут пока ничего не пропагандировал, а во-вторых я похоже опять должен вас спросить — что вы имеете в виду, какие такие непредсказуемые ошибки?

                                    Я уже устал отвечать на подобные размышлизмы. Знаю одно — я не хотел бы быть вашим коллегой.


                                    Ну, при таком уровне аргументации я вас на работу и не возьму никогда.

                                    А вы пробовали не спрашивать, а прочитать ветку, в которую постите? Вот прямо мой первый комментарий, там ясно написано:


                                    Это я прочитал, но это касается только компилтайма. А вы там дальше уже про корректный враппер интов для рантайма пишете, и именно про него я спросил. То, что он также наследует желаемое вами поведение в компилтайме, я никак не оспариваю. Впрочем, поскольку обработка в рантайме в конечном итоге важнее всего, то в общем вариант с default все так же достаточен для решения основной задачи.
                                      0
                                      Впрочем, поскольку обработка в рантайме в конечном итоге важнее всего, то в общем вариант с default все так же достаточен для решения основной задачи

                                      Вариант без default позволяет получить при компиляции ошибку и тем самым предупредить некорректное поведение во время исполнения.
                                      В то же время, и вариант без default, и вариант с default одинаково ведут себя на всех входных данных (хотите оспорить это утверждение примером входного int с разным поведением двух вариантов?).
                                      Внимание, вопрос: так почему же нужно выбрать вариант с default?
                                        0
                                        В то же время, и вариант без default, и вариант с default одинаково ведут себя на всех входных данных


                                        Fixed.
                                          0
                                          image
                                            0
                                            Я задавал конкретный вопрос и ожидал конкретный ответ. Вы в конечном итоге его дали, дальнейшей дискуссии не предполагалось.
                                  0
                                  ваша ошибка в том, что считаете что сертифицируется ПО, а на самом деле сертифицируется процесс создания ПО.
                                  Вы должны гарантировать безошибочность написание текста исходного кода за все время. Мало того, вы так же должны доказать правильность работы компилятора, например, у нас имеется правило запрета использования прописной буквы я даже в комментариях, вы знаете откуда это идёт, думаю что нет. Вы можете написать правильно работающую программу, но вот использовать вам ее не разрешат, так как вы не сможете доказать ее правильность.
                                    0
                                    ваша ошибка в том, что считаете что сертифицируется ПО, а на самом деле сертифицируется процесс создания ПО

                                    Серьёзно? Я где-то говорил про сертификацию?
                                    И да, ПО всё же верифицируется.
                                    вы так же должны доказать правильность работы компилятора

                                    Нет, такого доказывать не надо. Надо доказать, что программа, скомпилированная данным компилятором, работает правильно.
                                    Например, КТ-178 вещает следующее:
                                    Компилятор считается пригодным для создания программного продукта после успешного завершения верификации данного продукта.

                                    у нас имеется правило запрета использования прописной буквы я даже в комментариях, вы знаете откуда это идёт, думаю что нет

                                    Боитесь разных символьных наборов на разных устройствах или ещё какой-то идиотизм. Но у меня другой вопрос: а расставлять знаки препинания как попало вас тоже заставляют?
                                    Вы можете написать правильно работающую программу, но вот использовать вам ее не разрешат, так как вы не сможете доказать ее правильность.

                                    Ценная мысль, надо бы отлить в граните.
                                    Но зачем вы написали весь этот ваш комментарий?
                            +1
                            Да вот только правило говорит нам, что следует всегда использовать default. Я и указываю на излишнюю догматичность и даже вредность этого правила для случая enum.

                            В случае, если enum содержит, например, пару десятков значений, а реально полезны (как-то обрабатываются) из них в конкретном switch штук пять — перечисление всех возможных значений в switch вряд ли можно назвать чистым и читаемым кодом. Указание только обрабатываемых значений и обработка по default, по мне, выглядят куда более читабельными.

                            Конечно, всё, что пришло извне, должно подвергаться проверке. Только при чём тут default?

                            Как я понял, речь о том, что значение переменной в памяти может измениться/побиться, причём может получиться совершенно произвольное значение, не соответствующее ни одному из используемых в enum (т.е. при использовании enum со значениями 1, 2, 3 переменная может оказаться равной, скажем, 23). В таком случае наличие case со всеми значениями enum и без default в конце не приведёт к срабатыванию ни одного case (значение-то совсем кривое). И вот тут default с «аварийной» обработкой будет очень кстати.
                            Поэтому лично я обеими руками за default всегда и везде.
                              0
                              В случае, если enum содержит, например, пару десятков значений, а реально полезны (как-то обрабатываются) из них в конкретном switch штук пять — перечисление всех возможных значений в switch вряд ли можно назвать чистым и читаемым кодом. Указание только обрабатываемых значений и обработка по default, по мне, выглядят куда более читабельными.

                              Если у вас enum с парой десятков значений или используется как флаги (что очень любят), то код ваш уже говно — можете использовать default.
                              Как я понял, речь о том, что значение переменной в памяти может измениться/побиться, причём может получиться совершенно произвольное значение, не соответствующее ни одному из используемых в enum (т.е. при использовании enum со значениями 1, 2, 3 переменная может оказаться равной, скажем, 23). В таком случае наличие case со всеми значениями enum и без default в конце не приведёт к срабатыванию ни одного case (значение-то совсем кривое). И вот тут default с «аварийной» обработкой будет очень кстати.
                              Поэтому лично я обеими руками за default всегда и везде.

                              Да, вон там ниже HighPredator, тоже такой же рукастый, обосрался на простейшем примере — полюбопытствуйте.
                          –1
                          Без проблем. Предположим, есть следующий код:
                          #include <stdio.h>
                          #include <stdlib.h>
                          #include <string.h>
                          
                          enum values
                          {
                              one,
                              two,
                              three,
                              four,
                              five
                          };
                          
                          int foo(void)
                          {
                              return rand() % (five + 1);
                          }
                          
                          int main(void)
                          {
                              enum values f = foo();
                          
                              printf("Original value = %d\n", f);
                          
                              char c;
                          
                              memset(&c, 0xFF, 2 * sizeof(c));
                          
                              printf("New value = %d\n", f);
                          
                              switch (f)
                              {
                                  case one:
                                  case two:
                                  case three:
                                  case four:
                                  case five:
                                      printf("Within range\n");
                                      break;
                                  default:
                                      printf("Out of range\n");
                                      break;
                              }
                          
                              return 0;
                          }
                          Идея простая: из-за ошибки в коде, повреждается переменная и по факту начинает содержать значения не из перечисления. В таких случаях, дефолтный блок, написанный осмысленно, будет выступать барьером против неопределенного поведения в процессе работы приложения в рантайме. А оно бы и получилось в данном варианте, ведись работа с переменной дальше с ожиданием, что значения всегда будут в диапазоне.
                            0
                            Без проблем. Предположим, есть следующий код

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

                            Идея идиотская, а запуск показывает, что вашим default даже подтереться нельзя, не то что «барьером против неопределенного поведения» сделать.
                            А оно бы и получилось в данном варианте

                            А неопределённое поведение и так случилось — в момент записи в память. И default здесь не поможет. Идея написать такой код, который будет устойчив к неопределённому поведению C/C++ такая же дебильная, как идея ораторов выше исправлять кодом аппаратные ошибки.
                            Неопределённое поведение потому и неопределённое, что не определено :)
                            А оно бы и получилось в данном варианте, ведись работа с переменной дальше с ожиданием, что значения всегда будут в диапазоне

                            Вы бы предыдущую дискуссию хоть бы почитали. В том то и дело, что я нигде не предлагал всегда считать, что значение в допустимом диапазоне.
                            Наверное, это сложная для вас мысль, потому я повторю её иначе: не писать default и всегда считать, что переменная в допустимом диапазоне — это две большие разницы.
                            Хотя выше уже есть мой код, иллюстрирующий эти слова, только сегодня и только сейчас я специально для вас продемонстрирую этот трюк ещё раз, предварительно пофиксив ваш код: невероятный трюк.
                            И хотя тут нет претензий на мировое господство защиту от неопределённого поведения и косяков «железа», тем не менее присутствует реальная защита — от забытого случая при добавлении нового элемента в enum.
                            А вот полагаюсь я на то, что значение enum корректное (т.е. является одним из его элементов), только тогда, когда 1) оно уже было проверено при поступлении на вход программы (из сети, консоли, файлов и т.п.) и 2) это enum class в C++. Потому что, как и было показано вашим замечательным примером, от неопределённого поведения ничего не спасёт, разве что свечка в церкви и санитайзер на CI, а присвоить случайно число в enum class переменную нельзя. Таким образом останутся только логические ошибки, которые ловятся тестами.
                              0
                              С вами все понятно. Разговор закончен.
                                0
                                Что, так обидно, что не проверили пример? :) Ну, не расстраивайтесь, это даже хорошо — будет вам хорошим уроком: от неопределённого поведения надо избавляться, а не стремиться его побороть постфактум во время исполнения.
                                  0
                                  Нет. Я не веду диалог а) с хамлом, б) с неумеющими читать.
                        0

                        А почему плохо засунуть return -1 в default секцию?

                        +2
                        Как по мне, самый большой минус стандарта то, что он платный. При этом есть очень похожий стандарт: SEI CERT C Coding Standard. И доступ к нему полный.
                          0
                          Кстати, PVS-Studio умеет классифицировать свои диагностики и согласно SEI CERT.
                          +1
                          Хардкор в MISRA начинаестся не с языковых конструкций, а со стандартной библиотекой. Например: malloc/realloc/free — strictly prohibited. И теперь живите с этим (это кстати нормально).
                            +4
                            Ну, если человеку (не вам лично, я вообще) кажется хардкором отсутствие динамического выделения памяти — значит он очень далек от эмбеда. :)

                            Статическое объявление всего — вообще первая особенность программирования под железо. Во-первых, в 90% случаев отсутствует ОС, так что по умолчанию следить за памятью нечему, а реализации менеджера памяти в стандартной библиотеке не слишком оптимальны. При этом действительно хорошая реализация malloc() может занимать места столько же, сколько вся программа.

                            Ну и я уже не говорю о том, что выделение памяти, действительно, недетерминированная операция; нет никакой гарантии, что в нужный момент окажется доступным нужный объем памяти.
                              0
                              У меня как раз на прошлой неделе джуниоры впоролись в нехватку 8К RAM. До этого писали только на ПК, вот и удивились что оно вона как бывает в реальной жизни. Ну ничего, научатся ещё.
                              Я им сразу говорил, что строки должны лежать в progmem и там же должна быть таблица с указателями на эти строки. Но нет, мы типа умные и за всем сами следим. В итоге потеряли два дня, зато вроде урок усвоили.
                            0

                            Добрый вечер! Спасибо за статью...


                            Вы написали:
                            "…


                            Дело в том, что не существует какого-то специального «аттестата» о том, что ваш код соответствует MISRA. Как оговаривает сам стандарт, отслеживание кода на соответствие должно выполняться двумя сторонами: заказчиком ПО и поставщиком ПО. Поставщик разрабатывает ПО, соответствующее стандарту, и заполняет необходимые документы. Заказчик же со своей стороны должен удостовериться, что данные из этих документов соответствуют действительности.


                            ..."


                            Скажите пожалуйста, если исполнитель не предоставляет исходный код, то как заказчик проверит на соответствие стандарту?

                              0
                              Наверное, никак. Вряд ли можно доверять таблицам, не имея доступу к исходному коду.
                                0

                                Спасибо за ответ :)
                                Есть ещё вопрос…
                                Скажи пожалуйста:
                                Есть ли стандартизированные компиляторы для автомобильной промышленности? (в части реализации функциональной безопасности).
                                Есть ли стандартизированный Статический анализатор (в части реализации функциональной безопасности)?

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                            Самое читаемое