简单 C 字符串函数上的 Valgrind 错误

发布于 2024-12-05 06:15:53 字数 2028 浏览 1 评论 0原文

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

#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;
}

当使用 Intel 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 字符串中讨论的那样, strlen 和 Valgrind。

那里指出这是 valgrind 中的一个错误,现已修复。然而,尽管使用了最新的 valgrind 版本,我仍然遇到这些错误。有人知道这方面的一些帮助吗?

Let's consider this simple test program:

#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;
}

When compiling it with the Intel c++ compiler and optimizations turned on (O3), I get the following errors from 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)

I am using the most recent version of valgrind (3.6.1). This does not happen when turning optimizations off (-O0), and it does not happen with g++. However, it appears with all Intel compilers I have tried out so far (11.0, 11.1, 12).

It seems that the errors are related to SIMD-acceleration of the string functions, like discussed in C strings, strlen and Valgrind.

There it was stated that this was a bug in valgrind and is fixed now. However, although using the newest valgrind version, I still have these errors. Does anybody know some help about this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

青朷 2024-12-12 06:15:53

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 标准保证在此类平台上返回 0。

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

根据 Valgrind 的说法,fg 都没有正确初始化,因此 f & g 也无法初始化。然而,结果总是为零,任何有一点逻辑的人都会告诉你。 Valgrind 不理解逻辑,它只是根据一些简单的规则遵循位。

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

  • 使用 Valgrind 和英特尔编译器时关闭优化。

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

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

    // 仅当 sizeof(buf) 至少一样大时才修复错误
    // 作为比 strlen(buf) 大的 16 的最大倍数
    #if VALGRIND
    memset(buf, '\0', sizeof(buf));
    #endif
    

Valgrind is trying to determine whether a value depends on initialized memory or not, which is not a tractable problem in general. Valgrind does a "best effort" by tracking which bits are set, and allowing them to cascade. There are many ways to fool it. For example, I just cooked up this code:

#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;
}

Side Note: The above program, strictly speaking, is correct on any platform that does not have trap representations for an unsigned int, which is true on our platform (Valgrind is x86 only). Undefined behavior is not invoked on these platforms, and main is guaranteed by the C standard to return 0 on such platforms.

Citation: n1256: 7.20.3.3: malloc returns objects whose values are "indeterminate". n1256 3.17.2: An indeterminate value is either a "trap representation" or an "unspecified value". Note that on x86 there are no trap representations for unsigned integers.

According to Valgrind, neither f nor g are properly initialized, so f & g cannot be initialized either. However, the result is always zero, as anybody with an ounce of logic will tell you. Valgrind does not understand logic, it just follows bits around according to some simple rules.

What is probably happening here is that Intel's C compiler gave you an opitimized version of strlen which uses SSE or some kind of trick, which caused the "uninitialized" bits in Valgrind to get set in the result. This would naturally happen, of course, if the optimized code read past the initialized part of buf and then performed an series of operations like those above. And of course, it would do something like that, because it's faster to do it that way (and it's always safe to read past the end of arrays on x86, as long as you don't cross a page boundary). You have some options.

  • Turn off optimizations when using Valgrind and Intel's compiler.

  • Add code to Valgrind to catch this particular kind of error. (Valgrind has special cases in it already.)

  • Modify your code conditionally to get Valgrind to give the right result. For example, put this at the top:

    // 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
    
寄与心 2024-12-12 06:15:53

不要使用 valgrind 进行优化。

Don't use valgrind with optimizations.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文