2016-11-04 7 views
0

После запуска программы через Valgrind я получил следующее сообщение:Valgrind - snprintf: Условный переход или шаг зависит от неинициализированного значения (ов)

==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. Кроме того, массив содержит ожидаемую строку после выполнения строки.

+0

Я полагаю, что valgrind не знает о состоянии 'st_args'? Возможно, попробуйте использовать локальный 'st_args'. – Jeremy

+0

'strncpy (st_args-> target, TARGET, sizeof (TARGET) -1);' плохие привычки. (malloc делает ** не ** инициализирует память для всех нулей!) ... и аналогично для 'IFNAMSIZ-1' – joop

+0

. Вы как-то недоумеваете, что используете подходящий инструмент для работы (' snprintf') и неправильный инструмент для любого задания ('strncpy'). –

ответ

2

сообщение VALGRIND, в

== 9290 == Условного перехода или шаг зависит от неинициализированного значения (ов)

разумно сам за себя: программа наблюдается полагаться на на неинициализированная память для принятия решения. Случае в стандартной библиотечной функции, как и в случае с ней, естественно предположить, что с аргументами функции что-то не так. Поскольку вы специально печатаете строки, наиболее вероятной причиной является то, что один из аргументов строки не уничтожен.

И действительно, хотя бы один из них. Рассмотрим этот код:

#define TARGET "8.8.8.8" 

[...]

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

Пытаясь обезопасить себя, вы выстрелили себе в ногу. strncpy() копирует не более указанного количества байтов, но не после этого добавляет терминатор. Таким образом, его страница Linux руководства содержит следующее предупреждение:

Предупреждения: Если нет нулевых байт среди первых n байт src, строка помещается в dest не будет нуль.

Вы обеспечили, что ситуация, описанная в этом предупреждении происходит - не нуль-терминатор не записывается, и последний байт выделяется для st_args->target остается неинициализированным.

Поскольку вы стараетесь выделить достаточное пространство для полной строки для начала, включая терминатор, strncpy() в любом случае является излишним. Просто используйте strcpy(). Или, действительно, если ваша система имеет strdup() или вы хотите написать реализацию, то strdup() намного чище, чем malloc() + strcpy().

В качестве альтернативы, если вы хотите использовать strncpy(), то это хорошая идея, чтобы гарантировать, что конечная строка прекращается, следуя за каждым вызовом strncpy(), вручную записывая терминатор в последний байт адресата.В этом случае, это было бы

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

Заметим также, что вы на самом деле выделить еще один байт, чем вам нужно, для sizeof строковый литерал включает терминатор. Код, описанный выше, написан для этого фактического распределения по одному байту слишком большого числа.

+0

спасибо за ваше объяснение! Последний оставшийся вопрос, вы сказали, что 'strdup()' более безопасно и чище. Я собираюсь использовать 'strdup()' вместо 'strncpy()', чтобы сохранить адрес цели, но мне все еще нужно использовать 'strncpy()' для имени устройства, потому что нет лучшего способа, не так ли? – ogs

+0

@SnP, да, вы не можете использовать 'strdup()', когда желаемым местом назначения является массив. Поэтому для случая 'st_args-> device, я рекомендую использовать' strncpy() 'и после этого принудительно завершать завершение строки, как я уже описал, или сначала проверить длину исходной строки и использовать обычный' strcpy() ', если это ОК. –