Memcheck сообщает об унифицированных значениях при доступе к локальным переменным вниз по стеку.

Я столкнулся с проблемой, что Memcheck сообщает о неинициализированных значениях, и я думаю, что это совершенно законно. Мне удалось создать небольшой пример программы, демонстрирующей такое поведение. Я хотел бы знать, действительно ли Memcheck ошибается и что можно с этим поделать. (Есть ли какое-либо решение, кроме добавления ошибок в файл подавления?)

Чтобы воспроизвести это, я сделал программу ниже. Он запускает функцию go, которая помещает 0x42 в стек, вызывает og (это помещает адрес следующей инструкции leave в стек), затем в og сохраняет esp+4 в глобальную переменную a.

Стек выглядит так:

| address of `leave` instruction |                     pc = a[-1]
| 0x42                           |  a points here, answer = a[0]

Если я соберу его и запущу Valgrind,

gcc -g -m32 main.c go.S -o main
valgrind --track-origins=yes ./main

Valgrind считает, что значение в переменной pcanswer, если вы вместо этого поместите его в if) не определено. Я проверил с помощью отладчика, что значения там действительно то, что я хотел.

==14160== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==14160== Command: ./main
==14160== 
==14160== Conditional jump or move depends on uninitialised value(s)
==14160==    at 0x804847D: print (main.c:18)
==14160==    by 0x80484B0: ??? (go.S:19)
==14160==    by 0x8048440: main (main.c:8)
==14160==  Uninitialised value was created by a stack allocation
==14160==    at 0x80484AC: ??? (go.S:19)
==14160== 
==14160== Use of uninitialised value of size 4
==14160==    at 0x80484B1: ??? (go.S:20)
==14160==    by 0x8048440: main (main.c:8)
==14160==  Uninitialised value was created by a stack allocation
==14160==    at 0x80484AC: ??? (go.S:19)

Если я отлаживаю Valgrind с помощью --vgdb-error=0 и печатаю определение, оно говорит, что все биты не определены.

(gdb) p &pc
$1 = (int *) 0xfea5e4a8
(gdb) mo xb 0xfea5e4a8 4
              ff      ff      ff      ff
0xFEA5E4A8:     0x9e    0x84    0x04    0x08

Значение по адресу 0xfea5e4a8 равно

(gdb) x/x 0xfea5e4a8
0xfea5e4a8:     0x0804849e

а также

(gdb) x/i 0x0804849e
   0x804849e <go+10>:   leave  
(gdb)

main.c

#include<stdio.h>

int *a;

extern void go();

int main() {
    go();
    printf("finito\n");
    return 0;
}

int print() {
    int answer = a[0];
    int pc = a[-1];

    // use the vars
    if (pc == 0x42) {
        printf("%d\n", 0);
    }
}

go.S

    .text

.globl go

go:
    pushl %ebp
    movl %esp, %ebp

    pushl $0x42
    call og

    leave
    ret

og:
    addl $4, %esp
    movl %esp, a
    sub $4, %esp
    call print
    ret

person user7610    schedule 09.01.2016    source источник
comment
Я думаю, проблема может заключаться в том, что как только вы сделаете addl $4, %esp, вы больше не будете владеть частью стека, кроме esp. Это всегда так. Хотя это случается редко, операционная система может перезаписать эту часть стека, скажем, в обработчике прерывания.   -  person 500 - Internal Server Error    schedule 09.01.2016


Ответы (1)


Проблема в этой последовательности кода:

addl $4, %esp
movl %esp, a
sub $4, %esp

Когда вы переместите %esp вверх, valgrind пометит все, что ниже новой позиции указателя стека, как неопределенное, и оно останется таким даже после того, как вы переместите его обратно.

В любом случае это небезопасно, потому что если сигнал попадет между add и sub, то этот стек действительно может быть перезаписан (в 32-битном коде - в 64-битном коде есть «красная зона» под указателем, который безопасен, но valgrind знает об этом ).

person TomH    schedule 09.01.2016