简单 C 字符串函数上的 Valgrind 错误 [英] Valgrind errors on simple C string functions

查看:29
本文介绍了简单 C 字符串函数上的 Valgrind 错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

#include #include int main(int argc, char *argv[]){字符缓冲区[256];国际我;strcpy(buf,"Hello world!");我 = strlen(buf);printf("字符串长度为 %d.\n",i);返回0;}

在使用 Intel C++ 编译器并打开优化 (O3) 对其进行编译时,我从 valgrind 收到以下错误:

==8727== 条件跳转或移动取决于未初始化的值==8727== 在 0x4009EF: main (strtest.cpp:11)==8727== 使用大小为 8 的未初始化值==8727== 在 0x4FC61ED:_itoa_word(在/lib64/libc-2.4.so 中)==8727== 由 0x4FC9317:vfprintf(在/lib64/libc-2.4.so 中)==8727== by 0x4FD02A9:printf(在/lib64/libc-2.4.so 中)==8727== by 0x400A09: main (strtest.cpp:13)==8727== 条件跳转或移动取决于未初始化的值==8727== 在 0x4FC61F7:_itoa_word(在/lib64/libc-2.4.so 中)==8727== 由 0x4FC9317:vfprintf(在/lib64/libc-2.4.so 中)==8727== by 0x4FD02A9:printf(在/lib64/libc-2.4.so 中)==8727== by 0x400A09: main (strtest.cpp:13)==8727== 条件跳转或移动取决于未初始化的值==8727== 在 0x4FC9386:vfprintf(在/lib64/libc-2.4.so 中)==8727== by 0x4FD02A9:printf(在/lib64/libc-2.4.so 中)==8727== by 0x400A09: main (strtest.cpp:13)==8727== 条件跳转或移动取决于未初始化的值==8727== 在 0x4FC990F:vfprintf(在/lib64/libc-2.4.so 中)==8727== by 0x4FD02A9:printf(在/lib64/libc-2.4.so 中)==8727== by 0x400A09: main (strtest.cpp:13)==8727== 条件跳转或移动取决于未初始化的值==8727== 在 0x4FC82F2:vfprintf(在/lib64/libc-2.4.so 中)==8727== by 0x4FD02A9:printf(在/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 版本,但我仍然有这些错误.有人知道这方面的帮助吗?

解决方案

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

#include int main(int argc, char *argv[]){无符号 *p = malloc(sizeof(unsigned));无符号 x = *p;免费(p);无符号 f = x == 0;无符号 g = x == 1;返回 f &G;}

<块引用>

旁注:严格来说,上述程序在任何没有 unsigned int 陷阱表示的平台上正确,这在我们的平台上是正确的(Valgrind 仅适用于 x86).在这些平台上不会调用未定义的行为,并且 C 标准保证 main 在这些平台上返回 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 VALGRINDmemset(buf, '\0', sizeof(buf));#万一

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?

解决方案

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
    

这篇关于简单 C 字符串函数上的 Valgrind 错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆