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

时间:2011-06-13 21:10:38

标签: c++ 64-bit portability 32bit-64bit

我在一些(C ++)代码中找到了与此类似的代码片段,我正准备使用64位端口。

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表达式,因此这不像其他循环那样简单(类型转换错误会导致错误)。任何工具都需要确定循环执行超过2 31 次,但这意味着它需要能够估计表达式(pos = find(ch, start)) != npos将被评估为真的次数 - 否小壮举!即使工具可以确定循环可以执行超过2次 31 次(例如,因为它识别find函数正在处理字符串),怎么知道循环不会执行超过2 64 次,也会溢出size_t值?

似乎很清楚,最终确定并修复此类错误需要人眼,但是有哪些模式可以消除这种错误,因此可以手动检查?我应该注意哪些类似的错误?

编辑1:由于shortintlong类型本质上存在问题,因此可以通过检查这些类型的每个实例来找到此类错误。但是,鉴于它们在传统C ++代码中无处不在,我不确定这对于大型软件是否实用。还有什么可以解决这个错误?每个while循环是否可能会出现某种错误? (for循环肯定不会免疫它!)如果我们不处理像short这样的16位类型,这种错误有多糟糕?

编辑2:这是另一个示例,显示了此错误在for循环中的显示方式。

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

这基本上是同样的问题:循环正在指望一些永远不会与更广泛类型直接交互的变量。变量仍然可以溢出,但没有编译器或工具检测到转换错误。 (严格来说,没有。)

编辑3 我正在使用的代码非常大。 (单独使用C ++的代码为1000万到1500万行。)检查所有代码是不可行的,因此我特别感兴趣的是如何自动识别这类问题(即使它导致高误报率)。

3 个答案:

答案 0 :(得分:1)

代码评论。让一群聪明的人看着代码。

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

这个例子应该是:

typedef int_fast32_t linecount_appt;
linecount_appt n;

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

答案 1 :(得分:1)

它清楚你需要的是一个智能的“范围”分析器工具,用于确定计算的值范围与存储这些值的类型。 (您的基本反对意见是智能范围分析仪是一个人)。您可能需要一些额外的代码注释(手动放置良好的typedef或提供显式范围约束的断言)以实现良好的分析,并处理其他任意大的用户输入。

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

我们的DMS Software Reengineering Toolkit实际上内置了范围分析机制......但它目前还没有连接到DMS的C ++前端;我们只能这么快地兜售: - {[我们在COBOL程序中使用它来处理涉及范围的不同问题]。

如果没有这样的范围分析,您可能会检测到具有此类依赖流的循环的存在; n的值明显取决于循环计数。我怀疑这会让你在程序中的每个循环都产生副作用,这可能没那么多帮助。

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

答案 2 :(得分:0)

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