1

I am testing valgrind and have this small C program that leaks 4 bytes:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int* x = malloc(sizeof(int));
    printf( "Address: %p\n", x);
    return 0;
}

I compile it with: gcc -g -o leak leak.c, and run it:

$ leak
Address: 0x55a72e303260
$ leak
Address: 0x55f370273260

So it shows two different addresses for two separate runs. However if I run it under valgrind it always shows the same address: 0x4a66040:

$ valgrind --leak-check=full --show-leak-kinds=all  leak
==8186== Memcheck, a memory error detector
==8186== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==8186== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==8186== Command: leak
==8186== 
Address: 0x4a66040
==8186== 
==8186== HEAP SUMMARY:
==8186==     in use at exit: 4 bytes in 1 blocks
==8186==   total heap usage: 2 allocs, 1 frees, 1,028 bytes allocated
==8186== 
==8186== 4 bytes in 1 blocks are definitely lost in loss record 1 of 1
==8186==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==8186==    by 0x109156: main (leak.c:6)
==8186== 
==8186== LEAK SUMMARY:
==8186==    definitely lost: 4 bytes in 1 blocks
==8186==    indirectly lost: 0 bytes in 0 blocks
==8186==      possibly lost: 0 bytes in 0 blocks
==8186==    still reachable: 0 bytes in 0 blocks
==8186==         suppressed: 0 bytes in 0 blocks
==8186== 
==8186== For counts of detected and suppressed errors, rerun with: -v
==8186== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Why is this so? And is it possible for valgrind to show the real address of the memory?

Håkon Hægland
  • 39,012
  • 21
  • 81
  • 174
  • But that is the "real" address (Always a fuzzy thing when talking about virtual memory) when running under valgrind and the allocator it uses. – Shawn Aug 20 '19 at 08:59
  • 2
    [Address space layout randomization](https://en.wikipedia.org/wiki/Address_space_layout_randomization) ensures that the address where the program runs is normally not consistent. Valgrind is a debugging tool so that would be unhelpful. The addresses are virtual, an application progam has no access to real addresses. – Weather Vane Aug 20 '19 at 09:00
  • @WeatherVane Does that mean that it not possible to free the memory later? For example, my test program printed address: `0x55a72e303260`, then I could not later create a program that did `void *addr = (void*)0x55a72e303260; free(addr)` ? – Håkon Hægland Aug 20 '19 at 09:10
  • You can only `free` the memory at an address that was allocated by `malloc` function family. Or in other words, you can only pass an address to `free` that was returned by `malloc` etc. – Weather Vane Aug 20 '19 at 09:13
  • @WeatherVane Yes I did allocate it by `malloc`, right? So can it then be `free`d later is the question – Håkon Hægland Aug 20 '19 at 09:17
  • In that case why should it not be possible to `free` the memory later? Unless you corrupted memory, or altered the value of the pointer you were returned. I don't see the point of hard-coding a pointer value: because it won't be the same from one run to the next. – Weather Vane Aug 20 '19 at 09:19
  • So it is not possible to free leaked memory later even if you know the address of the leaked memory? – Håkon Hægland Aug 20 '19 at 09:21
  • You should keep track of the addresses that are returned by `malloc()` and friends so that you can pass them to `free()` later. Not doing so is asking for trouble. – marcolz Aug 20 '19 at 09:22
  • Ok, I think you misunderstood the question. Thanks for the help anyway! – Håkon Hægland Aug 20 '19 at 09:23
  • 1
    Right, your main question was probably answered by the first comment by @WeatherVane. I have updated my answer as well. – marcolz Aug 20 '19 at 09:29
  • @marcolz So then the answer is: it is not possible to free the memory later, even if you know the address because of the randomization feature. – Håkon Hægland Aug 20 '19 at 09:32
  • A "leak" means you haven't `free`d memory. There's no reason why you can't `free` memory, but copying the address given by Valgrind isn't the way to do it (and won't be repeatable in the proper version): you should find out *why* there is a leak, instead of trying to use sticking plaster. – Weather Vane Aug 20 '19 at 09:36
  • @WeatherVane Ok, thanks I agree on finding the cause of the leak, but my question was about freeing leaked memory from another process. I guess this leaves me still confused, and I will rather ask a new question than continuing this discussion in the comments.. – Håkon Hægland Aug 20 '19 at 09:40
  • Well, you are correct in that you *can* `free()` any address that was previously allocated by `malloc()` and friends. – marcolz Aug 20 '19 at 09:46
  • @marcolz Added [follow-up question](https://stackoverflow.com/q/57571539/2173773) – Håkon Hægland Aug 20 '19 at 10:24

1 Answers1

5

It is the "real" address of the allocated memory.

Valgrind replaces your malloc() implementation with its own version, so that it can do what it does, see the line:

==8186==    at 0x483874F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)

So it is the underlying call to obtain memory that differs between your "normal" libc implementation of malloc() and that of valgrind's implementation. Likely libc uses brk() and valgrind uses mmap().

brk() follows the rules of Address Space Layout Randomization while mmap() allows for explicitly selecting the virtual memory address where to map the newly allocated memory.

Edit: strace of both versions, shows:

libc:

brk(NULL)                               = 0x5611e25ec000
brk(0x5611e260d000)                     = 0x5611e260d000
fstat(1, {st_dev=makedev(0, 22), st_ino=67, st_mode=S_IFCHR|0620, st_nlink=1, st_uid=1000, st_gid=5, st_blksize=1024, st_blocks=0, st_rdev=makedev(136, 64), st_atime=1566292432 /* 2019-08-20T11:13:52.993864629+0200 */, st_atime_nsec=993864629, st_mtime=1566292432 /* 2019-08-20T11:13:52.993864629+0200 */, st_mtime_nsec=993864629, st_ctime=1564479628 /* 2019-07-30T11:40:28.009864433+0200 */, st_ctime_nsec=9864433}) = 0
write(1, "Address: 0x5611e25ec260\n", 24) = 24

valgrind:

mmap(0x4c2c000, 2158912, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0) = 0x4c2c000
...
write(1027, "==18842==    at 0x4C2FB0F: mallo"..., 91) = 91

So in fact Valgrind explicitly chooses where to map its memory pool.

marcolz
  • 2,880
  • 2
  • 23
  • 28
  • If I run the program under `valgrind` repeatedly it always prints: `Address: 0x4a66040` why is it always the same? – Håkon Hægland Aug 20 '19 at 09:03
  • 1
    Because the valgrind implementation of `malloc()` is not affected by - or possibly explicitly avoids - address space randomization. – marcolz Aug 20 '19 at 09:10
  • So the reported address `0x4a66040` is not the real address of the allocated memory, but some address in valgrinds buffers. That's why valgrind can free `0x4a66040` for each run, and be able to reallocate the same buffer at the exactly the same place at the next run? – Håkon Hægland Aug 20 '19 at 09:15
  • 1
    Yes, I just edited my answer with `strace` output, showing that valgrind explicitly selects where its buffer is allocated in the virtual address space of your process. – marcolz Aug 20 '19 at 09:17