Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invalid write/read valgrind errors, no solution found in other questions

Tags:

c

valgrind

I'm writing C code for a homework assignment that replicates main memory by a dynamic array of memory segments.

These memory segments come from a different interface, which is itself just a static array of uint32_ts.

My main memory interface is called heapmem (as in, heap memory), and I've been getting strange valgrind read/write errors since the switch. Before chewing me out, I have looked and researched and am coming to SO as a last resort.

Here's the error

==30352== Invalid write of size 8
==30352==    at 0x401661: HeapMem_map (heapmem.c:84)
==30352==    by 0x400E74: map (um.c:109)
==30352==    by 0x4010FD: runOpcode (um.c:182)
==30352==    by 0x4011A1: UM_run (um.c:209)
==30352==    by 0x400A71: main (main.c:10)
==30352==  Address 0x4c53b00 is 0 bytes after a block of size 16 alloc'd
==30352==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==30352==    by 0x401425: HeapMem_new (heapmem.c:32)
==30352==    by 0x400ABE: UM_new (um.c:31)
==30352==    by 0x400A64: main (main.c:8)
==30352== 
==30352== Invalid read of size 8
==30352==    at 0x401787: HeapMem_put (heapmem.c:114)
==30352==    by 0x400D38: sstore (um.c:90)
==30352==    by 0x401090: runOpcode (um.c:167)
==30352==    by 0x4011A1: UM_run (um.c:209)
==30352==    by 0x400A71: main (main.c:10)
==30352==  Address 0x4c53b00 is 0 bytes after a block of size 16 alloc'd
==30352==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==30352==    by 0x401425: HeapMem_new (heapmem.c:32)
==30352==    by 0x400ABE: UM_new (um.c:31)
==30352==    by 0x400A64: main (main.c:8)
==30352== 
==30352== Invalid read of size 8
==30352==    at 0x401956: car_double (heapmem.c:151)
==30352==    by 0x401640: HeapMem_map (heapmem.c:82)
==30352==    by 0x400E74: map (um.c:109)
==30352==    by 0x4010FD: runOpcode (um.c:182)
==30352==    by 0x4011A1: UM_run (um.c:209)
==30352==    by 0x400A71: main (main.c:10)
==30352==  Address 0x4c53b00 is 0 bytes after a block of size 16 alloc'd
==30352==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==30352==    by 0x401425: HeapMem_new (heapmem.c:32)
==30352==    by 0x400ABE: UM_new (um.c:31)
==30352==    by 0x400A64: main (main.c:8)
==30352== 
==30352== Invalid read of size 8
==30352==    at 0x40174A: HeapMem_get (heapmem.c:108)
==30352==    by 0x400CD9: sload (um.c:86)
==30352==    by 0x401079: runOpcode (um.c:164)
==30352==    by 0x4011A1: UM_run (um.c:209)
==30352==    by 0x400A71: main (main.c:10)
==30352==  Address 0x4c7e0f0 is 0 bytes after a block of size 4,096 alloc'd
==30352==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==30352==    by 0x401923: car_double (heapmem.c:148)
==30352==    by 0x401640: HeapMem_map (heapmem.c:82)
==30352==    by 0x400E74: map (um.c:109)
==30352==    by 0x4010FD: runOpcode (um.c:182)
==30352==    by 0x4011A1: UM_run (um.c:209)
==30352==    by 0x400A71: main (main.c:10)

Here are functions in code giving me errors:

//  Heap Memory Structure
struct T {
   Stack_T SegID_stack;
   MemSeg_T* HeapMem_car;
   int length, highest;
};

//  Create a new heap memory structure
T HeapMem_new (MemSeg_T program) {
    assert (program);
    T retHeap = malloc(sizeof(*retHeap));
    Stack_T structStack = Stack_new ();
    retHeap->length = INIT_SIZE;
    retHeap->highest = 0;
    MemSeg_T* structCar = malloc(INIT_SIZE * sizeof(*structCar));
    //  Fill the array with NULL ptrs
    for (int i = 0; i < INIT_SIZE; i++) {
        structCar[i] = NULL;
    }
    retHeap->HeapMem_car = structCar;
    retHeap->SegID_stack = structStack;
    //  We'll be using the map function to initialize
    //  the heap with a program at the 0th segment.
    HeapMem_map (retHeap, MemSeg_length (program));
    retHeap->HeapMem_car[PROGRAM_LOC] = program;
    return retHeap;
}

//  Line 84
heapmem->HeapMem_car[toMap] = segment;
//  Line 114
MemSeg_T segToPut = heapmem->HeapMem_car[toPut];
//  Line 151
newCar[i] = heapmem->HeapMem_car[i];
//  Line 108
MemSeg_T wordSeg = heapmem->HeapMem_car[toGet];

Rest of code available here.

like image 627
James Roseman Avatar asked Dec 20 '22 13:12

James Roseman


1 Answers

First a small dissection of one of your errors:

==30352== Invalid write of size 8
==30352==    at 0x401661: HeapMem_map (heapmem.c:84)
==30352==    by 0x400E74: map (um.c:109)
==30352==    by 0x4010FD: runOpcode (um.c:182)
==30352==    by 0x4011A1: UM_run (um.c:209)
==30352==    by 0x400A71: main (main.c:10)
==30352==  Address 0x4c53b00 is 0 bytes after a block of size 16 alloc'd
==30352==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==30352==    by 0x401425: HeapMem_new (heapmem.c:32)
==30352==    by 0x400ABE: UM_new (um.c:31)
==30352==    by 0x400A64: main (main.c:8)

Note the bottom of this list is telling you where an allocation happened. The top is telling you how it was misused. In this case, you're walking past the end of the requested allocation by 8 bytes exactly.

You will notice all of the overruns in this and the remaining violations are reaching beyond their means by exactly the same offset (8 bytes). Further examination of the referenced code shows it seems to always be the same array. This is actually a good thing, as it becomes very likely an issue of simply incorrectly calculating how may data items are present and reaching either one or two beyond your allowed space

In this case, the item being breached appears to be a dynamic allocated list of pointers (heapmem->HeapMem_car[]). Running on a machine with 64-bit pointers would make each one 8-bytes wide, therefore you're likely simply off-by-one in the last-element-accessible of this allocation, and in C, that generally always means at some point you allocated N items and then accessed array[N] forgetting the limit is N-1. All of the above access violations appear to be centered around faith that indexes into that array are not out-of-bounds, yet valgrind is reporting they are. I suggest you slip some assert()s into those access points and break on violation to see how you got there. Oh wait.. valgrind has that info for you already. Look at that lovely call-stack. Hmmmm...

So why does it seem to work even with these breaches? A number of possibilities. If you don't step outside the allocated memory far - and all addresses here are 0 bytes after - (these are pointers after all, so pray they're NULL) there's a good chance that you don't overwrite vital data and the programme seems to work. Until the allocations land somewhere else suddenly and you step over a page boundary. Overshoot that and kerboom.

Thanks to Daniel Fischer for contribution on the second part of this answer (why it seems to work).

like image 161
WhozCraig Avatar answered Dec 24 '22 00:12

WhozCraig