目录

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] 区分从文件读取的字符与 EOFWEOF

[规则 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] 不要将分号与 ifforwhile 语句写在同一行。

[建议 EXP16-C] 不要将函数指针与常量值进行比较。

[建议 EXP19-C] ifforwhile 语句的循环体需使用大括号包裹。

[建议 EXP20-C] 通过显式测试判断操作成功与否、布尔值真假以及数值相等性。


整数

[建议 INT00-C] 理解开发环境所使用的数据模型。

[建议 INT02-C] 理解整数转换规则。

[建议 INT04-C] 对来自污染源的整数值强制做范围限制。

[建议 INT07-C] 数值型字符仅使用显式的 signedunsigned 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] 考虑对函数指针进行加密处理。