为什么 Valgrind memcheck 不捕获这个 UB?
Why doesn't Valgrind memcheck catch this UB?
如标题所说,我真的需要帮助理解,为什么我的系统(linux mint 19
、GCC-8.0.1
、valgrind-3.13.0
、c17
)将这段代码视为无效代码:
#include <stdio.h>
#include <string.h>
void printThis( const char *const ptr );
int main( void) {
char a[10] = "asds";
char b[10] = "1234567890";
strcpy ( a, b );
printThis( a );
}
void printThis( const char *const ptr ){
printf("Copy completed! : %s\n", ptr );
}
Valgrind 在此处报告问题:
==6973== Memcheck, a memory error detector
==6973== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==6973== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==6973== Command: /home/michi/Templates/Cprogram/bin/Debug/Cprogram
==6973==
==6973== Source and destination overlap in strcpy(0x1ffefffd14, 0x1ffefffd1e)
==6973== at 0x4C32E97: strcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6973== by 0x108724: main (main.c:12)
==6973==
Copy completed! : 1234567890
==6973==
==6973== HEAP SUMMARY:
==6973== in use at exit: 0 bytes in 0 blocks
==6973== total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==6973==
==6973== All heap blocks were freed -- no leaks are possible
==6973==
==6973== For counts of detected and suppressed errors, rerun with: -v
==6973== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
这个有效代码:
#include <stdio.h>
void strcpy2(char *s, char *t);
void printThis( const char *const ptr );
int main( void) {
char a[10] = "asds";
char b[10] = "1234567890";
strcpy2( a, b );
printThis( a );
}
void strcpy2(char *s, char *t) {
while ( ( *(s++) = *(t++) ) );
}
void printThis( const char *const ptr ){
printf("Copy completed! : %s\n", ptr );
}
Valgrind 输出:
==7025== Memcheck, a memory error detector
==7025== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==7025== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==7025== Command: /home/michi/Templates/Cprogram/bin/Debug/Cprogram
==7025==
Copy completed! : 1234567890
==7025==
==7025== HEAP SUMMARY:
==7025== in use at exit: 0 bytes in 0 blocks
==7025== total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==7025==
==7025== All heap blocks were freed -- no leaks are possible
==7025==
==7025== For counts of detected and suppressed errors, rerun with: -v
==7025== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
使用 O0
、O1
、O2
和 O3
以及 GCC 标志编译:
-Wpedantic -std=c17 -Wall -Wextra -Werror -Wstrict-prototypes -Wmissing-prototypes -Wmisleading-indentation -Wduplicated-cond -Wold-style-definition -Wconversion -Wshadow -Winit-self -Wfloat-equal -Wwrite-strings -O0 -g
Valgrind 可以捕获只有某些类型的错误。它无法检测堆栈,因此它不会看到您的 strcpy2
的错误。 OTOH strcpy
被一个检查源和目标是否重叠的版本所取代 - 它可以捕获这个 only 因为 a + 10 == b
in your 编译程序!
要捕获此类错误,请使用 GCC 的 -fsanitize=address
:
% ./a.out
=================================================================
==3368==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff13832a2a at pc 0x557f05344da8 bp 0x7fff13832990 sp 0x7fff13832980
READ of size 1 at 0x7fff13832a2a thread T0
#0 0x557f05344da7 in strcpy2 (/a.out+0xda7)
#1 0x557f05344cca in main (/a.out+0xcca)
#2 0x7f2d400e5b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
#3 0x557f05344a49 in _start (/a.out+0xa49)
Address 0x7fff13832a2a is located in stack of thread T0 at offset 106 in frame
#0 0x557f05344b39 in main (/a.out+0xb39)
This frame has 2 object(s):
[32, 42) 'a'
[96, 106) 'b' <== Memory access at offset 106 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
[ ... many more lines follow ... ]
我更新了标题,以便更准确地说明所使用的 Valgrind 工具。
Valgrind 可以 检测到此类错误,但如前所述,memcheck
.
检测不到
如果我 运行 使用 Valgrind exp-sgcheck
这个示例代码(exp = 实验性的,sgcheck = 堆栈和全局检查)
valgrind --tool=exp-sgcheck ./so19
然后我得到
==25056== Invalid read of size 1
==25056== at 0x40059D: strcpy2 (so19.c:23)
==25056== by 0x400564: main (so19.c:18)
==25056== Address 0x1ffeffed1a expected vs actual:
==25056== Expected: stack array "b" of size 10 in frame 1 back from here
==25056== Actual: unknown
==25056== Actual: is 0 after Expected
==25056==
==25056== Invalid write of size 1
==25056== at 0x4005A0: strcpy2 (so19.c:23)
==25056== by 0x400564: main (so19.c:18)
==25056== Address 0x1ffeffed2a expected vs actual:
==25056== Expected: stack array "a" of size 10 in frame 1 back from here
==25056== Actual: unknown
==25056== Actual: is 0 after Expected
==25056==
==25056== Invalid read of size 1
==25056== at 0x4005A2: strcpy2 (so19.c:23)
==25056== by 0x400564: main (so19.c:18)
==25056== Address 0x1ffeffed2a expected vs actual:
==25056== Expected: stack array "a" of size 10 in frame 1 back from here
==25056== Actual: unknown
==25056== Actual: is 0 after Expected
==25056==
==25056== Invalid read of size 1
==25056== at 0x4E74C9C: vfprintf (in /lib64/libc-2.12.so)
==25056== by 0x4E7BFF9: printf (in /lib64/libc-2.12.so)
==25056== by 0x4005CD: printThis (so19.c:27)
==25056== by 0x400570: main (so19.c:19)
==25056== Address 0x1ffeffed2a expected vs actual:
==25056== Expected: stack array "a" of size 10 in frame 3 back from here
==25056== Actual: unknown
==25056== Actual: is 0 after Expected
==25056==
==25056== Invalid read of size 1
==25056== at 0x4E9E7C0: _IO_file_xsputn@@GLIBC_2.2.5 (in /lib64/libc-2.12.so)
==25056== by 0x4E74FFF: vfprintf (in /lib64/libc-2.12.so)
==25056== by 0x4E7BFF9: printf (in /lib64/libc-2.12.so)
==25056== by 0x4005CD: printThis (so19.c:27)
==25056== by 0x400570: main (so19.c:19)
==25056== Address 0x1ffeffed2a expected vs actual:
==25056== Expected: stack array "a" of size 10 in frame 4 back from here
==25056== Actual: unknown
==25056== Actual: is 0 after Expected
这是在使用调试信息编译时 (-g
)。通过优化构建,未检测到任何错误。
如标题所说,我真的需要帮助理解,为什么我的系统(linux mint 19
、GCC-8.0.1
、valgrind-3.13.0
、c17
)将这段代码视为无效代码:
#include <stdio.h>
#include <string.h>
void printThis( const char *const ptr );
int main( void) {
char a[10] = "asds";
char b[10] = "1234567890";
strcpy ( a, b );
printThis( a );
}
void printThis( const char *const ptr ){
printf("Copy completed! : %s\n", ptr );
}
Valgrind 在此处报告问题:
==6973== Memcheck, a memory error detector ==6973== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==6973== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info ==6973== Command: /home/michi/Templates/Cprogram/bin/Debug/Cprogram ==6973== ==6973== Source and destination overlap in strcpy(0x1ffefffd14, 0x1ffefffd1e) ==6973== at 0x4C32E97: strcpy (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==6973== by 0x108724: main (main.c:12) ==6973== Copy completed! : 1234567890 ==6973== ==6973== HEAP SUMMARY: ==6973== in use at exit: 0 bytes in 0 blocks ==6973== total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated ==6973== ==6973== All heap blocks were freed -- no leaks are possible ==6973== ==6973== For counts of detected and suppressed errors, rerun with: -v ==6973== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
这个有效代码:
#include <stdio.h>
void strcpy2(char *s, char *t);
void printThis( const char *const ptr );
int main( void) {
char a[10] = "asds";
char b[10] = "1234567890";
strcpy2( a, b );
printThis( a );
}
void strcpy2(char *s, char *t) {
while ( ( *(s++) = *(t++) ) );
}
void printThis( const char *const ptr ){
printf("Copy completed! : %s\n", ptr );
}
Valgrind 输出:
==7025== Memcheck, a memory error detector ==7025== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. ==7025== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info ==7025== Command: /home/michi/Templates/Cprogram/bin/Debug/Cprogram ==7025== Copy completed! : 1234567890 ==7025== ==7025== HEAP SUMMARY: ==7025== in use at exit: 0 bytes in 0 blocks ==7025== total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated ==7025== ==7025== All heap blocks were freed -- no leaks are possible ==7025== ==7025== For counts of detected and suppressed errors, rerun with: -v ==7025== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
使用 O0
、O1
、O2
和 O3
以及 GCC 标志编译:
-Wpedantic -std=c17 -Wall -Wextra -Werror -Wstrict-prototypes -Wmissing-prototypes -Wmisleading-indentation -Wduplicated-cond -Wold-style-definition -Wconversion -Wshadow -Winit-self -Wfloat-equal -Wwrite-strings -O0 -g
Valgrind 可以捕获只有某些类型的错误。它无法检测堆栈,因此它不会看到您的 strcpy2
的错误。 OTOH strcpy
被一个检查源和目标是否重叠的版本所取代 - 它可以捕获这个 only 因为 a + 10 == b
in your 编译程序!
要捕获此类错误,请使用 GCC 的 -fsanitize=address
:
% ./a.out
=================================================================
==3368==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff13832a2a at pc 0x557f05344da8 bp 0x7fff13832990 sp 0x7fff13832980
READ of size 1 at 0x7fff13832a2a thread T0
#0 0x557f05344da7 in strcpy2 (/a.out+0xda7)
#1 0x557f05344cca in main (/a.out+0xcca)
#2 0x7f2d400e5b96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
#3 0x557f05344a49 in _start (/a.out+0xa49)
Address 0x7fff13832a2a is located in stack of thread T0 at offset 106 in frame
#0 0x557f05344b39 in main (/a.out+0xb39)
This frame has 2 object(s):
[32, 42) 'a'
[96, 106) 'b' <== Memory access at offset 106 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
[ ... many more lines follow ... ]
我更新了标题,以便更准确地说明所使用的 Valgrind 工具。
Valgrind 可以 检测到此类错误,但如前所述,memcheck
.
如果我 运行 使用 Valgrind exp-sgcheck
这个示例代码(exp = 实验性的,sgcheck = 堆栈和全局检查)
valgrind --tool=exp-sgcheck ./so19
然后我得到
==25056== Invalid read of size 1
==25056== at 0x40059D: strcpy2 (so19.c:23)
==25056== by 0x400564: main (so19.c:18)
==25056== Address 0x1ffeffed1a expected vs actual:
==25056== Expected: stack array "b" of size 10 in frame 1 back from here==25056== Actual: unknown
==25056== Actual: is 0 after Expected
==25056==
==25056== Invalid write of size 1
==25056== at 0x4005A0: strcpy2 (so19.c:23)
==25056== by 0x400564: main (so19.c:18)
==25056== Address 0x1ffeffed2a expected vs actual:
==25056== Expected: stack array "a" of size 10 in frame 1 back from here==25056== Actual: unknown
==25056== Actual: is 0 after Expected
==25056==
==25056== Invalid read of size 1
==25056== at 0x4005A2: strcpy2 (so19.c:23)
==25056== by 0x400564: main (so19.c:18)
==25056== Address 0x1ffeffed2a expected vs actual:
==25056== Expected: stack array "a" of size 10 in frame 1 back from here==25056== Actual: unknown
==25056== Actual: is 0 after Expected
==25056==
==25056== Invalid read of size 1
==25056== at 0x4E74C9C: vfprintf (in /lib64/libc-2.12.so)
==25056== by 0x4E7BFF9: printf (in /lib64/libc-2.12.so)
==25056== by 0x4005CD: printThis (so19.c:27)
==25056== by 0x400570: main (so19.c:19)
==25056== Address 0x1ffeffed2a expected vs actual:
==25056== Expected: stack array "a" of size 10 in frame 3 back from here==25056== Actual: unknown
==25056== Actual: is 0 after Expected
==25056==
==25056== Invalid read of size 1
==25056== at 0x4E9E7C0: _IO_file_xsputn@@GLIBC_2.2.5 (in /lib64/libc-2.12.so)==25056== by 0x4E74FFF: vfprintf (in /lib64/libc-2.12.so)
==25056== by 0x4E7BFF9: printf (in /lib64/libc-2.12.so)
==25056== by 0x4005CD: printThis (so19.c:27)
==25056== by 0x400570: main (so19.c:19)
==25056== Address 0x1ffeffed2a expected vs actual:
==25056== Expected: stack array "a" of size 10 in frame 4 back from here==25056== Actual: unknown
==25056== Actual: is 0 after Expected
这是在使用调试信息编译时 (-g
)。通过优化构建,未检测到任何错误。