【问题标题】:Valgrind errors on simple C string functions简单 C 字符串函数上的 Valgrind 错误
【发布时间】:2011-11-19 09:15:49
【问题描述】:

让我们考虑这个简单的测试程序:

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
        char buf[256];
        int i;

        strcpy(buf,"Hello world!");
        i = strlen(buf);
        printf("Length of string is %d.\n",i);
        return 0;
}

使用英特尔 c++ 编译器进行编译并启用优化 (O3) 时,我从 valgrind 收到以下错误:

==8727== Conditional jump or move depends on uninitialised value(s)
==8727==    at 0x4009EF: main (strtest.cpp:11)
==8727== Use of uninitialised value of size 8
==8727==    at 0x4FC61ED: _itoa_word (in /lib64/libc-2.4.so)
==8727==    by 0x4FC9317: vfprintf (in /lib64/libc-2.4.so)
==8727==    by 0x4FD02A9: printf (in /lib64/libc-2.4.so)
==8727==    by 0x400A09: main (strtest.cpp:13)
==8727== Conditional jump or move depends on uninitialised value(s)
==8727==    at 0x4FC61F7: _itoa_word (in /lib64/libc-2.4.so)
==8727==    by 0x4FC9317: vfprintf (in /lib64/libc-2.4.so)
==8727==    by 0x4FD02A9: printf (in /lib64/libc-2.4.so)
==8727==    by 0x400A09: main (strtest.cpp:13)
==8727== Conditional jump or move depends on uninitialised value(s)
==8727==    at 0x4FC9386: vfprintf (in /lib64/libc-2.4.so)
==8727==    by 0x4FD02A9: printf (in /lib64/libc-2.4.so)
==8727==    by 0x400A09: main (strtest.cpp:13)
==8727== Conditional jump or move depends on uninitialised value(s)
==8727==    at 0x4FC990F: vfprintf (in /lib64/libc-2.4.so)
==8727==    by 0x4FD02A9: printf (in /lib64/libc-2.4.so)
==8727==    by 0x400A09: main (strtest.cpp:13)
==8727== Conditional jump or move depends on uninitialised value(s)
==8727==    at 0x4FC82F2: vfprintf (in /lib64/libc-2.4.so)
==8727==    by 0x4FD02A9: printf (in /lib64/libc-2.4.so)
==8727==    by 0x400A09: main (strtest.cpp:13)

我正在使用最新版本的 valgrind (3.6.1)。关闭优化 (-O0) 时不会发生这种情况,g++ 也不会发生这种情况。但是,它出现在我迄今为止尝试过的所有英特尔编译器(11.0、11.1、12)中。

似乎错误与字符串函数的 SIMD 加速有关,如C strings, strlen and Valgrind 中所述。

据说这是 valgrind 中的一个错误,现在已修复。但是,尽管使用了最新的 valgrind 版本,我仍然有这些错误。有人知道这方面的帮助吗?

【问题讨论】:

    标签: c string valgrind strlen


    【解决方案1】:

    不要使用 valgrind 进行优化。

    【讨论】:

      【解决方案2】:

      Valgrind 试图确定一个值是否依赖于初始化的内存,这通常不是一个容易处理的问题。 Valgrind 通过跟踪设置了哪些位并允许它们级联来“尽最大努力”。有很多方法可以欺骗它。例如,我刚刚编写了这段代码:

      #include <stdlib.h>
      
      int main(int argc, char *argv[])
      {
          unsigned *p = malloc(sizeof(unsigned));
          unsigned x = *p;
          free(p);
          unsigned f = x == 0;
          unsigned g = x == 1;
          return f & g;
      }
      

      旁注: 严格来说,上述程序在任何没有unsigned int 陷阱表示的平台上都是正确,在我们的平台上也是如此(Valgrind 仅适用于 x86)。在这些平台上不会调用未定义的行为,C 标准保证 main 在这些平台上返回 0。

      引用: n1256: 7.20.3.3: malloc 返回值为“不确定”的对象。 n1256 3.17.2:不确定值是“陷阱表示”或“未指定值”。请注意,在 x86 上,无符号整数没有陷阱表示。

      根据 Valgrind 的说法,fg 都没有被正确初始化,所以 f &amp; g 也不能被初始化。然而,结果总是零,任何有一点逻辑的人都会告诉你。 Valgrind 不懂逻辑,它只是按照一些简单的规则到处乱跑。

      这里可能发生的情况是,英特尔的 C 编译器为您提供了 strlen 的优化版本,它使用 SSE 或某种技巧,导致 Valgrind 中的“未初始化”位在结果中被设置。当然,如果优化后的代码读过bufinitialized 部分,然后执行一系列类似上述的操作,这自然会发生。当然,它会做类似的事情,因为这样做会更快(并且在 x86 上读取数组末尾总是安全的,只要您不跨越页面边界)。你有一些选择。

      • 在使用 Valgrind 和 Intel 的编译器时关闭优化。

      • 将代码添加到 Valgrind 以捕获这种特殊类型的错误。 (Valgrind 已经有特殊情况了。)

      • 有条件地修改你的代码,让 Valgrind 给出正确的结果。例如,将其放在顶部:

        // This only fixes the error if sizeof(buf) is at least as large
        // as the largest multiple of 16 larger than strlen(buf)
        #if VALGRIND
        memset(buf, '\0', sizeof(buf));
        #endif
        

      【讨论】:

      • unsigned x = *p; 是未定义的行为,因此 x 未正确初始化。在那之后,编译器可以有效地做他想做的任何事情,而且他似乎做到了。这不是 valgrind 的错,而是你的错。
      • @Jens Gustedt:这不是“未定义的行为”,而是完全不同的“不确定值”,可能是陷阱表示。在这种情况下,编译器不能“为所欲为”,而必须严格遵循 POSIX 和 C 标准。事实上,该程序在任何没有无符号整数陷阱表示的平台上都符合 C 标准。这包括所有 POSIX 平台。
      • @SoapBox:你错了。错误发生在printf 内部,但它们是由strlen 引起的。您可以将 i = 12; 放在 printf 之前,错误消息就会消失,即使我已经 12 岁了。这是因为 Valgrind 错误地认为 i 未初始化,即使它是实际上初始化为值 12。这是由于 Valgrind 对strlen 的错误解释造成的。所以printf与它无关,你可以将printf替换为exit(i),你也会得到一个错误。
      • 我避免在我之前的声明中说这是 C 中未定义的行为,与实现无关。它似乎只是 C++ 中未定义的行为。
      • 然而,在 C89 中它似乎是未定义的。它说未定义的行为是“在使用不可移植或错误的程序结构、错误数据或不确定值对象时的行为,标准对此没有任何要求”。 C99 似乎改变了这一点。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多