C - valgrind - 大小为 1 的无效读取
Posted
技术标签:
【中文标题】C - valgrind - 大小为 1 的无效读取【英文标题】:C - valgrind - invalid read of size 1 【发布时间】:2014-03-02 04:17:26 【问题描述】:我在使用 Valgrind 调试代码时遇到了一些问题。以下是出现错误的结构和主要部分:
struct trieNode
char *word;
struct trieNode *(subNode[LEAF_NUM]);
struct sharpNode *sharp;
;
//linked list of sharp(s)
struct sharpNode
char *word;
struct sharpNode *next;
;
if (head->word == NULL)
//strlen(head->word) == 0)
head->word = (char *)malloc(MAX_LEN * sizeof(char)); //LINE 191
//memset(head->word, '\0',strlen(head->word));
strncpy (head->word, word, strlen(word));
else
if (head->sharp == NULL)
head->sharp = sharpNodeCreate();
head->sharp->word = (char *)malloc(MAX_LEN * sizeof(char)); //LINE 200
//head->sharp->word[strlen(word)] = '\0';
strncpy (head->sharp->word, word, strlen(word));
else if ( sharpIndex == 0 && strlen(trie_ptr->word) > 0)
printf("%s\n", trie_ptr->word); //LINE 135
else if (notSharp == 0 && sharp_ptr != NULL)
printf("%s\n", sharp_ptr->word); //LINE 137
else
printf("There are no more T9onyms\n");
当我运行 valgrind 时,它会抱怨:
==20040== Invalid read of size 1
==20040== at 0x4A09264: __GI_strlen (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==20040== by 0x334BC6DD2B: puts (in /usr/lib64/libc-2.17.so)
==20040== by 0x400C2C: lookupTrie (trie.c:135)
==20040== by 0x400905: main (t9.c:23)
==20040== Address 0x4c4e2f4 is 0 bytes after a block of size 4 alloc'd
==20040== at 0x4A06409: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==20040== by 0x400D6B: populateTrie (trie.c:191)
==20040== by 0x4008F9: main (t9.c:22)
==20040==
ace
Enter Key Sequence (or "#" for next word):
#
==20040== Invalid read of size 1
==20040== at 0x4A09264: __GI_strlen (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==20040== by 0x334BC6DD2B: puts (in /usr/lib64/libc-2.17.so)
==20040== by 0x400C4A: lookupTrie (trie.c:137)
==20040== by 0x400905: main (t9.c:23)
==20040== Address 0x4dad294 is 0 bytes after a block of size 4 alloc'd
==20040== at 0x4A06409: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==20040== by 0x400DDA: populateTrie (trie.c:200)
==20040== by 0x4008F9: main (t9.c:22)
有人能指出正确的方向吗?
【问题讨论】:
head->sharp = sharpNodeCreate();
,sharpNodeCreate()
在做什么?
在strncpy (head->word, word, strlen(word));
中,word
(第二个参数)是什么?
【参考方案1】:
你得到的错误是由于
strncpy (head->word, word, strlen(word));
和
strncpy (head->sharp->word, word, strlen(word));
不使用 NUL 字符终止其目标。 strncpy()
并不总是 NUL 终止其输出字符串;实际上,只有当word
比strncpy()
的大小参数短(以字符数计)时,它才会这样做。
因此,在运行时,您当前的源字符串和大小参数为strncpy()
(其中单词中的字符数恰好等于且不短于大小-参数),NUL 字符不是由strncpy()
写在head->word
的末尾。但是当printf
试图打印你的字符串时,它必须隐式地找到那个字符串的结尾,由它的终止 NUL 字符标记。因此,它会读取您分配的所有缓冲区,但找不到 NUL 字符,并且纯粹靠运气在缓冲区结束后立即找到一个,因此不会崩溃。然而,这是一个无效的读取; Valgrind 为你找到了它,但你必须修复它。
要解决这个问题,我建议您将strncpy()
的大小参数替换为MAX_LEN-1
,并手动终止字符串复制为head->word[MAX_LEN-1] = '\0'
。
或者,您可以像我之前所做的那样,自己实现一个 strzcpy(char* d, char* s, size_t len)
函数,该函数复制 len-1
字符和 NUL 终止符。可惜直到 C11 才标准化这样的功能。
【讨论】:
【参考方案2】: strncpy (head->word, word, strlen(word));
顺便说一句:这不是我第一次看到这种模式。它是如何自我传播的?这个模因的持有者应该都已经被自然选择杀死了(如果只是......)
strncpy
是为了防止缓冲区溢出而发明的。缓冲区是函数的第一个参数(填充数据的地方)。当您尝试填充超出其容量的数据时,就会发生溢出。为了防止这种情况,您可以通过告诉strncpy
缓冲区可以容纳多少数据来限制可以填充的数据量。你传入缓冲区的大小。您不会传递必须复制的数据量。 strncpy
完全能够自己解决。它无法知道复制多少数据是安全。 这就是为什么您需要在单独的参数中传递该金额。
当然,您必须以空值终止您的字符串。 strncpy
不会帮你填满缓冲区。
【讨论】:
以上是关于C - valgrind - 大小为 1 的无效读取的主要内容,如果未能解决你的问题,请参考以下文章
wchar_t valgrind 问题 - 大小为 8 的读取无效
free():fclose 上的下一个大小(正常)无效。但不是在 Valgrind 运行时[重复]