valgrind使用经验两则

分享Cvalgrind by 达达 at 2011-11-06

今天在Ubuntu上面用valgrind运行了BinSpec,出现了一堆警告,摸索了一阵子才把警告都消除掉,下面是我遇到的两个问题。

第一类警告:

==4016== Invalid read of size 1
==4016==    at 0x0804a36c: ignore_wac (within /home/bg5sbk/Code/Labs/BinSpec/bin/bs)
==4016==    by 0x0804a491: next_token (within /home/bg5sbk/Code/Labs/BinSpec/bin/bs)
==4016==    by 0x0804b36f: bs_parse (within /home/bg5sbk/Code/Labs/BinSpec/bin/bs)
==4016==    by 0x0804c39c: main (within /home/bg5sbk/Code/Labs/BinSpec/bin/bs)
==4016==    Address 0x41e83c7 is 0 bytes after a block of size 527 alloc'd
==4016==    at 0x04028876: malloc (vg_replace_malloc.c:236)
==4016==    by 0x0804c4e9: file_get_contents (within /home/bg5sbk/Code/Labs/BinSpec/bin/bs)
==4016==    by 0x0804c31b: main (within /home/bg5sbk/Code/Labs/BinSpec/bin/bs)

这一类警告相对比较好找,已经提示出问题的函数是ignore_wac,内存是在file_get_contents函数分配的,并提示在xxx地址之后是0字节,意思就是越界读取了。

察看一下代码,在最后一次next_token函数调用的时候的确会越界读取,但是最终原因是file_get_contents返回的不是C语言的\0结尾的字符串,所以最终修改的是file_get_contents函数的内存分配,多分配一个末尾字节,这个警告便消失了。

第二类警告:

==4038== Conditional jump or move depends on uninitialised value(s)
==4038==    at 0x040ae410: vfprintf (vfprintf.c:1620)
==4038==    by 0x040b51de: printf (printf.c:35)
==4038==    by 0x08049a5b: bs_print_pkg_list (within /home/bg5sbk/Code/Labs/BinSpec/bin/bs)
==4038==    by 0x08049a43: bs_print_pkg_list (within /home/bg5sbk/Code/Labs/BinSpec/bin/bs)
==4038==    Uninitialised value was created by a heap allocation
==4038==    at 0x04028876: malloc (vg_replace_malloc.c:236)
==4038==    by 0x08049566: bs_def_new (within /home/bg5sbk/Code/Labs/BinSpec/bin/bs)
==4038==    by 0x0804acf3: parse_def (within /home/bg5sbk/Code/Labs/BinSpec/bin/bs)
==4038==    by 0x0804b22b: parse_pkg (within /home/bg5sbk/Code/Labs/BinSpec/bin/bs)

这类警告的信息比较直观,但是却比较不好排查,反复检查bs_def_new函数的结构体初始化都没有发现问题,最后才定位到这个函数中的字符串复制代码,原来的代码是这样的:

    char *name2 = (char *)malloc(name_len + 1);
    def->name = strncpy(name2, name, name_len);
    def->name_len = name_len;

因为按man提供的帮助信息,strncpy会把目标字符串中过长的部分自动填充\0,所以这里实际上不会有问题,但是valgrind会认为name2这个内存块分配后没初始化,最后加上bzero函数后警告消失,最终代码是这样:

    size_t name2_len = name_len + 1;
    char *name2 = (char *)malloc(name_len + 1);
    bzero(name2, name2_len);

    def->name = strncpy(name2, name, name_len);
    def->name_len = name_len;