如何检测难以捉摸的64位可移植性问题

How can elusive 64-bit portability issues be detected?

本文关键字:64位 可移植性 问题 难以捉摸 何检测 检测      更新时间:2023-10-16

我在为64位端口准备的一些(C++)代码中发现了类似的片段

int n;
size_t pos, npos;
/* ... initialization ... */
while((pos = find(ch, start)) != npos)
{
    /* ... advance start position ... */
    n++; // this will overflow if the loop iterates too many times
}

虽然我非常怀疑这是否会在内存密集型应用程序中造成问题,但从理论角度来看是值得的,因为类似的错误可能会出现,导致问题。(在上面的例子中,将n更改为short,即使是小文件也可能溢出计数器。)

静态分析工具很有用,但它们不能直接检测这种错误。(无论如何,还没有。)计数器n根本不参与while表达式,所以这不像其他循环那样简单(类型转换错误会泄露错误)。任何工具都需要确定循环将执行231次以上,但这意味着它需要能够估计表达式(pos = find(ch, start)) != npos将被求值为true的次数——这不是一个小壮举!即使一个工具可以确定循环执行的次数可以超过231次(比如说,因为它识别出find函数正在字符串上工作),它怎么能知道循环不会执行264多次,也会溢出size_t值呢?

很明显,要最终识别和修复这种错误需要人眼,但是否有模式会泄露这种错误,以便手动检查?还有什么类似的错误我应该警惕?

EDIT 1:由于shortintlong类型本身就有问题,因此可以通过检查这些类型的每个实例来发现这种错误。然而,考虑到它们在遗留C++代码中的普遍性,我不确定这是否适用于大型软件。还有什么原因导致了这个错误?每个while循环都可能出现这样的错误吗?(for循环当然不能幸免!)如果我们不处理像short这样的16位类型,这种错误有多严重?

EDIT 2:下面是另一个示例,显示此错误如何出现在for循环中。

int i = 0;
for (iter = c.begin(); iter != c.end(); iter++, i++)
{
    /* ... */
}

从根本上讲,这是一个相同的问题:循环依赖于一些从未与更广泛的类型直接交互的变量。变量仍然可能溢出,但没有编译器或工具检测到强制转换错误。(严格来说,没有。)

第3版:我正在使用的代码非常大。(仅C++就有10-15万行代码。)检查所有代码是不可行的,所以我特别感兴趣的是自动识别这类问题的方法(即使它会导致高假阳性率)。

代码审查。让一群聪明人看看代码。

使用shortintlong是一个警告信号,因为标准中没有定义这些类型的范围。大多数用法应该在<stdint.h>中更改为新的int_fastN_t类型,用于处理到intN_t的序列化。实际上,这些<stdint.h>类型应该用于typedef新的应用程序特定类型。

这个例子应该是:

typedef int_fast32_t linecount_appt;
linecount_appt n;

这表达了一种设计假设,即行数适合32位,并且如果设计要求发生变化,也可以很容易地修复代码。

很明显,您需要一个智能的"范围"分析工具来确定计算的值的范围与存储这些值的类型。(您的根本反对意见是智能测距仪是一个人)。您可能需要一些额外的代码注释(手动放置良好的typedef或提供明确范围约束的断言)来实现良好的分析,并处理明显任意大的用户输入。

您需要进行特殊的检查来处理C/C++表示算术是合法的但愚蠢的地方(例如,假设您不想要[twos补码]溢出)。对于n++示例,(相当于n_after=n_before+1),n_before可以是2^31-1(因为你对字符串的观察),所以n_before+1可以是2^2,这是溢出。(我认为标准的C/C++语义说溢出到-0而不抱怨是可以的)。

事实上,我们的DMS软件重组工具包内置了范围分析机制……但目前它还没有连接到DMS的C++前端;我们只能这么快地兜售:-{[我们已经在COBOL程序中使用了它来解决涉及范围的不同问题]。

在没有这种范围分析的情况下,您可能会检测到具有这种依赖流的循环的存在;n的值显然取决于循环计数。我怀疑这会让程序中的每个循环都产生副作用,这可能没有多大帮助。

另一个海报建议以某种方式使用特定于应用程序的类型(例如*linecount_appt*)重新声明所有类似int的声明,然后对这些声明进行类型定义,以确定对应用程序有效的值。要做到这一点,我认为您必须将每个类似int的声明分类(例如,"这些声明都是*linecount_appt*")。通过对10M SLOC进行手动检查似乎非常困难,而且非常容易出错。查找所有从"相同"值源接收(通过赋值)值的声明可能是获得有关此类应用程序类型所在位置的提示的一种方法。您希望能够机械地找到这样的声明组,然后使用一些工具自动将实际声明替换为指定的应用程序类型(例如*linecount_appt*)。这可能比进行精确的范围分析要容易一些。

有一些工具可以帮助查找此类问题。我不会在这里给出任何链接,因为我知道的那些都是商业链接,但应该很容易找到。