1

According to the manual it doesn't:

It is important to understand that your program can copy around junk (uninitialised) data as much as it likes. Memcheck observes this and keeps track of the data, but does not complain. A complaint is issued only when your program attempts to make use of uninitialised data in a way that might affect your program's externally-visible behaviour.

The question is if there's some important reason to behave this way? Is there a (commonly) used construct that's copies uninitialized data that would trigger false positives? Or is there a way to make valgrind complain about this?

My concern was that in C the use of uninitialized variables have undefined behavior (IIRC), so for example the following functions could emit nasal daemons:

int fubar(void) {
     int a;

     return a;
}

now I recalled incorrectly, it's only in certain situations it's undefined, for example if you're doing arithmetics with uninitialized variables:

int fubar(void) {
    int a;

    a -= a;
    return a;
}

So the same question arises here. Is there some important reason for valgrind to allow arithmetics with uninitialized data? etc. Note that especially if it were floating point data it could actually alter externally observable behaviour as trapping on FP-errors might be enabled.

skyking
  • 13,817
  • 1
  • 35
  • 57
  • The copying of uninitialized data is explicitly *well-defined* in certain cases (ISO 9899, 6.2.6 Representation of types) to enable e.g. `memcpy` and `memmove` to function. `int main() { unsigned char a; unsigned char b = a; }` is actually well-formed, if ill-advised. – DevSolar Feb 26 '16 at 12:01
  • @DevSolar Yes, I've updated the question to cover the case where you do `a -= a` on uninitialized `a` - that at least is if I understand it correctly undefined behavior (according to http://stackoverflow.com/questions/11962457/why-is-using-an-uninitialized-variable-undefined-behavior-in-c). – skyking Feb 26 '16 at 12:08
  • `a -= a;` is not necessarily undefined, [see here](http://stackoverflow.com/questions/25074180/is-aa-or-a-a-undefined-behaviour-if-a-is-not-initialized/25074258#25074258) – M.M Feb 26 '16 at 12:21

1 Answers1

3

Yes, this happens quite often. For example:

struct {
    char a;
    int b;
} s1, s2;

s1.a = '.';
s1.b = 31337;

memcpy (&s2, &s1, sizeof(s1));

Here, you are copying uninitialized bytes (the padding bytes between a and b).

I think, it is good that valgrind doesn't complain here.

Generally spoken (in response to your arithmetic example):

Valgrind tries to only complain when uninitialized data can be the cause of non-deterministic behaviour of your program. Or the other way round: It does not complain, if it can rule out that this is the case. So, for example, when a branch or syscall parameter depends on uninitialized data valgrind complains. You can try that out by putting exit(a) after a -= a;, which results in

Syscall param exit_group(status) contains uninitialised byte(s)

even when usually the result will be 0 in any case. (You might have to declare a volatile to prevent removing it from the code)

Ctx
  • 18,090
  • 24
  • 36
  • 51
  • I've updated the question regarding using uninitialized data in arithmetics. Do you have a good idea for that too? – skyking Feb 26 '16 at 11:53
  • Padding bytes are not uninitialized. *6.2.6.1.,p6: When a value is stored in an object of structure or union type, including in a member object, the bytes of the object representation that correspond to any padding bytes take unspecified values.* Anyway, padding bytes aren't an object, so they cannot be uninitialized. – 2501 Feb 26 '16 at 11:59
  • 1
    @2501 I just checked the resulting assembly, the padding bytes are not touched with gcc. – Ctx Feb 26 '16 at 12:07
  • @Ctx Irrelevant. See definition of unspecified. The C language is defined by the relevant text, not the output of the assembly of a compiler. – 2501 Feb 26 '16 at 12:08
  • 1
    @2501 Maybe, but for this question it _is_ quite relevant. – Ctx Feb 26 '16 at 12:09
  • @Ctx: Taking the "object representation" of something -- your anon struct for example -- and copying it, `unsigned char` by `unsigned char`, is explicitly well-defined behaviour for this very purpose -- so that `memcpy` and `memmove` could be implemented in generic C (casting their `void *` parameters to `unsigned char *`). That GCC does some special magic here is somewhat surprising, but by no means necessary. Anyway, good answer. – DevSolar Feb 26 '16 at 12:11
  • @DevSolar I meant, they are not touched when initializing the struct members a and b. memcpy() of course happily copies them. – Ctx Feb 26 '16 at 12:16
  • Ah. Ok, my misunderstanding. (I *was* wondering why they would do that. ;-) ) – DevSolar Feb 26 '16 at 12:17
  • @2501 padding bytes in a struct are part of the struct object – M.M Feb 26 '16 at 12:24
  • I think gcc zero-initializes padding bytes (during initialization) and copies them on struct assignment, to enable the idiom of using `memcmp` to compare two structs that may have been assigned – M.M Feb 26 '16 at 12:32
  • @M.M As I said, I have checked the assembly and it doesn't. For example (with the above struct) `struct { ... } s3 = { 10, 20 };` results in a `movb` and a `movl` on a stack area of 8 bytes, leaving the 3 padding bytes untouched. – Ctx Feb 26 '16 at 12:41