我是否需要担心 Valgrind 报告超出我的应用程序范围的错误?
当运行 Valgrind 的 memcheck 工具时,我经常会收到数十万(或更多,因为 Valgrind 在 100K 处截止)小的无效读取语句,例如:
==32027== Invalid read of size 1
==32027== at 0x3AB426E26A: _IO_default_xsputn (in /lib64/libc-2.5.so)
==32027== by 0x3AB426CF70: _IO_file_xsputn@@GLIBC_2.2.5 (in /lib64/libc-2.5.so)
==32027== by 0x3AB42621FA: fwrite (in /lib64/libc-2.5.so)
==32027== by 0x4018CA: STARCH_gzip_deflate (in /home/areynolds/trunk/utility/applications/bed/starch/bin/starch)
==32027== by 0x401F48: compressFileWithGzip (in /home/areynolds/trunk/utility/applications/bed/starch/bin/starch)
==32027== by 0x4028B5: transformInput (in /home/areynolds/trunk/utility/applications/bed/starch/bin/starch)
==32027== by 0x402F12: main (in /home/areynolds/trunk/utility/applications/bed/starch/bin/starch)
==32027== Address 0x7febb9b3c is on thread 1's stack
这些语句引用了对我的应用程序外部函数的调用(“starch
”)并且它似乎是 libc
的一部分。这是我需要关心的事情吗?
编辑
如果我修改fwrite
调用以删除一个字节,那么我的gzip流就会损坏。这是原始代码:
int STARCH_gzip_deflate(FILE *source, FILE *dest, int level) {
int ret, flush;
unsigned have;
z_stream strm;
unsigned char in[STARCH_Z_CHUNK];
unsigned char out[STARCH_Z_CHUNK];
/* initialize deflate state */
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
/* deflateInit2 allows creation of archive with gzip header, i.e. a gzip file */
/* cf. http://www.zlib.net/manual.html */
ret = deflateInit2(&strm, level, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY);
if (ret != Z_OK)
return ret;
/* compress until end of file */
do {
strm.avail_in = fread(in, 1, STARCH_Z_CHUNK, source);
if (ferror(source)) {
(void)deflateEnd(&strm);
return Z_ERRNO;
}
flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
strm.next_in = in;
do {
strm.avail_out = STARCH_Z_CHUNK;
strm.next_out = out;
ret = deflate(&strm, flush);
assert(ret != Z_STREAM_ERROR);
have = STARCH_Z_CHUNK - strm.avail_out;
/* invalid read happens here */
if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
(void)deflateEnd(&strm);
return Z_ERRNO;
}
} while (strm.avail_out == 0);
assert(strm.avail_in == 0);
} while (flush != Z_FINISH);
assert(ret == Z_STREAM_END);
/* clean up and return */
(void)deflateEnd(&strm);
return Z_OK;
}
EDIT 2
我想我看到了问题。我有 in[STARCH_Z_CHUNK]
而不是 in[STARCH_Z_CHUNK + 1]
(对于 out[]
也是如此)。如果我按 -1
调整 fread
和 fwrite
语句,我似乎不会得到那些 Invalid read of size 1
语句,尽管我仍然看到很多特定于 zlib
的 Invalid read of size 4
和 8
:
==32624== Invalid read of size 4
==32624== at 0x3AB5206455: deflateInit2_ (in /usr/lib64/libz.so.1.2.3)
==32624== by 0x40180E: STARCH_gzip_deflate (in /home/areynolds/trunk/utility/applications/bed/starch/bin/starch)
==32624== by 0x401F48: compressFileWithGzip (in /home/areynolds/trunk/utility/applications/bed/starch/bin/starch)
==32624== by 0x402C03: transformInput (in /home/areynolds/trunk/utility/applications/bed/starch/bin/starch)
==32624== by 0x402F12: main (in /home/areynolds/trunk/utility/applications/bed/starch/bin/starch)
==32624== Address 0x7feafde38 is on thread 1's stack
编辑3
我正在使用-g
重新编译,如上所述,它确实将行号与错误相关联。
但我只是对 argv[]
变量进行简单的 strncpy
,例如:
strncpy(uniqTag, argv[2], strlen(argv[2]) + 1);
这应该复制以 null 结尾的 argv[2]
字符串到 uniqTag
,但 valgrind
仍然将其标记为错误。
编辑 4
这是错误消息:
==3682== Invalid read of size 1
==3682== at 0x4A081C1: strncpy (mc_replace_strmem.c:329)
==3682== by 0x4022F1: parseCommandLineInputs (starch.c:589)
==3682== by 0x402F20: main (starch.c:46)
==3682== Address 0x7fedffe11 is on thread 1's stac
这是两行相关的行; valgrind 说第二行是无效读取:
uniqTag = (char *)malloc(strlen(argv[2]) + 1);
strncpy(uniqTag, argv[2], strlen(argv[2]) + 1);
因为 strlen(argv[2]) + 1 > > strlen(argv[2])
,这应该会产生一个以 null 结尾的 uniqTag
。
When running Valgrind's memcheck
tool, I often get many hundreds of thousands (or more, since Valgrind cuts off at 100K) of small invalid read statements, e.g.:
==32027== Invalid read of size 1
==32027== at 0x3AB426E26A: _IO_default_xsputn (in /lib64/libc-2.5.so)
==32027== by 0x3AB426CF70: _IO_file_xsputn@@GLIBC_2.2.5 (in /lib64/libc-2.5.so)
==32027== by 0x3AB42621FA: fwrite (in /lib64/libc-2.5.so)
==32027== by 0x4018CA: STARCH_gzip_deflate (in /home/areynolds/trunk/utility/applications/bed/starch/bin/starch)
==32027== by 0x401F48: compressFileWithGzip (in /home/areynolds/trunk/utility/applications/bed/starch/bin/starch)
==32027== by 0x4028B5: transformInput (in /home/areynolds/trunk/utility/applications/bed/starch/bin/starch)
==32027== by 0x402F12: main (in /home/areynolds/trunk/utility/applications/bed/starch/bin/starch)
==32027== Address 0x7febb9b3c is on thread 1's stack
These statements refer to calls to functions outside of my application ("starch
") and which appear to be part of libc
. Is this something I need to be concerned with?
EDIT
If I modify the fwrite
call to remove one byte, then my gzip stream gets corrupted. Here's the original code:
int STARCH_gzip_deflate(FILE *source, FILE *dest, int level) {
int ret, flush;
unsigned have;
z_stream strm;
unsigned char in[STARCH_Z_CHUNK];
unsigned char out[STARCH_Z_CHUNK];
/* initialize deflate state */
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
/* deflateInit2 allows creation of archive with gzip header, i.e. a gzip file */
/* cf. http://www.zlib.net/manual.html */
ret = deflateInit2(&strm, level, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY);
if (ret != Z_OK)
return ret;
/* compress until end of file */
do {
strm.avail_in = fread(in, 1, STARCH_Z_CHUNK, source);
if (ferror(source)) {
(void)deflateEnd(&strm);
return Z_ERRNO;
}
flush = feof(source) ? Z_FINISH : Z_NO_FLUSH;
strm.next_in = in;
do {
strm.avail_out = STARCH_Z_CHUNK;
strm.next_out = out;
ret = deflate(&strm, flush);
assert(ret != Z_STREAM_ERROR);
have = STARCH_Z_CHUNK - strm.avail_out;
/* invalid read happens here */
if (fwrite(out, 1, have, dest) != have || ferror(dest)) {
(void)deflateEnd(&strm);
return Z_ERRNO;
}
} while (strm.avail_out == 0);
assert(strm.avail_in == 0);
} while (flush != Z_FINISH);
assert(ret == Z_STREAM_END);
/* clean up and return */
(void)deflateEnd(&strm);
return Z_OK;
}
EDIT 2
I think I see the problem. I have in[STARCH_Z_CHUNK]
and not in[STARCH_Z_CHUNK + 1]
(and likewise for out[]
). If I adjust both of the fread
and fwrite
statements by -1
, I don't seem to get those Invalid read of size 1
statements, although I still see a lot of Invalid read of size 4
and 8
which are specific to zlib
:
==32624== Invalid read of size 4
==32624== at 0x3AB5206455: deflateInit2_ (in /usr/lib64/libz.so.1.2.3)
==32624== by 0x40180E: STARCH_gzip_deflate (in /home/areynolds/trunk/utility/applications/bed/starch/bin/starch)
==32624== by 0x401F48: compressFileWithGzip (in /home/areynolds/trunk/utility/applications/bed/starch/bin/starch)
==32624== by 0x402C03: transformInput (in /home/areynolds/trunk/utility/applications/bed/starch/bin/starch)
==32624== by 0x402F12: main (in /home/areynolds/trunk/utility/applications/bed/starch/bin/starch)
==32624== Address 0x7feafde38 is on thread 1's stack
EDIT 3
I am recompiling with -g
which, as mentioned, does associate line numbers with errors.
But I'm just doing a straightforward strncpy
of argv[]
variables, e.g.:
strncpy(uniqTag, argv[2], strlen(argv[2]) + 1);
This should copy over the null-terminated argv[2]
string to uniqTag
, but valgrind
still marks this as an error.
EDIT 4
Here's the error message:
==3682== Invalid read of size 1
==3682== at 0x4A081C1: strncpy (mc_replace_strmem.c:329)
==3682== by 0x4022F1: parseCommandLineInputs (starch.c:589)
==3682== by 0x402F20: main (starch.c:46)
==3682== Address 0x7fedffe11 is on thread 1's stac
Here are the two relevant lines; valgrind is saying the second line is an invalid read:
uniqTag = (char *)malloc(strlen(argv[2]) + 1);
strncpy(uniqTag, argv[2], strlen(argv[2]) + 1);
Because strlen(argv[2]) + 1 > strlen(argv[2])
, this should result in a null-terminated uniqTag
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在这种情况下,我会说你这样做。 libc 函数参数来自您的程序。我大胆猜测一下,您的代码中存在一个错误,导致 fwrite 读取超出其源缓冲区末尾的一个字节。
编辑:
顺便说一句,这样的小错误通常是看不见的(即您的代码不会崩溃),因为编译器和内存分配器通常会分配特定大小的内存块并将它们对齐在字边缘。这意味着很多时候,您可以在不触发内存保护代码的情况下访问请求的缓冲区末端之后的一个小区域。当然,如果您更改编译器、libc、平台或位数(例如从 64 位更改为 32 位),您的代码可能会中断。
Valgrind 有 libc 中预期错误的抑制列表,通常可以在 /usr/lib64/valgrind/default.supp 或 /usr/lib/valgrind/default.supp 中找到。 valgrind 在 libc 中检测到相当多的问题,其中许多是为了优化代码而故意的,但由于 99% 的情况下都是被测试的代码导致了问题。
EDIT2:
请记住,与大多数调试工具一样,如果您使用调试符号编译代码,Valgrind 将针对其检测到的问题输出无限更有用的信息。它将能够向您指出与问题相关的特定代码行 - 即使它们通常不是实际问题所在。如果您使用 GCC,只需在其选项中添加 -g 即可使用调试符号编译代码。不过,在生产版本中,请记住删除该标志!
In this case I'd say that you do. The libc function arguments come from your program. I'd hazard a guess and say that you have an off by one error in your code that leads fwrite to read one byte past the end of its source buffer.
EDIT:
By the way, such a small error can often remain unseen (i.e. your code does not crash) because both the compiler and the memory allocator usually allocate memory blocks in specific sizes and align them at word edges. This means that many times there is a small region past the requested buffer end that you can access without triggering the memory protection code. Of course your code might just break if you change compiler, libc, platform or bitness (e.g. go from 64 to 32 bit).
Valgrind has suppression lists for expected errors in libc, which you can usually find at /usr/lib64/valgrind/default.supp or /usr/lib/valgrind/default.supp. There are quite a few issues that valgrind detects in libc, many of them intentional in an effort to optimise the code, but due to the suppresions in 99% of the cases it's the tested code that causes the problem.
EDIT2:
Keep in mind that, like most debugging tools, Valgrind will output infinitely more useful information on the issues it detects if you compile your code with debugging symbols. It will be able to point you to specific lines of code that are related to the issue - even if quite often they are not where the actual issue lies. If you use GCC just add -g to its options to compile your code with debugging symbols. In a production release, though, please remember to remove that flag!
您应该跟踪调用堆栈,直到找到属于您的代码并查找错误的根源。在这种情况下,
STARCH_gzip_deflate
似乎在调用fwrite
时使用了错误的内容(可能是错误的FILE *
或您尝试写入的缓冲区) out)这会导致 valgrind 对你吠叫。不过,这可能实际上并不是一个错误,也可能不是您的错误。但很可能是这样。
You should follow down the call stack until you get to some code that is yours and look for the error's origin. In this case,
STARCH_gzip_deflate
appears to be callingfwrite
with something that is bad (probably a badFILE *
or the buffer you are attempting to write out) which is causing the valgrind to bark at you.It is possible that this is not actually an error or that it is not your error, though. But it probably is.