From: Dallman, J. <joh...@si...> - 2008-04-24 15:18:23
|
Julian Seward [mailto:js...@ac...] write: > Sigh. Then gcc is generating code which is in violation of ISO > in that it reads beyond the end of an allocated block. Sure seems to be. Here's the simple example: /* =============================================================== gcc_struct_bug.c Demonstration program for a problem with GCC and malloc'ed structs on 64-bit x86 Linux, demonstrated with Valgrind 3.3.0's Memcheck. The example Linux was 64-bit SuSE Linux Enterprise Server 10 with Service Pack 1, and the GCC supplied with it: "gcc (GCC) 4.1.2 20070115 (prerelease) (SUSE Linux)". Build with "gcc -m64 -g -O0 gcc_struct_bug.c -o gcc_struct_bug." Verify that it runs and then run it through Valgrind. The error that comes out is: ==25937== Invalid read of size 8 ==25937== at 0x40069F: main (gcc_struct_bug.c:74) ==25937== Address 0x4d55030 is 0 bytes inside a block of size 6 alloc'd ==25937== at 0x4A1FDEB: malloc (vg_replace_malloc.c:207) ==25937== by 0x400672: main (gcc_struct_bug.c:69) That happens because at point A, marked below, GCC generates a single 8-byte load to get the structure at *c into a register for passing to set_colour(). Since the malloc'ed block is only six bytes, this involves reading off the end. The GDB disassembly of that line, on my example machine, reads: 0x000000000040069b <main+65>: mov 0xfffffffffffffff8(%rbp),%rax 0x000000000040069f <main+69>: mov (%rax),%rdi 0x00000000004006a2 <main+72>: callq 0x400628 <set_colour> This breaks ISO C rules in that it reads off the end of a malloc'ed block. It is not harmless: were the structure six bytes before a page boundary - easily achieved if it were an element of a larger structure - and the following page mprotect'ed, the read would cause a segmentation violation. The problem is suppressed in this simple example at -O3, seemingly through the variables being optimised out of existence, but that isn't applicable to the far more complex code within which the problem was discovered, which doesn't work at -O3. Using GCC's -fmudflap -lmudflap options suppresses the valgrind error, but causes a great deal more memory allocation and freeing. =============================================================== */ #include <stdio.h> #include <stdlib.h> typedef struct colour_s { unsigned short red; unsigned short green; unsigned short blue; } colour_t; void set_colour( colour_t col) { printf( "colour is %d %d %d\n", col.red, col.green, col.blue); } int main( int argc, char *argv[]) { colour_t *c; if( (c = (colour_t*)malloc( sizeof(colour_t))) != NULL) { c->red = 115; c->green = 122; c->blue = 98; set_colour( *c); /* Point A - GCC uses an eight-byte load to load a six-byte structure into a register for passing, which involves reading off the end of the malloc'ed block. */ free( c); /* Free the memory */ exit( 0); } else { printf( "Failed to allocate a colour_t\n"); exit(1); } } -- John Dallman Parasolid Porting Engineer |