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 终止其输出字符串;实际上,只有当wordstrncpy() 的大小参数(以字符数计)时,它才会这样做。

因此,在运行时,您当前的源字符串和大小参数为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 的无效读取的主要内容,如果未能解决你的问题,请参考以下文章

大小为 4 valgrind 的读取无效

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

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

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

c - 无法理解为什么我有这些错误:无效读取大小1和Syscall参数unlink(路径名)指向不可寻址的字节

分段错误:C ++ IntVector中为11