wchar_t valgrind 问题 - 大小为 8 的读取无效

Posted

技术标签:

【中文标题】wchar_t valgrind 问题 - 大小为 8 的读取无效【英文标题】:wchar_t valgrind issue - Invalid read of size 8 【发布时间】:2013-03-12 11:20:38 【问题描述】:

我无法弄清楚为什么 Valgrind 在使用 wchar_t 时会打印 Invalid read of size 8。我正在使用 valgrind-3.7.0 和 gcc 4.7.2 运行 64 位 Ubuntu (3.5.0-25) 系统。

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

编译:

$ 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

我没有问题:

==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 的块)但有些情况失败了,然后我认为我必须始终为 NULL 终止符附加 8 个字节((wcslen(item) + 2) * sizeof(wchar_t)) ,它起作用了,但这没有任何意义,因为 sizeof(wchar_t) - 在我的系统中 - 是 4 个字节,应该足以处理 L'\0' 终止符。

我还阅读了 glibc wcslen 源代码,但没有什么新内容。我现在正在考虑 Valgrind 问题。各位大佬能不能给个亮点?是否值得针对 Valgrind 提交错误?

谢谢

【问题讨论】:

可能是 valgrind 的问题,是的。使用您的代码和相同的 gcc 版本,我没有收到 3.8.1 版本的错误。 将此new_text = (wchar_t*) malloc( (wcslen(text) + 1) * sizeof(wchar_t)); 更改为new_text = calloc(wcslen(text) + 1, sizeof(*new_text)); 并重新测试。 附带说明 - 如果您在字符串中使用 any 非 ASCII 字符,您的代码将不起作用。你应该设置语言环境。 【参考方案1】:

这可能是wcslen函数的SSE优化造成的;参见例如https://bugzilla.redhat.com/show_bug.cgi?id=798968 或 https://bugs.archlinux.org/task/30643。

在优化 wcslen 时,一次读取多个宽字符并使用矢量化指令 (SSE) 将它们与 L'\0' 进行比较会更快。不幸的是,valgrind 将此视为未初始化的读取 - 确实如此,但它是无害的,因为 wcslen 的结果不依赖于未初始化的值。

修复方法是更新 valgrind,希望更新的版本能够抑制误报。

【讨论】:

以上是关于wchar_t valgrind 问题 - 大小为 8 的读取无效的主要内容,如果未能解决你的问题,请参考以下文章

大小为 4 valgrind 的读取无效

大小为 8 的无效读取 - Valgrind + C

Valgrind 大小为 4 的无效读取(OpenCV)

注意:“无符号”说明符会更改 Visual Studio 中 wchar_t 类型的大小

CString 如何转为wchar_t

free():fclose 上的下一个大小(正常)无效。但不是在 Valgrind 运行时[重复]