Почему Valgrind memcheck не ловит этот UB? - PullRequest
0 голосов
/ 18 сентября 2018

Как и заголовок, мне действительно нужна помощь в понимании того, почему этот код обрабатывается в моей системе (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

Ответы [ 2 ]

0 голосов
/ 18 сентября 2018

Я обновил заголовок, чтобы быть более точным относительно используемого инструмента Valgrind.

Valgrind может обнаруживать ошибки такого рода, но, как уже отмечалось, не с memcheck.

Если я запускаю этот пример кода с Valgrind exp-sgcheck (exp = экспериментальный, sgcheck = проверка стека и глобальных переменных)

valgrind --tool=exp-sgcheck ./so19

Тогда я получаю

== 25056 == Недопустимое чтение размера 1
== 25056 == в 0x40059D: strcpy2 (so19.c: 23)
== 25056 == в 0x400564: основное (so19.c: 18)
== 25056 == Адрес 0x1ffeffed1a ожидается против фактического:
== 25056 == Ожидается: массив стека "b" размером 10 в кадре 1 отсюда

== 25056 == Фактически: неизвестно
== 25056 == Фактически: 0 после ожидаемого
== 25056 ==
== 25056 == Неверная запись размером 1
== 25056 == при 0x4005A0: strcpy2 (so19.c: 23)
== 25056 == по 0x400564: main (so19.c: 18)
== 25056 == Адрес 0x1ffeffed2a ожидается против фактического:
== 25056 == Ожидается: массив стека "a "размером 10 в кадре 1 отсюда

== 25056 == Фактически: неизвестно
== 25056 == Фактически: 0 после ожидаемого
== 25056 ==
== 25056 == Недопустимое чтение размера 1
== 25056 ==в 0x4005A2: strcpy2 (so19.c: 23)
== 25056 == по 0x400564: основной (so19.c: 18)
== 25056 == Адрес 0x1ffeffed2a ожидается против фактического:
== 25056== Ожидаемый: стек стека «a» размером 10 в кадре 1 отсюда

== 25056 == Фактически: неизвестно
== 25056 == Фактически: 0 после ожидаемого
== 25056 ==
== 25056 == Недопустимое чтение размера 1
== 25056 == при 0x4E74C9C: vfprintf (в /lib64/libc-2.12.so)
== 25056 == по 0x4E7BFF9: printf (в /lib64/libc-2.12.so)
== 25056 == по 0x4005CD: printThis (so19.c: 27)
== 25056 == по 0x400570: основной (so19.c: 19)
== 25056 == Ожидаемый адрес 0x1ffeffed2a против фактического:
== 25056 == Ожидается: стек стека "a" размером 10 в кадре 3 отсюда

== 25056 ==Факт: неизвестно
== 25056 == Факт: 0 после ожидаемого
== 25056 ==
== 25056 == Недопустимое чтение размера 1
== 25056 == в 0x4E9E7C0: _IO_file_xsputn @@ GLIBC_2.2.5 (в /lib64/libc-2.12.so)

== 25056 == по 0x4E74FFF: vfprintf (в /lib64/libc-2.12.so)
== 25056 == по 0x4E7BFF9: printf (в /lib64/libc-2.12.so)
== 25056 == по 0x4005CD: printThis (so19.c: 27)
== 25056 == по0x400570: main (so19.c: 19)
== 25056 == Ожидаемый адрес 0x1ffeffed2a против фактического:
== 25056 == Ожидаемый: стек стека "a" размера 10 в кадре 4 отсюда

== 25056 == Фактически: неизвестно
== 25056 == Фактически: равно 0 после ожидаемого

Это при компиляции с отладочной информацией (-g).При оптимизированной сборке ошибок не обнаружено.

0 голосов
/ 18 сентября 2018

Valgrind может отлавливать только определенные виды ошибок .Он не может обработать стек, поэтому он не увидит ошибку с вашим strcpy2.OTOH strcpy заменяется версией, которая проверяет, перекрываются ли источник и назначение - он может перехватить эту только , потому что a + 10 == b в вашей скомпилированной программе!

Чтобы поймать этот тип ошибки, используйте 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 ... ]
...