Valgrind - snprintf:条件跳转或移动取决于未初始化的值

Valgrind - snprintf : Conditional jump or move depends on uninitialised value(s)

通过 valgrind 启动程序后,我收到以下消息:

==9290== Conditional jump or move depends on uninitialised value(s)
==9290==    at 0x4E82A03: vfprintf (vfprintf.c:1661)
==9290==    by 0x4EA9578: vsnprintf (vsnprintf.c:119)
==9290==    by 0x4E8B531: snprintf (snprintf.c:33)
==9290==    by 0x400820: _function (in /home/snp/prog/TEST)
==9290==    by 0x4006D5: start (in /home/snp/prog/TEST)
==9290==    by 0x40085C: main (in /home/snp/prog/TEST)
==9290==  Uninitialised value was created by a heap allocation
==9290==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9290==    by 0x400715: init (in /home/snp/prog/TEST)
==9290==    by 0x400857: main (in /home/snp/prog/TEST)

以下代码重现错误:

#include <net/if.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>

#define TARGET "8.8.8.8"
#define DEVICE "eth0"

static int _function(void);

struct remote
{
    char *target;
    char device[IFNAMSIZ];
};

struct remote * st_args;

int start(void)
{
    return (_function());
}

int init(void)
{
    st_args = malloc (sizeof (struct remote));
    if (st_args == NULL)
        return (-1);

    st_args->target = malloc (sizeof (TARGET)+1);
    if (st_args->target == NULL) 
    {
        free (st_args);
        return (-1);
    }

    strncpy(st_args->target, TARGET , sizeof(TARGET)-1);
    strncpy(st_args->device, DEVICE, IFNAMSIZ-1);

   return 0;
}

void stop(void)
{
    if (st_args != NULL) 
    {
        free (st_args->target);
        free (st_args);
    }
}

static int _function(void)
{
    char cmd[256];

    memset(cmd, 0, sizeof(cmd));

    snprintf(cmd, sizeof(cmd), "ping -I %s %s", st_args->device, st_args->target);

    return 0;
}

int main(int argc, char **argv)
{
    init();
    start();
    stop(); 
   return 0;
}

我还是不明白,为什么valgrind不接受snprintf命令。此外,该数组包含行执行后的预期字符串。

Valgrind 的消息,

==9290== Conditional jump or move depends on uninitialised value(s)

是不言自明的:程序被观察到依赖于未初始化的内存来做出决定。在标准库函数中发生这种情况,很自然地假设函数参数有问题。由于您专门打印字符串,最可能的原因是其中一个字符串参数未终止。

事实上,至少有一个是。考虑这段代码:

#define TARGET "8.8.8.8"

[...]

strncpy(st_args->target, TARGET , sizeof(TARGET)-1);

为了安全起见,你搬起石头砸了自己的脚。 strncpy() 最多复制指定数量的字节,但它 不会 之后附加终止符。因此,其 Linux 手册页包含此警告:

Warning: If there is no null byte among the first n bytes of src, the string placed in dest will not be null terminated.

您已确保该警告中描述的情况会发生——没有写入空终止符,分配给 st_args->target 的最后一个字节仍未初始化。

由于您小心地为整个字符串分配了足够的 space,包括终止符,所以 strncpy() 无论如何都是过大的。只需使用 strcpy()。或者实际上,如果您的系统有 strdup() 或者您愿意编写一个实现,那么 strdup()malloc() + strcpy().

干净得多

或者,如果您想使用 strncpy(),那么最好通过在每个 strncpy() 调用的最后一个字节手动写入终止符来确保目标字符串终止目的地。在这种情况下,那将是

st_args->target[sizeof(TARGET)] = '[=12=]';

另请注意,您实际上分配的字节比您需要的多一个字节,因为 sizeof 字符串文字包含终止符。上面的代码是为实际的一字节太多分配编写的。