Bug 381299

Summary: false uninit on new page via sbrk(n)
Product: [Developer tools] valgrind Reporter: John Reiser <jreiser>
Component: memcheckAssignee: Julian Seward <jseward>
Status: RESOLVED NOT A BUG    
Severity: normal CC: ivosh
Priority: NOR    
Version First Reported In: 3.13 SVN   
Target Milestone: ---   
Platform: Compiled Sources   
OS: Linux   
Latest Commit: Version Fixed/Implemented In:
Sentry Crash Report:

Description John Reiser 2017-06-16 19:51:24 UTC
Memcheck generates a false positive uninitialized complaint when the target program uses a value from a new page that was [just] allocated via sbrk(n).  The operating system guarantees that new pages are all zero, so memcheck should not complain.

===== vg-brk.c test case
#include <stdio.h>
#include <unistd.h>

#define PAGE_SIZE (1u<<12)
#define PAGE_MASK -PAGE_SIZE

int
main(int argc, char *argv[])
{
	void *p0 = sbrk(0);
	printf("p0=%p  from sbrk(0)\n", p0);
	void *p1 = (void *)(PAGE_MASK & (-1+ PAGE_SIZE + (long)p0));
	int r1 = brk(p1);
	printf("p1=%p  p0 rounded up to page boundary  r1=%d\n", p1, r1);
	void *p2 = sbrk(0x1000);
	printf("p2=%p  new page was allocated here\n", p2);
	void *p3 = sbrk(0x1000);
	printf("p3=%p  new page was allocated here\n", p3);
	printf("\n");
	printf("will access %p\n", p2);
	printf("%d\n", *(int *)p2);
	return 0;
}
=====

$ gcc -g -o vg-brk vg-brk.c
$ valgrind-3.13.0/bin/valgrind --track-origins=yes ./vg-brk
==18003== Memcheck, a memory error detector
==18003== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==18003== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==18003== Command: ./vg-brk
==18003== 
p0=0x4228000  from sbrk(0)
p1=0x4228000  p0 rounded up to page boundary  r1=0
p2=0x4228000  new page was allocated here
p3=0x4229000  new page was allocated here

will access 0x4228000
==18003== Conditional jump or move depends on uninitialised value(s)
==18003==    at 0x4E8844A: vfprintf (in /usr/lib64/libc-2.24.so)
==18003==    by 0x4E906D8: printf (in /usr/lib64/libc-2.24.so)
==18003==    by 0x4006C2: main (vg-brk.c:21)
==18003==  Uninitialised value was created
==18003==    at 0x4F37579: brk (in /usr/lib64/libc-2.24.so)
==18003==    by 0x4F37658: sbrk (in /usr/lib64/libc-2.24.so)
==18003==    by 0x40064D: main (vg-brk.c:15)
==18003== 
  [[snip]]
Comment 1 Ivo Raisr 2017-06-16 20:06:33 UTC
Agreed. However great care needs to be taken to distinguish between *first* such allocated space or a *subsequent* one.


brk/sbrk were part of POSIX 1997 (and removed in POSIX 2001).
The standard said by that time [1]:
"The newly-allocated space is set to 0. However, if the application first decrements and then increments the break value, the contents of the reallocated space are unspecified."

Linux man pages do not say anything about initial contents.

Solaris man page brk(2) says:
"Newly allocated space is set to zero. If, however, the same memory space is reallocated to the same process its contents are undefined."


[1] http://pubs.opengroup.org/onlinepubs/7908799/xsh/brk.html
Comment 2 John Reiser 2017-06-16 20:33:02 UTC
I believe that the test case takes appropriate care so that the memory read access via "*(int *)p2" is to a new page.  The "rounded up", and always incrementing the brk(), and never decrementing it, takes care of that.

Linux guarantees that *new* pages are all zero; I believe that Solaris does, too.  The purpose of the caution in the Solaris manual is to remind the programmer that any *re*-allocated address space (an increment after a decrement) can be undefined (and does not necessarily retain its old values, or *any* known values.)

The test case has a bug on some 64-bit systems: the PAGE_MASK must be at least as wide as uintptr_t:
    #define PAGE_SIZE (1ul<<12)
where the 'l' was omitted in the original.  The bug does not affect the results for current x86_64 Linux using ET_EXEC, because all the addresses happen to be in the lower 32 bits.
Comment 3 John Reiser 2017-06-16 21:01:45 UTC
The printf() performs some allocation that invalidates the assumption that p1 still is the highest address.

Closing as Invalid bug report.