Valgrind reports uninitialized value inside standard library (vfprintf.c)

I have a function that does vsnsprintf

in a temporary buffer in an object created on the stack.

In the constructor of the object, I initialize the first character of the buffer to zero.

Valgrind complains about an uninitialized value created on the stack invfprintf.c

Complete working example below followed by valgrind output

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

struct tmp_buf
{
    tmp_buf() { *b = 0; }
    mutable char b[1024];
};

char const* va_stack_str(const char* format, va_list ap, const tmp_buf& b = tmp_buf())
{
    vsnprintf(b.b, sizeof(b.b), format, ap);
    return b.b;
}

char const* stack_str(const char* format, ...)
{
    va_list ap;
    va_start(ap, format);
    const char* str = va_stack_str(format, ap);
    va_end(ap);
    return str;
}

int main()
{
    printf("%s", stack_str("hello %s", "world"));
    return 0;
}

      

The app works as expected, but running it through valgrind complains about uninitialized values

My valgrind team valgrind --leak-check=full --track-origins=yes --quiet

Valgrind output:

==30513== Conditional jump or move depends on uninitialised value(s)
==30513==    at 0x4E828F3: vfprintf (vfprintf.c:1661)
==30513==    by 0x4E8B388: printf (printf.c:33)
==30513==    by 0x400A73: main (main.cpp:28)
==30513==  Uninitialised value was created by a stack allocation
==30513==    at 0x4E80BF6: vfprintf (vfprintf.c:235)
==30513== 
==30513== Syscall param write(buf) points to uninitialised byte(s)
==30513==    at 0x4F233B0: __write_nocancel (syscall-template.S:81)
==30513==    by 0x4EB0A82: _IO_file_write@@GLIBC_2.2.5 (fileops.c:1261)
==30513==    by 0x4EB1F5B: _IO_do_write@@GLIBC_2.2.5 (fileops.c:538)
==30513==    by 0x4EB3ADD: _IO_flush_all_lockp (genops.c:848)
==30513==    by 0x4EB3C39: _IO_cleanup (genops.c:1013)
==30513==    by 0x4E730FA: __run_exit_handlers (exit.c:95)
==30513==    by 0x4E73194: exit (exit.c:104)
==30513==    by 0x4E58ECB: (below main) (libc-start.c:321)
==30513==  Address 0x4025000 is not stack'd, malloc'd or (recently) free'd
==30513==  Uninitialised value was created by a stack allocation
==30513==    at 0x4E80BF6: vfprintf (vfprintf.c:235)

      

Changing the constructor tmp_buf

to the memset

entire buffer doesn't change the valgrind output

tmp_buf() { memset(b, 0, sizeof(b)); }

      

+3


source to share


1 answer


While I'm not very familiar with Valgrind, I see a clear problem in your code and can offer my best guess as to why Valgrind is complaining the way it is.

First, the problem:

The function va_stack_str

returns a pointer to an b

argument member b

that is of type const tmp_buf&

. Since this function does not control the lifetime of the object that this argument refers to, it returns a pointer whose validity can only be guaranteed until the end of the complete expression in which it is called. If the argument is b

initialized with a temporary one (this is how it is used stack_str

), then the end of the full expression exactly coincides with the duration of the correct pointer.

The function stack_str

continues to store the pointer returned va_stack_str

in a local variable str

and then returns it. By this time, the full expression in which it was called has va_stack_str

ended, so the pointer is hanging - it points to a buffer that was allocated on the stack but has since been freed.



The code works, probably because that part of the stack where the buffer existed was not overwritten by the time it was read and therefore still contains the contents of what used to be a buffer.

Why I think valgrind is releasing "uninitialized values":

vfprintf

of course allocates some stack space for local variables, some of which are probably allocated in the same stack folder, which used to be the buffer we asked to print. When it vfprintf

uses this buffer (the one we passed it in), Valgrind sees this memory not as our original buffer (which was freed), but as the address of the local variables allocated vfprintf

.

My guess is that one of these local variables is uninitialized at the point at which it is vfprintf

looking at the buffer we passed to it looking for a terminating NULL character. In this case, it parses memory pointing to its own uninitialized local variable, which usually won't happen because it vfprintf

initializes it later, but before it intends to use it. vfprintf

expects you to pass in a pointer to your allocated buffer, not the one that will eventually point to its own local variables!

+3


source







All Articles