10 правил кодирования НАСА для написания критически важных для безопасности программ

По | 22.11.2020

В крупных и сложных программных проектах используются различные стандарты и рекомендации по кодированию. Эти руководящие принципы устанавливают основные правила, которым необходимо следовать при написании программного обеспечения. Обычно они определяют:

а) Как следует структурировать код?
б) Какую языковую функцию следует или не следует использовать?

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

Лучшие программисты мира, работающие в НАСА, следуют ряду рекомендаций по разработке критически важного для безопасности кода. Фактически, многие агентства, в том числе Лаборатория реактивного движения НАСА (JPL), сосредотачиваются на коде, написанном на языке программирования C. Это связано с тем, что для этого языка существует обширная поддержка инструментов, таких как экстракторы логических моделей, отладчики, стабильный компилятор, надежные анализаторы исходного кода и инструменты метрик.

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

Но знаете ли вы, какие стандарты используют космические агентства для управления своими машинами? Ниже мы перечислили 10 правил кодирования НАСА, установленных ведущим ученым JPL Джерардом Хольцманном. Все они в первую очередь ориентированы на параметры безопасности, и вы также можете применить их к другим языкам программирования.

Правило № 1 — Простой поток управления

Пишите программы с очень простыми конструкциями потока управления — не используйте setjmp или longjmp конструкции, перейти к заявления, а также прямые или косвенные рекурсия.

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

Правило № 2 — Фиксированная верхняя граница петель

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

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

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

Правило № 3 — Запрет динамического выделения памяти

Не используйте динамическое выделение памяти после инициализации.

Причина: Распределители памяти вроде маллок, а сборщики мусора часто ведут себя непредсказуемо, что может исключительно повлиять на производительность. Кроме того, ошибки памяти также могут возникать из-за ошибки программиста, в том числе

  • Попытка выделить больше памяти, чем доступно физически
  • Забывая освободить память
  • Продолжение использования памяти после ее освобождения
  • Превышение границ выделенной памяти

Принуждение всех модулей к размещению в фиксированной заранее выделенной области хранения может устранить эти проблемы и упростить проверку использования памяти.

Один из способов динамически потребовать память при отсутствии выделения памяти из кучи — использовать стековую память.

Правило № 4 — Никаких больших функций

Никакая функция не должна быть длиннее той, что может быть напечатана на одном листе бумаги в стандартном справочном формате с одной строкой на объявление и одной строкой на утверждение. Это означает, что функция не должна содержать более 60 строк кода.

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

Правило № 5 — Низкая плотность утверждения

Низкая плотность утверждения

Плотность утверждений программы должна составлять минимум два утверждения на функцию. Утверждения используются для проверки ненормальных условий, которые никогда не должны происходить при выполнении в реальной жизни. Их следует определять как булевы тесты. Когда утверждение не выполняется, следует предпринять явное действие по восстановлению.

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

Причина: Согласно отраслевой статистике усилий по кодированию, модульные тесты выявляют как минимум один дефект на 10–100 строк кода. Вероятность обнаружения дефектов увеличивается с увеличением плотности утверждений.

Использование утверждений также важно, поскольку они являются частью сильной стратегии защитного кодирования. Они используются для проверки условий до и после функции, параметра и возвращаемого значения функции и инвариантов цикла. Утверждения можно выборочно отключить после тестирования кода, критичного к производительности.

Правило № 6 — объявляйте объекты данных на наименьшем уровне объема

Это поддерживает основной принцип сокрытия данных. Все объекты данных должны быть объявлены на минимально возможном уровне области действия.

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

Прочтите: 20 величайших программистов всех времен

Правило № 7 — Проверка параметров и возвращаемого значения

Возвращаемое значение (я) непустых функций должно проверяться каждой вызывающей функцией, а правильность параметров должна проверяться внутри каждой функции.

В самой строгой форме это правило означает даже возвращаемое значение printf заявления и файл близко утверждения следует проверять.

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

Правило № 8 — Ограниченное использование препроцессора

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

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

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

Предостережение против условной компиляции не менее важно — всего с 10 директивами условной компиляции может быть 1024 возможных версии (2 ^ 10) кода, что увеличит требуемые усилия по тестированию.

Прочтите: 9 новых языков программирования, которые нужно выучить в этом году

Правило № 9 — Ограниченное использование указателей

Использование указателей должно быть ограничено. Допускается не более одного уровня разыменования. Операции разыменования указателя не должны быть скрыты внутри typedef объявление или определения макросов.

Указатели на функции также не допускаются.

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

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

Читайте: 14 лучших программ для написания кода

Правило № 10 — Составьте весь код

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

Весь код следует проверять ежедневно по крайней мере с одним (желательно более чем одним) современным статическим анализатором исходного кода и должен проходить процесс анализа без предупреждений.

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

Читайте: 30 удивительных изобретений НАСА, которые мы используем в нашей повседневной жизни

Что НАСА говорит об этих правилах?

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

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *