如何在C程序中找到(所有)整数溢出?

17

我正在开发一个大型项目,一般情况下工作正常,但是一旦输入数据超出某些限制,就会出现严重问题。

这些问题(疑似)只是由于带符号整数溢出引起的,例如:

int a, o;
// Initialize a and o
int x = (a+o) >> 1);

显然,一旦a和o的总和溢出(大于2^31-1),x就不再是a和o的平均值。

有没有通用的方法可以在运行中的程序中找到所有这些整数溢出?

我考虑使用像Valgrind或GDB扩展这样的工具,在每个整数算术指令处断点,获取参数并将正确结果(使用更大的数据类型或任意精度算术计算)与实际结果进行比较。如果结果不同,它应该输出警告,触发调试中断或类似操作。

我知道如何检查单个算术指令是否溢出(例如检查加法的符号),但由于代码量巨大,手动在整个项目中插入检查代码并不可行。

3个回答

3
你需要仔细检查所有的代码,确定用户输入的限制并验证其输入。你可能还需要重新编写一些算法以减少溢出问题。
由于你所给出的示例不能处理负数值,因此你应该使用无符号整型unsigned int,这样可以增加一个数量级。 编辑: gcc有-ftrapv选项,但这通常不会实际起到任何作用,只有在使用-O0时才有效。如果你采取捕获溢出的方法,你仍然需要全面了解代码才能进行完整的测试。

我大致知道用户输入的限制是什么。但是,通过代码进行工作的问题在于,该项目有超过200k行代码(其中199k行不是由我编写的),我担心手动检查这些代码是否存在溢出风险将非常困难。 - ChrisM
@ChrisM - 如果你处于一个不能相信别人编写的代码是正确的位置,那么你可能不应该使用它。业务实践应该被采用来处理这类事情(审查、测试等)。如果只有你自己编写了这段代码并且周围没有其他人,那么你需要花费一些时间阅读它。 - OrangeDog
好的,这很遗憾。我只是希望能够自动找到代码中有问题的部分(可能有十几个或更多)。 - ChrisM
@ChrisM - 你可以在gcc中设置条件断点以捕获意外值,但这仍然需要知道所有参与算术运算的变量的位置。 - OrangeDog
1
-ftrapv 的“通常实际上不执行任何操作”警告只适用于使用优化编译的情况。使用 -O0 时,它应该可以正常工作。 - caf
1
@caf:即使使用 -O0,它对我仍然无效。我通过比较使用 -ftrapv 生成的二进制文件和未使用 -ftrapv 的二进制文件发现它们完全相同。 - ChrisM

3

对于大型代码库,Coverity是一个很好的工具。我不确定它是否能检测到所有整数溢出,但值得一试。


0
如何编写一个脚本,可以遍历代码并将所有的“a+b”替换为DEBUGADD(a,b) - 这样你就可以这样做:
#ifdef DEBUG
int addFn(int a, int b) {
  long long m;
  int n;
  m = (long long)a + (long long)b;
  n = a + b;
  if (m != (long long)n)
    printf("PANIC!\n");
  return n;
}
#define DEBUGADD(a,b) addFn(a,b)
#else
#define DEBUGADD(a,b) ((a)+(b))
#endif

4
请确保你的脚本正确处理空格、强制类型转换、运算顺序和返回类型,但不要改变原来的意思。 :) - Karmastan
更好的写法是 assert(((a <= 0) || (b <= INT_MAX - a)) && ((a >= 0) || (b >= INT_MIN - a))),因为类似的表达式也适用于 longlong long - caf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接