SEI CERT C Coding Standard был разработан по результатам исследования стандарта языка С и списка уязвимостей Common Weakness Enumeration. Все указания на неопределенное поведение (UB) в функциях библиотеки языка С и конструкциях языка были оформлены как правила и рекомендации.
Некоторые правила очевидны (проверять деление на 0), некоторые могут проявиться только при очень странных действиях программиста (копирование FILE), про некоторые правила уже упоминалось ранее (размер массива для храненеия строки).
Аналогичный стандарт MISRA С используется при разработке кода для микроконтроллеров транспортных средств и вводит жесткие ограничения с целью обеспечения большей переносимости кода (int32_t вместо int, запрет setlocale), улучшения его читабельности (обязательные скобки в логических условиях и {} во всех операторах), множество запретов на потенциально опасные действия (использование динамической памяти, рекурсия, операция запятая).
Препроцессор
Предпочитайте встроенные или статические функции макросам с параметрами
#define ABS(x) (((x) < 0) ? -(x) : (x)) заменить на inline int abs(int x) { return x<0?-x:x; } и _Generic, если нужна функция для аргументов разных типов.
Используйте круглые скобки внутри макросов вокруг имен параметров
Списки замены макросов должны быть заключены в круглые скобки
Не используйте имена стандартных заголовочных файлов для своих файлов
Понимать порядок замен макросов при сцеплении или создании строковых констант
Используйте в заголовочном файле защиту от повторного включения
Избегайте использования повторяющихся вопросительных знаков
Вплоть до последней версии C23 препроцессор выполнял замену последовательностей из 3 символов (триграфов), начинающихся с ??, на символы, отсутствующие на некоторых национальных клавиатурах. Так как замена происходит везде, даже в комментариях и строках, это может привести к неожиданным результатам. Хотя в С23 триграфы убрали, есть возможность включить их опцией компилятора.
#include<stdio.h>intmain(){
int x=0; // Почему 0??/
x=1;
printf("x=%d??!??/n", x); // Вывод x=0| Для вывода ??! написать "x=%d?""?!\n"
}
Р’РІРѕРґ:
Выполнить Вывод:
Изучите детали реализации, касающиеся сохранения обработчика сигналов
Избегайте использования сигналов для реализации обычной функциональности
Вызывайте только асинхронно-безопасные функции в обработчиках сигналов
Не обращайтесь к общим объектам в обработчиках сигналов
Не вызывайте signal() из прерываемых обработчиков сигналов
Не возвращайте из обработчика сигналов исключений
Обработка ошибок
Примите и реализуйте последовательную и всеобъемлющую политику обработки ошибок
Используйте ferror() вместо errno для проверки ошибок потока FILE
Избегайте внутриполосных индикаторов ошибок
Используйте обработчики ограничений времени выполнения при вызове интерфейсов проверки границ
Выберите подходящую стратегию завершения
Независимый от приложения код должен обеспечивать обнаружение ошибок, не диктуя обработку ошибок
Поймите поведение завершения assert() и abort()
Предпочитайте функции, которые поддерживают проверку ошибок, эквивалентным функциям, которые не поддерживают
Установите errno в ноль перед вызовом библиотечной функции, известной тем, что она устанавливает errno, и проверяйте errno только после того, как функция вернет значение, указывающее на сбой
Не полагайтесь на неопределенные значения errno
Выявляйте и обрабатывайте стандартные ошибки библиотеки
Обнаруживайте ошибки преобразования строки в число
Интерфейсы
Функции должны проверять свои параметры
Избегайте размещения строк в памяти непосредственно перед конфиденциальными данными
Функции, которые считывают или записывают в массив или из него, должны принимать аргумент для указания исходного или целевого размера
Создавайте согласованные интерфейсы и возможности для связанных функций
Предоставляйте согласованный и удобный механизм проверки ошибок
Используйте совместимые параметры массива
Обеспечьте безопасность типов
Совместимые значения должны иметь один и тот же тип
API должны иметь параметры безопасности, включенные по умолчанию
Распараллеливание
Получайте и освобождайте примитивы синхронизации в одном модуле, на одном уровне абстракции
Не используйте volatile в качестве примитива синхронизации
Обеспечьте видимость при доступе к общим переменным
Присоединяйтесь к потокам или отсоединяйтесь от них, даже если их статус выхода не важен
Не выполняйте операции, которые могут блокироваться, удерживая блокировку
Убедитесь, что каждый мьютекс переживет данные, которые он защищает
Убедитесь, что составные операции над общими переменными являются атомарными
Не предполагайте, что группа вызовов независимых атомарных методов является атомарной
Избегайте проблемы ABA при использовании алгоритмов без блокировок
Очищайте хранилище, специфичное для потока
Не уничтожайте мьютекс, пока он заблокирован
Предотвращайте гонки данных при доступе к битовым полям из нескольких потоков
Избегайте условий гонки при использовании библиотечных функций
Объявляйте объекты, совместно используемые потоками, с соответствующими сроками хранения
Избегайте взаимоблокировки, блокируя в предопределенном порядке
Обертывайте функции, которые могут ложно проснуться в цикле
Не вызывайте signal() в многопоточной программе
Сохраняйте безопасность и жизнеспособность потока при использовании условных переменных
Не присоединяйтесь и не отсоединяйтесь от потока, который ранее был присоединен или отсоединен
Не ссылайтесь на атомарную переменную дважды в выражении
Обертывайте функции, которые могут ложно завершиться сбоем в цикле
Не допускайте гонок данных в многопоточном коде
Разное
Стремитесь к чистой компиляция при высоких уровнях предупреждений
Стремитесь к логической завершенности
Используйте комментарии последовательно и в читаемой форме
Не манипулируйте значениями типа time_t напрямую
Остерегайтесь оптимизаций компилятора
Обнаружение и удаление мертвого кода
Кодировка символов: в целях безопасности используйте подмножество ASCII.
Кодировка символов: проблемы, связанные с UTF8
Включите диагностические тесты с использованием assert
Обнаружение и удаление кода, который не имеет никакого эффекта или никогда не выполняется
Обнаружение и удаление неиспользуемых переменных
Не вводите ненужные зависимости от платформы
Не зависьте от неопределенного поведения
Завершите каждый набор операторов, связанных с меткой case, оператором break
Будьте осторожны при работе с конфиденциальными данными (например, пароли) в программном коде
Для функций, возвращающих массив, предпочтительнее возвращать пустой массив, чем пустое значение
Не используйте оператор switch для передачи управления в сложный блок
Используйте надежные условия завершения цикла
Используйте setjmp(), longjmp() безопасно
Остерегайтесь различий в библиотеках и языках, специфичных для разных поставщиков
Не использовать устаревшие или не рекомендуемые функции
Не используйте небезопасные или слабые криптографические алгоритмы
Не используйте функцию rand() для генерации псевдослучайных чисел
Правильно задавайте начальные значения для генераторов псевдослучайных чисел
Не передавайте недействительные данные в функцию asctime()
Убедитесь, что управление никогда не достигнет конца непустой функции
Не обрабатывайте предопределенный идентификатор как объект, если он может быть реализован только как макрос
Не вызывайте va_arg() для va_list, имеющего неопределенное значение
Не нарушайте ограничения
Никогда не кодируйте конфиденциальную информацию напрямую