我正在尝试找到一些有效的技巧,我可以将integer-overflow
检测工具作为基础。我知道那里有许多现成的检测工具,但我正在尝试自己实现一个简单的检测工具,这既是为了我个人对这方面的兴趣,也是为了我的知识。
我知道像Pattern Matching
和Type Inference
这样的技术,但我读到需要更复杂的代码分析技术来检测int溢出。还有Taint Analysis
可以“标记”不受信任的数据源。
还有其他技术,我可能不知道,它能够检测整数溢出吗?
答案 0 :(得分:2)
可能值得尝试使用cppcheck
静态分析工具claims来检测版本1.67的有符号整数溢出:
新检查:
- 通过太多位检测移位,有符号整数溢出和危险符号转换
请注意,它支持C和C ++语言。
无符号整数没有溢出检查,因为标准无符号类型永远不会溢出。
以下是一些基本示例:
#include <stdio.h>
int main(void)
{
int a = 2147483647;
a = a + 1;
printf("%d\n", a);
return 0;
}
使用这样的代码:
$ ./cppcheck --platform=unix64 simple.c
Checking simple.c...
[simple.c:6]: (error) Signed integer overflow for expression 'a+1'
然而,我不会期待太多(至少在当前版本中),因为程序略有不同:
int a = 2147483647;
a++;
在没有注意到溢出的情况下通过。
答案 1 :(得分:2)
您似乎正在寻找某种值范围分析,并检测该范围何时超出设定范围。从表面上看,这似乎很简单,但实际上很难。会有很多误报,甚至没有计算实施中的错误。
暂时忽略细节,将一对[lower bound, upper bound]
与每个变量相关联,并做一些数学计算以确定每个运算符的新边界。例如,如果代码添加了两个变量,则在分析中将上限添加到一起以形成新的上限,并将下限添加到一起以获得新的下限。
但当然不是那么简单。首先,如果有非直线代码怎么办? if
并不是太糟糕,你可以只评估双方,然后在它之后取得范围的并集(这可能会丢失信息!如果两个范围之间存在差距,它们的联合将跨越差距) 。循环需要技巧,一个天真的实现可能会在循环上运行数十亿次迭代分析,甚至根本不会终止。即使您使用没有无限提升链的抽象域,您仍然可能遇到麻烦。解决这个问题的关键词是“拓宽运算符”和(可选地,但可能是一个好主意)“缩小运算符”。
甚至比这更糟糕,因为什么是变量?你从未获取地址的标量类型的常规局部变量也不错。但阵列怎么样?现在你甚至不确定哪个条目受到影响 - 索引本身可能是一个范围!然后就是别名。这远非一个已解决的问题,并导致许多现实世界的工具做出非常悲观的假设。
另外,函数调用。你将从一些上下文调用函数,希望是一个已知的函数(如果没有,那么它很简单:你什么都不知道)。这使得它很难,不仅突然有更多状态同时跟踪,可能有几个地方可以调用一个函数,包括本身。通常的反应是在扩展其中一个参数的范围时重新评估该函数,如果不仔细进行,这可能需要数十亿步。还有一些算法可以针对不同的上下文不同地分析函数,这可以提供更准确的结果,但是很容易花费大量时间来分析那些不重要的上下文。
无论如何,如果你已经做到这一点,你可以阅读Accurate Static Branch Prediction by Value Range Propagation及相关论文,以便更好地了解如何实现这一目标。
并非全部。仅考虑单个变量的范围而不关心(关键字:非关系抽象域)之间的关系,它们对于非常简单(对于人类读者)的事情是不好的,例如减去总是在值中靠近的两个变量,为此假设它们可能与它们的边界允许的距离相差很远,它们将会产生很大的范围。即使是一些微不足道的事情,比如
; assume x in [0 .. 10]
int y = x + 2;
int diff = y - x;
对于人类读者来说,很明显diff = 2
。在到目前为止所描述的分析中,结论将是y in [2 .. 12]
和diff in [-8, 12]
。现在假设代码继续
int foo = diff + 2;
int bar = foo - diff;
现在我们得到foo in [-6, 14]
和bar in [-18, 22]
即使bar
显然是2,但范围再次加倍。现在这是一个简单的例子,你可以编造一些特殊的黑客来检测它,但这是一个更普遍的问题。这种效应往往会迅速炸毁变量范围并产生许多不必要的警告。部分解决方案是为变量之间的差异分配范围,然后您获得所谓的差异约束矩阵(毫不奇怪,这是关系抽象域的示例)。对于过程间分析,它们可能会变得很大而且很慢,或者如果你想在它们上面抛出非标量变量,那么算法就会变得更加复杂。它们只能让你到目前为止 - 如果你在混合中加入一个乘法(包括x + x
和变体),事情仍然会很快变坏。
所以你可以在混合中抛出其他可以用常数处理乘法的东西,例如参见Abstract Domains of Affine Relations⋆ - 这与范围非常不同,并且它本身不会告诉你很多关于你的范围变量,但您可以使用它来获得更准确的范围。
故事并没有就此结束,但这个答案越来越长。我希望这不会阻止你研究这个主题,这个主题非常适合开始简单并为分析工具添加越来越多有趣的东西。
答案 2 :(得分:1)
检查C中的整数溢出:
当您添加两个32位数字并获得33位结果时,低32位将写入目标,最高位将作为进位标志发出信号。许多语言(包括C)都没有提供访问此“进位”的方法,因此您可以在执行算术运算之前使用限制,即<limits.h>
进行检查。考虑 unsigned int s a
和b
:
如果MAX - b < a
,我们肯定知道a + b
会导致溢出。这个C FAQ给出了一个例子。
注意: 正如chux指出的那样,这个示例在签名整数方面存在问题,因为它无法处理MAX - b
或MIN + b
如果b < 0
。第二个链接(下面)中的示例解决方案涵盖了所有情况。
乘以数字也会导致溢出。解决方案是将第一个数字的长度加倍,然后进行乘法运算。类似的东西:
(typecast)a*b
注意: (typecast)(a*b)
是不正确的,因为它先截断然后截断。
可以找到c的详细技术HERE。使用宏似乎是一个简单而优雅的解决方案。
答案 3 :(得分:0)
我希望Frama-C能够提供这样的功能。 Frama-C专注于C源代码,但我不知道它是否是方言敏感或特定的。我相信它使用抽象解释来模拟价值观。我不知道它是否专门检查溢出。
我们的DMS Software Reengineering Toolkit有各种各样的语言前端,包括C的大多数主要方言。它提供控制和数据流分析,以及计算范围的抽象解释,作为您可以建立答案的基础。我的Google Tech Talk on DMS在大约0:28:30专门讨论了如何使用DMS对值范围的抽象解释来检测溢出(缓冲区上的索引)。检查数组大小上限的变化只是检查不超过2 ^ N的值。但是,现成的DMS不为C代码提供任何特定的溢出分析。 OP有空间做有趣的工作:=}