2013-03-22 5 views
8

Я не могу понять, почему Valgrind печатает Invalid read of size 8 при использовании wchar_t. Я запускаю 64-битную систему Ubuntu (3.5.0-25) с valgrind-3.7.0 и gcc 4.7.2.wchar_t valgrind issue - Неверное чтение размером 8

#include <stdio.h> 
#include <wchar.h> 
#include <stdlib.h> 
#include <string.h> 

int main() 
{ 
    // const wchar_t *text = L"This is a t"; // no Valgrind error 
    // const wchar_t *text = L"This is a teeeeeeee"; // no Valgrind error 
    const wchar_t *text = L"This is a test"; // Valgrind ERRROR 

    wchar_t *new_text = NULL; 

    new_text = (wchar_t*) malloc((wcslen(text) + 1) * sizeof(wchar_t)); 
    wcsncpy(new_text, text, wcslen(text)); 
    new_text[wcslen(text)] = L'\0'; 

    printf("new_text: %ls\n", new_text); 

    free(new_text); 

    return 0; 
} 

Compile:

$ gcc -g -std=c99 test.c -o test 
$ valgrind --tool=memcheck --leak-check=full --track-origins=yes --show-reachable=yes ./test 

Valgrind результаты:

==19495== Memcheck, a memory error detector 
==19495== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al. 
==19495== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info 
==19495== Command: ./test 
==19495== 
==19495== Invalid read of size 8 
==19495== at 0x4ED45A7: wcslen (wcslen.S:55) 
==19495== by 0x4ED5C0E: wcsrtombs (wcsrtombs.c:74) 
==19495== by 0x4E7D160: vfprintf (vfprintf.c:1630) 
==19495== by 0x4E858D8: printf (printf.c:35) 
==19495== by 0x4006CC: main (test.c:16) 
==19495== Address 0x51f1078 is 56 bytes inside a block of size 60 alloc'd 
==19495== at 0x4C2B3F8: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) 
==19495== by 0x40066F: main (test.c:12) 
==19495== 
new_text: This is a test 
==19495== 
==19495== HEAP SUMMARY: 
==19495==  in use at exit: 0 bytes in 0 blocks 
==19495== total heap usage: 1 allocs, 1 frees, 60 bytes allocated 
==19495== 
==19495== All heap blocks were freed -- no leaks are possible 
==19495== 
==19495== For counts of detected and suppressed errors, rerun with: -v 
==19495== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2) 

Теперь, если я бегу то же самое, но с 'рабочей строки', скажем

const wchar_t *text = L"This is a t"; // no Valgrind error 
// const wchar_t *text = L"This is a teeeeeeee"; // no Valgrind error 
// const wchar_t *text = L"This is a test"; // Valgrind ERRROR 

I не возникает проблем:

==19571== Memcheck, a memory error detector 
==19571== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al. 
==19571== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info 
==19571== Command: ./test 
==19571== 
new_text: This is a t 
==19571== 
==19571== HEAP SUMMARY: 
==19571==  in use at exit: 0 bytes in 0 blocks 
==19571== total heap usage: 1 allocs, 1 frees, 48 bytes allocated 
==19571== 
==19571== All heap blocks were freed -- no leaks are possible 
==19571== 
==19571== For counts of detected and suppressed errors, rerun with: -v 
==19571== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) 

Сначала я думал, что размер строка должна быть всегда кратна 8 (возможно, некоторые WCS читал куски 8), но в некоторых случаях не удалось, то я думал, что придется приложить всегда 8 байт для терминатора NULL ((wcslen(item) + 2) * sizeof(wchar_t)), это сработало, но это не имеет никакого смысла с sizeof(wchar_t) - в моей системе - 4 байта и должно быть достаточно для обработки терминатора L'\0'.

Я также прочитал исходный код glibc wcslen, но ничего нового. Теперь я думаю о проблеме Вальгринда. Вы, ребята, можете бросить здесь немного света? Стоит ли регистрировать ошибку против Valgrind?

Спасибо

+0

Возможно, проблема с valgrind, да. Я не получаю ошибок с версией 3.8.1 с вашим кодом и той же версией gcc. – teppic

+0

Изменить этот 'new_text = (wchar_t *) malloc ((wcslen (text) + 1) * sizeof (wchar_t));' стать 'new_text = calloc (wcslen (text) + 1, sizeof (* new_text));' и повторный тест. – alk

+0

В качестве побочного примечания - ваш код не будет работать, если вы используете _any_ не-ASCII-символы в своих строках. Вы должны установить локаль. – teppic

ответ

6

Это, вероятно, обусловлено оптимизацией SSE функции wcslen; см., например, https://bugzilla.redhat.com/show_bug.cgi?id=798968 или https://bugs.archlinux.org/task/30643.

При оптимизации wcslen быстрее считывать несколько широких символов за один раз и использовать векторизованные инструкции (SSE) для сравнения их с L'\0'. К сожалению, valgrind рассматривает это как неинициализированное чтение, которое оно есть, но оно безвредно, потому что результат wcslen не зависит от неинициализированного значения.

Исправление состоит в том, чтобы обновить valgrind в надежде, что более новая версия подавит ложный положительный результат.