Valgrind 在使用 realloc 和 strdup 进行分配时检测到错误

Valgrind detect error when allocating using realloc and strdup

到目前为止,我的程序运行良好,我想 运行 valgrind 以确保我没有忘记任何 free/malloc。但是,Valgrind 报告了我认为存在 none.

的错误

这是重现错误的代码片段:

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

int main()
{
    /* Init the array to NULL for realloc */
    char **name_array = NULL;
    int nb_names =0;

    /* Allocate the first name and add it to the array */
    name_array = realloc(name_array, sizeof(char *));
    name_array[nb_names] = strdup("Hello World!\n");
    nb_names++;


    /* Allocate the second name and add it to the array */
    name_array = realloc(name_array, sizeof(char *));
    name_array[nb_names] = strdup("This is a test!\n");

    /* Print the names */
    printf (name_array[0]);
    printf (name_array[1]);

    /* Free the strdup'd names and the array */
    free(name_array[0]);
    free(name_array[1]);
    free(name_array);
}

这是程序输出:

Hello World!
This is a test!

这是 Valgrind 的输出:

==31585== Memcheck, a memory error detector
==31585== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==31585== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==31585== Command: ./a.out --leak-check=full
==31585== 
==31585== Invalid write of size 4
==31585==    at 0x10538: main (in /home/pi/tmp/a.out)
==31585==  Address 0x49830a4 is 0 bytes after a block of size 4 alloc'd
==31585==    at 0x48358A0: realloc (vg_replace_malloc.c:632)
==31585==    by 0x10517: main (in /home/pi/tmp/a.out)
==31585== 
Hello World!
==31585== Invalid read of size 4
==31585==    at 0x10554: main (in /home/pi/tmp/a.out)
==31585==  Address 0x49830a4 is 0 bytes after a block of size 4 alloc'd
==31585==    at 0x48358A0: realloc (vg_replace_malloc.c:632)
==31585==    by 0x10517: main (in /home/pi/tmp/a.out)
==31585== 
This is a test!
==31585== Invalid read of size 4
==31585==    at 0x10578: main (in /home/pi/tmp/a.out)
==31585==  Address 0x49830a4 is 0 bytes after a block of size 4 alloc'd
==31585==    at 0x48358A0: realloc (vg_replace_malloc.c:632)
==31585==    by 0x10517: main (in /home/pi/tmp/a.out)
==31585== 
==31585== 
==31585== HEAP SUMMARY:
==31585==     in use at exit: 0 bytes in 0 blocks
==31585==   total heap usage: 4 allocs, 4 frees, 39 bytes allocated
==31585== 
==31585== All heap blocks were freed -- no leaks are possible
==31585== 
==31585== For counts of detected and suppressed errors, rerun with: -v
==31585== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

我已经搜索过关于这个答案的其他问答,通常人们会忘记分配空字节。

我知道realloc在没有内存时会导致内存泄漏,我应该先将realloc的结果赋值给一个临时变量,检查返回值不为null,然后将临时变量赋值给我的真实变量。

除了realloc失败时可能会泄露,这个程序有没有错误?


更新

感谢大家的快速回答。

这里记录的是更正后的代码:

    /* Allocate the first name and add it to the array */
    name_array = realloc(name_array, sizeof(char *) * ++nb_names);
    name_array[nb_names -1] = strdup("Hello World!\n");


    /* Allocate the second name and add it to the array */
    name_array = realloc(name_array, sizeof(char *) * ++nb_names);
    name_array[nb_names -1] = strdup("This is a test!\n");

    /* Print the names */
    printf (name_array[0]);
    printf (name_array[1]);

并且 valgrind 输出:

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

大家好。

这次(第二次)通话:

name_array = realloc(name_array, sizeof(char *));

您仍然只分配一个字符指针。所以,你不能存储两个指针;你需要增加尺寸:

name_array = realloc(name_array, 2 * sizeof *name_array);

现在,你会没事的。

请注意,如果 realloc() 失败,p = realloc(p, ..); 风格的 realloc() 可能会导致内存泄漏。

此外,您最好使用格式字符串来避免潜在的格式字符串攻击(如果它将由用户输入):

   /* Print the names */
    printf ("%s\n", name_array[0]);
    printf ("%s\n", name_array[1]);

地址清理器比 valgrind 更能报告错误。如果您像这样编译代码:

gcc test.c -fsanitize=address -g

然后运行它,它会在你的代码第19行报heap-buffer-overflow错误。这是您分配 name_array 的第二个元素的行,只为一个元素分配了内存。