Valgrind - snprintf:条件跳转或移动取决于未初始化的值 [英] Valgrind - snprintf : Conditional jump or move depends on uninitialised value(s)

查看:50
本文介绍了Valgrind - snprintf:条件跳转或移动取决于未初始化的值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

==9290== 条件跳转或移动取决于未初始化的值==9290== 在 0x4E82A03:vfprintf (vfprintf.c:1661)==9290== by 0x4EA9578: vsnprintf (vsnprintf.c:119)==9290== 由 0x4E8B531:snprintf (snprintf.c:33)==9290== by 0x400820:_function(在/home/snp/prog/TEST)==9290== by 0x4006D5:开始(在/home/snp/prog/TEST)==9290== by 0x40085C: main (in/home/snp/prog/TEST)==9290== 未初始化的值是由堆分配创建的==9290== 在 0x4C2AB80:malloc(在/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 #include #include #include #include #include #define 目标8.8.8.8"#define 设备eth0"静态 int _function(void);远程结构{字符*目标;字符设备[IFNAMIZ];};结构远程 * st_args;整数开始(无效){返回(_函数());}整数初始化(无效){st_args = malloc (sizeof (struct remote));如果(st_args == NULL)返回 (-1);st_args->target = malloc (sizeof (TARGET)+1);if (st_args->target == NULL){免费(st_args);返回 (-1);}strncpy(st_args->target, TARGET, sizeof(TARGET)-1);strncpy(st_args->设备,设备,IFNAMIZ-1);返回0;}无效停止(无效){如果(st_args != NULL){免费(st_args->目标);免费(st_args);}}静态 int _function(void){字符 cmd[256];memset(cmd, 0, sizeof(cmd));snprintf(cmd, sizeof(cmd), "ping -I %s %s", st_args->device, st_args->target);返回0;}int main(int argc, char **argv){在里面();开始();停止();返回0;}

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

解决方案

Valgrind 的消息,

<块引用>

==9290== 条件跳转或移动取决于未初始化的值

是不言自明的:观察到程序依赖于未初始化的内存来做出决定.发生在标准库函数中,正如它所做的那样,很自然地假设函数参数有问题.由于您专门打印字符串,因此最可能的原因是字符串参数之一未终止.

事实上,至少有一个是.考虑以下代码:

<块引用>

#define TARGET "8.8.8.8"

[...]

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

为了安全起见,你已经射中了自己的脚.strncpy() 最多复制指定数量的字节,但它不会在之后附加终止符.因此,它的 Linux 手册页包含以下警告:

<块引用>

警告:如果src的前n个字节中没有空字节,则放在dest 不会以 null 结尾.

您已经确保发生了该警告中描述的情况——没有写入空终止符,并且为 st_args->target 分配的最后一个字节保持未初始化.

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

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

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

还要注意,您实际上比需要的多一个字节,对于 sizeof 字符串文字包括终止符.上面的代码是为实际的一字节太多分配而编写的.

After launching a program thru valgrind I got the following message :

==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)

The following code reproduce the error :

#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;
}

I still does not understand the issue, why valgrind does not accept the snprintf command. In addition, the array contains the expected string after the line execution.

解决方案

Valgrind's message,

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

is reasonably self-explanatory: the program is observed to be relying on on uninitialized memory to make a decision. Happening in a standard library function as it does as it does, it is natural to suppose that there is something wrong with the function arguments. Since you're specifically printing strings, the most likely cause is that one of the string arguments is unterminated.

And indeed, at least one is. Consider this code:

#define TARGET "8.8.8.8"

[...]

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

In trying to be safe, you have shot yourself in the foot. strncpy() copies at most the specified number of bytes, but it does not append a terminator afterwards. Thus, its Linux manual page contains this warning:

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

You have ensured that the situation described in that warning takes place -- no null terminator is written, and the last byte allocated for st_args->target remains uninitialized.

Since you are careful to allocate enough space for the full string to begin with, including the terminator, the strncpy() is overkill anyway. Just use strcpy(). Or indeed, if your system has strdup() or you're willing to write an implementation, then strdup() is much cleaner than malloc() + strcpy().

Alternatively, if you want to use strncpy() then it's a good idea to ensure that the destination string is terminated by following up each strncpy() call by manually writing a terminator to the last byte of the destination. In this case, that would be

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

Note also that you actually allocate one more byte than you need, for the sizeof a string literal includes the terminator. The code just above is written for that actual one-byte-too-many allocation.

这篇关于Valgrind - snprintf:条件跳转或移动取决于未初始化的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆