Ошибки Valgrind для простых строковых функций C - PullRequest
5 голосов
/ 17 сентября 2011

Давайте рассмотрим эту простую тестовую программу:

#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;
}

При компиляции с помощью компилятора Intel c ++ и включенных оптимизаций (O3) я получаю следующие ошибки от 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)

Я использую самую последнюю версию valgrind (3.6.1).Это не происходит при отключении оптимизации (-O0), и не происходит с g ++.Тем не менее, он появляется со всеми компиляторами Intel, которые я опробовал до сих пор (11.0, 11.1, 12).

Кажется, что ошибки связаны с SIMD-ускорением строковых функций, как обсуждалось в C-строки, strlen и Valgrind .

Там было указано, что это ошибка в valgrind и теперь она исправлена.Однако, несмотря на использование самой последней версии valgrind, у меня все еще есть эти ошибки.Кто-нибудь знает какую-нибудь помощь по этому поводу?

Ответы [ 2 ]

6 голосов
/ 17 сентября 2011

Valgrind пытается определить, зависит ли значение от инициализированной памяти или нет, что в общем-то не является проблемой, которую можно устранить. Valgrind делает все возможное, отслеживая, какие биты установлены, и позволяя им каскадироваться. Есть много способов обмануть это. Например, я только что приготовил этот код:

#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;
}

Примечание: Вышеуказанная программа, строго говоря, правильная на любой платформе, которая не имеет представления прерываний для unsigned int, что верно для нашей платформы (Valgrind только для x86) Неопределенное поведение не вызывается на этих платформах, и main гарантируется стандартом C для возврата 0 на таких платформах.

Образец цитирования: n1256: 7.20.3.3: malloc возвращает объекты, значения которых являются "неопределенными". n1256 3.17.2: неопределенное значение является либо «представлением ловушки», либо «неопределенным значением». Обратите внимание, что в x86 нет целых представлений для целых чисел без знака.

Согласно Valgrind, ни f, ни g не инициализированы должным образом, поэтому f & g также нельзя инициализировать. Тем не менее, результат всегда равен нулю, как вам скажет любой, имеющий унцию логики. Valgrind не понимает логику, он просто следует битам по некоторым простым правилам.

Вероятно, здесь происходит то, что компилятор Intel C дал вам оптимизированную версию strlen, которая использует SSE или какую-то хитрость, которая привела к тому, что «неинициализированные» биты в Valgrind были установлены в результате. Естественно, это произошло бы, если бы оптимизированный код прочитал после , инициализировал часть buf, а затем выполнил серию операций, подобных описанным выше. И, конечно, он будет делать что-то подобное, потому что так делать быстрее (и всегда безопасно читать после конца массивов в x86, если вы не пересекаете границу страницы). У вас есть несколько вариантов.

  • Отключить оптимизацию при использовании Valgrind и компилятора Intel.

  • Добавьте код в Valgrind, чтобы поймать этот тип ошибки. (В Valgrind уже есть особые случаи.)

  • Измените ваш код условно, чтобы Valgrind дал правильный результат. Например, поместите это вверху:

    // 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
    
0 голосов
/ 17 сентября 2011

Не используйте valgrind с оптимизацией.

...