- 开发工具
- 静态分析
- 代码质量
- 质量保障
【免费下载链接】cppcheck
static analysis of C/C++ code
本篇文章围绕 cppcheck 的integerOverflow/integerOverflowCond检查项展开,讲解它如何基于平台整型宽度识别有符号整型算术表达式的上溢与下溢、其底层实现原理、误报规避策略以及修复方法。读完本文,你将掌握该检查项的报告条件、源码级判定逻辑,并能针对真实项目中的溢出告警给出正确修复方案。
检查项概览
Message: Signed integer overflow for expression 'x*y'.Category: Undefined Behaviour(未定义行为)Severity: Error / WarningLanguage: C/C++
该检查项由 lib/checktype.cpp 中的CheckTypeImpl::checkIntegerOverflow()实现,负责检测有符号整型算术表达式(+、-、*、/、<<等)是否会超出其结果类型所能表示的范围,产生溢出(overflow)或下溢(underflow)。判定依据是当前平台配置的整型宽度(int、long、long long的位数),并非笼统地按 32 位或 64 位假设。
检查项 ID 映射关系记录在 lib/checkersidmapping.cpp:旧版检查器 IDINT32被映射到integerOverflow,因此历史告警与抑制规则中的INT32仍能继续生效。
两种告警变体:integerOverflow 与 integerOverflowCond
按溢出的确定性程度,该检查项分为两种告警 ID:
| ID | 触发条件 | 示例消息 |
|---|---|---|
integerOverflow | 溢出/下溢是无条件的——对 cppcheck 能观察到的到达该点的每一个值都会发生 | Signed integer overflow for expression 'intmax+1' |
integerOverflowCond | 溢出/下溢仅在代码中某处条件成立时发生 | Either the condition 'x==123456' is redundant or there is signed integer overflow for expression 'x*x' |
两种变体的 ID 选择逻辑位于getMessageId()(lib/checktype.cpp):当 ValueFlow 推导出的溢出值带有condition(即该值依赖某个条件分支)时,ID 追加Cond后缀;若该值来自"安全检查"(safe)路径,则加上safe前缀(对应safeIntegerOverflow家族)。
integerOverflowCond的消息措辞也体现了其语义:既然溢出只有在条件成立时才可能发生,那么要么该条件本身是冗余的(条件为真时才会溢出的路径实际不可达),要么确实存在条件性溢出——两条路径必居其一。
为什么值得重视:有符号溢出的危害
有符号整型溢出在 C 和 C++ 标准中是未定义行为(UB)。实践中它通常意味着计算静默产生错误结果(回绕或截断);而在开启优化时,编译器被允许假设"溢出永远不会发生",从而以令人惊讶的方式消除或重排代码。
例如INT32_MAX + 1在理论上本应回绕成-2147483648,但优化编译器可能直接将其当作不可达路径处理,导致行为不可预测。检测器为这类问题分配 CWE-190(整数溢出),见 lib/checktype.cpp。
源码级判定逻辑
checkIntegerOverflow()的核心流程(lib/checktype.cpp)可以概括为以下步骤:
平台前置校验:若平台类型为
Unspecified(未指定),或平台int位宽不小于内部大整数位数MathLib::bigint_bits,则直接返回,无法运行该检查器——因为缺少可信的整型宽度信息。遍历算术运算符:扫描所有 token,仅关注
isArithmeticalOp()命中的运算符节点。确认结果类型:运算符的结果必须是有符号整型(
ValueType::sign == SIGNED);无符号结果天然回绕,不在此检查范围内。读取平台位宽:根据结果类型为
int/long/long long,分别取平台配置的int_bit、long_bit、long_long_bit(来自Platform配置,可通过--platform指定)。计算边界并取溢出值:
maxvalue = (1 << (bits - 1)) - 1,即该类型最大值;- 通过
getValueGE(maxvalue + 1)检测上溢(结果 ≥ 最大值 + 1); - 若未命中,再通过
getValueLE(-maxvalue - 2)检测下溢(结果 ≤ 最小值 - 1)。
特殊放行左移:对
<<运算,若结果值落在(0, 2^bits)区间(即左移进入了符号位,如 32 位int下的1 << 31),不报告。这是刻意为之——"左移进符号位"虽在标准上是未定义行为,但属于业界通行写法,改由 shiftTooManyBits 检查族 负责。上报:调用
integerOverflowError(),根据ValueFlow::Value的condition、safe标志组装消息,并按value.errorSeverity()决定Severity::error还是Severity::warning(lib/checktype.cpp)。
何时不会报告
文档特别强调:cppcheck 只在确实能证明计算会溢出时才告警。判断依据要么是已知常量值,要么是代码中某处条件(对应integerOverflowCond)。对于两个完全不受约束的参数的普通a * b,ValueFlow 拿不到任何可证明溢出的取值区间,因此不会报告。
这一点也在测试中得到印证:return intmax - 1;(已知不溢出)不产生任何告警,见 test/testtype.cpp。
测试用例验证
test/testtype.cpp 中的checkIntegerOverflow测试用例完整覆盖了各类场景,可作为理解该检查项行为的手册:
| 输入代码 | 期望输出 |
|---|---|
x = (int)0x10000 * (int)0x10000; | (error) ... [integerOverflow](32 位平台) |
x = (long)0x10000 * (long)0x10000; | (error) ... [integerOverflow](32 位平台) |
intmax = 0x7fffffff; return intmax + 1; | (error) Signed integer overflow ... 'intmax+1' |
intmax = 0x7fffffff; return intmax - 1; | 无告警(未溢出) |
if (x==123456) {} return x * x; | (warning) Either the condition 'x==123456' is redundant or there is signed integer overflow ... [integerOverflowCond] |
if (x==123456) {} return -123456 * x; | (warning) ... signed integer underflow ... [integerOverflowCond] |
if (x==123456) {} return 123456U * x; | 无告警(结果为无符号) |
循环中n = n * 47163 - 57412;(#13092) | 两处均报(error) Signed integer underflow |
(i == 31) ? 1 << i : 0(#12117) | 报告shiftTooManyBitsSigned,而非 integerOverflow |
注意测试均显式指定了Platform::Type::Unix32,证明位宽假设确实来自平台配置:同一段代码在 32 位平台会溢出,在 64 位平台可能不会,检测结果随--platform变化。
修复方法
对integerOverflow/integerOverflowCond告警,文档给出三种修复思路:
- 改用更宽的类型参与计算——在运算前先扩展类型,避免在窄类型中溢出;
- 重写计算逻辑以避免溢出——例如乘法前先做边界检查(
if (a > INT32_MAX / b) ...); - 如回绕行为正是意图所在,改用无符号类型——无符号算术按标准定义回绕,可消除未定义行为。
修复前后示例(来自 integerOverflow.md):
Before(已知会溢出 32 位 int):
int32_t f() { int32_t intmax = 0x7fffffff; // INT32_MAX return intmax + 1; // <- integerOverflow,确定溢出 32 位 int }After(先扩宽再相加):
int64_t f() { int32_t intmax = 0x7fffffff; return (int64_t)intmax + 1; // <- 加法前先扩宽 }第二例正是测试套件中(int)0x10000 * (int)0x10000报integerOverflow、而(long)0x10000 * (long)0x10000同样报错的同款场景——强制转换先行,使中间结果落入更宽类型,是消除溢出告警最直接的手段。
关联检查器与使用提示
- shiftTooManyBits:左移进符号位(如 32 位
int的1 << 31)被刻意排除在 integerOverflow 之外,由 shiftTooManyBits 检查族覆盖,checkIntegerOverflow()内部的放行逻辑见 lib/checktype.cpp。 - 平台依赖:该检查依赖
--platform(或 GUI 中的平台设置)提供的整型宽度;平台未指定(Unspecified)时检查器直接跳过,见 lib/checktype.cpp。在默认平台下运行结果可能与预期不符,建议显式指定目标平台后再评估告警。 - 符号性前提:只有结果为有符号整型的表达式才会被检查;无符号运算(如测试中的
123456U * x)永不触发本检查。 - 其余同属"未定义行为"类别的整型相关检查还包括浮点到整型转换溢出(
checkFloatToIntegerOverflow,lib/checktype.cpp 起),可在同一 checktype.cpp 文件中继续阅读。
- 开发工具
- 静态分析
- 代码质量
- 质量保障
【免费下载链接】cppcheck
static analysis of C/C++ code
相关推荐
cppcheck floatConversionOverflow 检查器详解:浮点转整数的未定义行为检测
cppcheck floatConversionOverflow 检查器详解:浮点转整数的未定义行为检测 导读 本文深入讲解 cppcheck 的 floatC
开发工具静态分析代码质量质量保障Cppcheck algorithmOutOfBounds 检查器详解:STL 算法输出区间越界的静态检测原理与实战
Cppcheck algorithmOutOfBounds 检查器详解:STL 算法输出区间越界的静态检测原理与实战 导读 本文以 cppcheck 仓库中的检
开发工具静态分析代码质量质量保障cppcheck 检查器详解:funcArgNamesDifferent / funcArgNamesDifferentUnnamed——函数声明与定义参数名不一致检测
cppcheck 检查器详解:funcArgNamesDifferent / funcArgNamesDifferentUnnamed——函数声明与定义参数名不
开发工具静态分析代码质量质量保障
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考