CERT C 规范
CERT C 规范
https://ww2.mathworks.cn/help/bugfinder/cert-c-rules-and-recommendations.html?lang=en
规则
预处理器
[规则 PRE30-C] 禁止通过宏拼接创建通用字符名。
[规则 PRE31-C] 避免在不安全宏的参数中引入副作用。
[规则 PRE32-C] 禁止在类函数宏的调用中使用预处理指令。
声明与初始化
[规则 DCL30-C] 为对象声明合适的存储期。
[规则 DCL31-C] 标识符使用前必须先声明。
[规则 DCL36-C] 禁止为同一个标识符声明冲突的链接属性。
[规则 DCL37-C] 禁止声明或定义保留标识符。
[规则 DCL38-C] 声明柔性数组成员时使用正确的语法。
[规则 DCL39-C] 避免结构体填充带来的信息泄露。
[规则 DCL40-C] 禁止为同一个函数或对象创建不兼容的声明。
[规则 DCL41-C] 禁止在 switch 语句中第一个 case 标签之前声明变量。
表达式
[规则 EXP30-C] 不要依赖带副作用表达式的求值顺序。
[规则 EXP32-C] 禁止通过非 volatile 引用访问 volatile 对象。
[规则 EXP33-C] 禁止读取未初始化的内存。
[规则 EXP34-C] 禁止解引用空指针。
[规则 EXP35-C] 禁止修改具有临时生存期的对象。
[规则 EXP36-C] 禁止将指针强制转换为对齐要求更严格的指针类型。
[规则 EXP37-C] 调用函数时传入正确数量和类型的参数。
[规则 EXP39-C] 禁止通过类型不兼容的指针访问变量。
[规则 EXP40-C] 禁止修改常量对象。
[规则 EXP42-C] 禁止对结构体填充数据进行比较操作。
[规则 EXP43-C] 使用 restrict 限定的指针时,避免引发未定义行为。
[规则 EXP44-C] 不要依赖 sizeof、_Alignof 或 _Generic 操作数中的副作用。
[规则 EXP45-C] 禁止在选择语句中执行赋值操作。
[规则 EXP46-C] 禁止对类布尔操作数使用位运算符。
[规则 EXP47-C] 调用 va_arg 时,禁止传入类型不正确的参数。
整数
[规则 INT30-C] 确保无符号整数运算不会发生回绕。
[规则 INT31-C] 确保整数转换不会导致数据丢失或被错误解读。
[规则 INT32-C] 确保有符号整数运算不会发生溢出。
[规则 INT33-C] 确保除法和取余运算不会引发除零错误。
[规则 INT34-C] 禁止对表达式执行负数位数的移位,或移位位数大于等于操作数本身的位宽。
[规则 INT35-C] 使用正确的整数精度。
[规则 INT36-C] 规范指针与整数之间的相互转换。
浮点数
[规则 FLP30-C] 禁止使用浮点型变量作为循环计数器。
[规则 FLP32-C] 预防并检测数学函数中的定义域与值域错误。
[规则 FLP34-C] 确保浮点数转换后的值在目标类型的取值范围内。
[规则 FLP36-C] 整数值转换为浮点类型时,需保证精度不丢失。
[规则 FLP37-C] 禁止通过对象的内存表示来比较浮点数值。
数组
[规则 ARR30-C] 禁止生成或使用越界的指针或数组下标。
[规则 ARR32-C] 确保变长数组的长度参数处于有效范围内。
[规则 ARR36-C] 禁止对不指向同一数组的两个指针进行相减或比较操作。
[规则 ARR37-C] 禁止对非数组对象的指针执行整数加减操作。
[规则 ARR38-C] 确保库函数不会生成无效指针。
[规则 ARR39-C] 禁止对指针执行缩放后的整数加减操作。
字符与字符串
[规则 STR30-C] 禁止尝试修改字符串字面量。
[规则 STR31-C] 确保字符串的存储空间足以容纳字符数据和空终止符。
[规则 STR32-C] 禁止将非空终止的字符序列传入期望字符串参数的库函数。
[规则 STR34-C] 字符转换为更大的整数类型前,需先强制转换为 unsigned char 类型。
[规则 STR37-C] 字符处理函数的参数必须能以 unsigned char 类型表示。
[规则 STR38-C] 不要混淆窄字符、宽字符字符串及其对应的处理函数。
内存管理
[规则 MEM30-C] 禁止访问已释放的内存。
[规则 MEM31-C] 动态分配的内存不再使用时需及时释放。
[规则 MEM33-C] 对包含柔性数组成员的结构体,需使用动态方式进行分配和拷贝。
[规则 MEM34-C] 仅释放动态分配的内存。
[规则 MEM35-C] 为对象分配足够的内存空间。
[规则 MEM36-C] 禁止通过调用 realloc() 修改对象的内存对齐属性。
输入输出
[规则 FIO30-C] 禁止将用户输入内容嵌入格式字符串。
[规则 FIO32-C] 禁止对设备执行仅适用于普通文件的操作。
[规则 FIO34-C] 区分从文件读取的字符与 EOF、WEOF。
[规则 FIO37-C] 不要假定 fgets() 或 fgetws() 调用成功时一定会返回非空字符串。
[规则 FIO38-C] 禁止拷贝 FILE 对象。
[规则 FIO39-C] 对流交替执行输入和输出操作时,中间必须插入刷新或定位操作。
[规则 FIO40-C] fgets() 或 fgetws() 调用失败时,需重置相关字符串。
[规则 FIO41-C] 调用 getc()、putc()、getwc() 或 putwc() 时,流参数禁止引入副作用。
[规则 FIO42-C] 文件不再使用时需及时关闭。
[规则 FIO44-C] fsetpos() 仅能使用 fgetpos() 返回的位置值。
[规则 FIO45-C] 访问文件时,避免出现时间检查 - 时间使用(TOCTOU)竞态条件。
[规则 FIO46-C] 禁止访问已关闭的文件。
[规则 FIO47-C] 使用合法的格式字符串。
环境
[规则 ENV30-C] 禁止修改特定函数返回值所指向的对象。
[规则 ENV31-C] 环境指针在执行可能使其失效的操作后,禁止继续依赖该指针。
[规则 ENV32-C] 所有退出处理程序必须正常返回。
[规则 ENV33-C] 禁止调用 system() 函数。
[规则 ENV34-C] 禁止存储特定函数返回的指针。
信号
[规则 SIG30-C] 信号处理函数内仅能调用异步安全函数。
[规则 SIG31-C] 信号处理函数内禁止访问共享对象。
[规则 SIG34-C] 禁止在可中断的信号处理函数内调用 signal() 函数。
[规则 SIG35-C] 计算异常信号的处理函数禁止正常返回。
错误处理
[规则 ERR30-C] 调用会设置 errno 的库函数前,需将 errno 置零;仅当函数返回值表明调用失败后,再检查 errno 的值。
[规则 ERR32-C] 不要依赖 errno 的不确定值。
[规则 ERR33-C] 检测并处理标准库函数的错误。
[规则 ERR34-C] 字符串转换为数值时,需检测并处理转换错误。
并发
[规则 CON30-C] 及时清理线程本地存储。
[规则 CON31-C] 禁止销毁处于锁定状态的互斥锁。
[规则 CON32-C] 多线程环境下访问位域时,需预防数据竞争。
[规则 CON33-C] 使用库函数时,避免出现竞态条件。
[规则 CON34-C] 为线程间共享的对象声明合适的存储期。
[规则 CON35-C] 按预定义顺序加锁,避免死锁。
[规则 CON36-C] 将可能发生虚假唤醒的函数包裹在循环中。
[规则 CON37-C] 多线程程序中禁止调用 signal() 函数。
[规则 CON38-C] 使用条件变量时,需保证线程安全性与活性。
[规则 CON39-C] 禁止对已执行连接或分离操作的线程,再次执行连接或分离操作。
[规则 CON40-C] 禁止在一个表达式中对原子变量引用两次。
[规则 CON41-C] 将可能发生虚假失败的函数包裹在循环中。
[规则 CON43-C] 多线程代码中禁止出现数据竞争。
杂项
[规则 MSC30-C] 禁止使用 rand() 函数生成伪随机数。
[规则 MSC32-C] 为伪随机数生成器设置正确的种子。
[规则 MSC33-C] 禁止向 asctime() 函数传入非法数据。
[规则 MSC37-C] 确保控制流永远不会到达非 void 函数的末尾。
[规则 MSC38-C] 若预定义标识符可能仅以宏的形式实现,禁止将其当作对象处理。
[规则 MSC39-C] 禁止对值不确定的 va_list 调用 va_arg()。
[规则 MSC40-C] 禁止违反语言约束。
[规则 MSC41-C] 严禁硬编码敏感信息。
POSIX 扩展
[规则 POS30-C] 正确使用 readlink() 函数。
[规则 POS34-C] 禁止将自动变量的指针作为参数传入 putenv() 函数。
[规则 POS35-C] 检查符号链接是否存在时,避免出现竞态条件。
[规则 POS36-C] 放弃权限时,需遵循正确的撤销顺序。
[规则 POS37-C] 确保权限放弃操作执行成功。
[规则 POS38-C] 使用 fork 与文件描述符时,警惕竞态条件。
[规则 POS39-C] 系统间传输数据时,使用正确的字节序。
[规则 POS44-C] 禁止使用信号终止线程。
[规则 POS47-C] 禁止使用可被异步取消的线程。
[规则 POS48-C] 禁止解锁或销毁其他 POSIX 线程的互斥锁。
[规则 POS49-C] 当数据需要被多个线程访问时,需提供互斥锁,并确保不会访问相邻数据。
[规则 POS50-C] 为 POSIX 线程间共享的对象声明合适的存储期。
[规则 POS51-C] POSIX 线程按预定义顺序加锁,避免死锁。
[规则 POS52-C] 持有 POSIX 锁期间,禁止执行可能发生阻塞的操作。
[规则 POS53-C] 条件变量上的并发等待操作,禁止使用超过一个互斥锁。
[规则 POS54-C] 检测并处理 POSIX 库函数的错误。
微软 Windows 扩展
[规则 WIN30-C] 正确配对内存分配与释放函数。
建议
预处理器
[建议 PRE00-C] 优先使用内联函数或静态函数,而非类函数宏。
[建议 PRE01-C] 宏内的参数名称需用括号包裹。
[建议 PRE02-C] 宏的替换列表需用括号包裹。
[建议 PRE03-C] 编码非指针类型时,优先使用 typedef 而非 define。
[建议 PRE04-C] 不要复用标准头文件的名称。
[建议 PRE05-C] 理解令牌拼接或字符串化操作时的宏替换规则。
[建议 PRE06-C] 头文件需使用包含保护宏包裹。
[建议 PRE07-C] 避免使用连续的问号。
[建议 PRE08-C] 确保头文件名具有唯一性。
[建议 PRE09-C] 不要用已弃用或过时的函数替换安全函数。
[建议 PRE10-C] 多语句宏需用 do-while 循环包裹。
[建议 PRE11-C] 宏定义的末尾不要加分号。
[建议 PRE12-C] 不要定义不安全的宏。
声明与初始化
[建议 DCL00-C] 对不可变对象添加 const 限定。
[建议 DCL01-C] 不要在子作用域中复用变量名。
[建议 DCL02-C] 使用视觉上易于区分的标识符。
[建议 DCL03-C] 使用静态断言检测常量表达式的值。
[建议 DCL04-C] 一条声明语句仅声明一个变量。
[建议 DCL05-C] 仅对非指针类型使用 typedef。
[建议 DCL06-C] 使用有意义的符号常量表示字面量值。
[建议 DCL07-C] 函数声明符中需包含完整的类型信息。
[建议 DCL09-C] 返回 errno 的函数,需将返回值类型声明为 errno_t。
[建议 DCL20-C] 函数无参数时,需显式指定参数列表为 void。
[建议 DCL10-C] 维护可变参数函数的实现者与调用者之间的契约。
[建议 DCL11-C] 理解可变参数函数相关的类型问题。
[建议 DCL12-C] 使用不透明类型实现抽象数据类型。
[建议 DCL13-C] 函数参数中,指向不会被函数修改的值的指针,需声明为 const。
[建议 DCL15-C] 不需要外部链接的文件作用域对象或函数,需声明为 static。
[建议 DCL16-C] 使用大写的 ‘L’ 而非小写的 ‘l’ 标识长整型数值。
[建议 DCL18-C] 表示十进制数值时,整数常量不要以 0 开头。
[建议 DCL19-C] 最小化变量和函数的作用域。
[建议 DCL21-C] 理解复合字面量的存储规则。
[建议 DCL22-C] 对无法被缓存的数据使用 volatile 限定。
[建议 DCL23-C] 确保相互可见的标识符具有唯一性。
表达式
[建议 EXP00-C] 使用括号明确运算符的优先级。
[建议 EXP02-C] 注意逻辑与、逻辑或运算符的短路行为。
[建议 EXP03-C] 不要假定结构体的大小等于其所有成员大小的总和。
[建议 EXP05-C] 不要去除指针的 const 限定。
[建议 EXP07-C] 不要在表达式中假定常量的值,避免削弱常量的作用。
[建议 EXP08-C] 确保指针运算的使用符合规范。
[建议 EXP09-C] 使用 sizeof 运算符获取类型或变量的大小。
[建议 EXP10-C] 不要依赖子表达式的求值顺序,以及副作用发生的顺序。
[建议 EXP11-C] 不要对带位域的结构体的内存布局做任何假定。
[建议 EXP12-C] 不要忽略函数的返回值。
[建议 EXP13-C] 将关系运算符和相等性运算符视为非结合性运算符处理。
[建议 EXP15-C] 不要将分号与 if、for 或 while 语句写在同一行。
[建议 EXP16-C] 不要将函数指针与常量值进行比较。
[建议 EXP19-C] if、for 或 while 语句的循环体需使用大括号包裹。
[建议 EXP20-C] 通过显式测试判断操作成功与否、布尔值真假以及数值相等性。
整数
[建议 INT00-C] 理解开发环境所使用的数据模型。
[建议 INT02-C] 理解整数转换规则。
[建议 INT04-C] 对来自污染源的整数值强制做范围限制。
[建议 INT07-C] 数值型字符仅使用显式的 signed 或 unsigned char 类型。
[建议 INT08-C] 校验所有整数值都在有效范围内。
[建议 INT09-C] 确保枚举常量映射到唯一的值。
[建议 INT10-C] 使用 % 运算符时,不要假定余数一定为正。
[建议 INT12-C] 不要对表达式中普通 int 类型位域的类型做任何假定。
[建议 INT13-C] 位运算符仅作用于无符号操作数。
[建议 INT14-C] 避免对同一数据同时执行位运算和算术运算。
[建议 INT16-C] 不要对有符号整数的二进制表示做任何假定。
[建议 INT17-C] 以与实现无关的方式定义整数常量。
[建议 INT18-C] 整数表达式在比较或赋值前,先在更大的类型宽度中完成求值。
浮点数
[建议 FLP00-C] 理解浮点数的运算局限性。
[建议 FLP02-C] 需要精确计算的场景,避免使用浮点数。
[建议 FLP03-C] 检测并处理浮点数运算错误。
[建议 FLP06-C] 执行浮点运算前,先将整数转换为浮点类型。
数组
[建议 ARR01-C] 获取数组大小时,不要对指针使用 sizeof 运算符。
[建议 ARR02-C] 即使初始化列表已隐式定义了数组边界,也需显式指定。
字符与字符串
[建议 STR00-C] 使用合适的类型表示字符。
[建议 STR02-C] 对传入复杂子系统的数据进行无害化处理。
[建议 STR03-C] 避免无意中截断字符串。
[建议 STR06-C] 不要假定 strtok() 不会修改解析的原字符串。
[建议 STR07-C] 字符串操作优先使用带边界检查的接口。
[建议 STR10-C] 不要拼接不同类型的字符串字面量。
[建议 STR11-C] 用字符串字面量初始化字符数组时,不要指定数组长度。
内存管理
[建议 MEM00-C] 内存的分配与释放需在同一模块、同一抽象层级中执行。
[建议 MEM01-C] 指针指向的内存被释放后,立即为指针赋新值。
[建议 MEM02-C] 内存分配函数的返回值,需立即强制转换为对应分配类型的指针。
[建议 MEM03-C] 清除可复用资源中存储的敏感信息。
[建议 MEM04-C] 警惕长度为 0 的内存分配操作。
[建议 MEM05-C] 避免在栈上分配大块内存。
[建议 MEM06-C] 确保敏感数据不会被写入磁盘。
[建议 MEM11-C] 不要假定堆空间是无限的。
[建议 MEM12-C] 函数执行出错需要释放资源并退出时,可考虑使用 goto 链处理。
输入输出
[建议 FIO02-C] 对来自污染源的路径名进行规范化处理。
[建议 FIO03-C] 不要对 fopen() 的行为和文件创建逻辑做任何假定。
[建议 FIO06-C] 创建文件时设置合适的访问权限。
[建议 FIO08-C] 对已打开的文件调用 remove() 时需谨慎。
[建议 FIO10-C] 使用 rename() 函数时需谨慎。
[建议 FIO11-C] 指定 fopen() 的 mode 参数时需谨慎。
[建议 FIO21-C] 不要在共享目录中创建临时文件。
[建议 FIO24-C] 不要打开已处于打开状态的文件。
环境
[建议 ENV01-C] 不要对环境变量的大小做任何假定。
信号
[建议 SIG02-C] 避免使用信号实现常规业务功能。
错误处理
[建议 ERR00-C] 采纳并实现一套统一、完善的错误处理策略。
[建议 ERR01-C] 检查 FILE 流错误时,优先使用 ferror() 而非 errno。
[建议 ERR06-C] 理解 assert() 和 abort() 的程序终止行为。
[建议 ERR07-C] 优先使用支持错误检查的函数,而非功能等价但无错误检查的函数。
应用程序编程接口
[建议 API01-C] 避免在内存中将字符串紧挨着敏感数据排布。
[建议 API02-C] 对数组进行读写操作的函数,需传入参数指定源或目标数组的大小。
[建议 API04-C] 提供一套统一、易用的错误检查机制。
[建议 API05-C] 使用兼容数组参数。
并发
[建议 CON01-C] 同步原语的获取与释放需在同一模块、同一抽象层级中执行。
[建议 CON05-C] 持有锁期间,禁止执行可能发生阻塞的操作。
杂项
[建议 MSC00-C] 在高警告级别下实现无警告编译。
[建议 MSC01-C] 保证代码逻辑的完整性。
[建议 MSC04-C] 统一使用注释,保证注释的可读性。
[建议 MSC05-C] 不要直接操作 time_t 类型的值。
[建议 MSC06-C] 警惕编译器优化带来的潜在问题。
[建议 MSC12-C] 检测并移除无效果或永远不会执行的代码。
[建议 MSC13-C] 检测并移除未使用的变量值。
[建议 MSC15-C] 不要依赖未定义行为。
[建议 MSC17-C] 每个 case 标签对应的语句块末尾,都必须有 break 语句。
[建议 MSC18-C] 程序代码中处理密码等敏感数据时需格外谨慎。
[建议 MSC20-C] 不要使用 switch 语句将控制流转移到复杂语句块内部。
[建议 MSC21-C] 使用健壮的循环终止条件。
[建议 MSC22-C] 安全使用 setjmp()、longjmp() 机制。
[建议 MSC24-C] 不要使用已弃用或过时的函数。
POSIX 扩展
[建议 POS04-C] 避免使用 PTHREAD_MUTEX_NORMAL 类型的互斥锁。
[建议 POS05-C] 通过创建沙箱限制对文件的访问。
微软 Windows 扩展
[建议 WIN00-C] 动态加载库时需明确指定库名。
[建议 WIN01-C] 不要强制终止程序执行。
[建议 WIN02-C] 创建子进程时需限制其权限。
[建议 WIN03-C] 理解句柄继承的机制。
[建议 WIN04-C] 考虑对函数指针进行加密处理。