Difference between revisions of "Heapusage"
(Created page with "== Summary == This documentation describes the usage of heapusage developed by Kristofer Berggren (d99kris) (https://github.com/d99kris/heapusage). With practical examples it is shown how this tool can be used to find memory leaks, double free, use after free and overflows. == Requirements == According to the GitHub documentation, heapusage can be used on Linux and macOS and has been tested on these operating systems: * macOS Big Sur 11.0 * Ubuntu 20.04 LTS During c...") |
m (Alternatives added) |
||
Line 9: | Line 9: | ||
* macOS Big Sur 11.0 | * macOS Big Sur 11.0 | ||
* Ubuntu 20.04 LTS | * Ubuntu 20.04 LTS | ||
== Alternatives == | |||
* AddressSanitizer (https://github.com/google/sanitizers/wiki/AddressSanitizer) | |||
* Electric Fence (https://linux.die.net/man/3/efence) | |||
* mtrace (https://man7.org/linux/man-pages/man3/mtrace.3.html) | |||
According to the GitHub documentation, heapusage can be used on Linux and macOS and has been tested on these operating systems: | |||
* macOS Big Sur 11.0 | |||
* Ubuntu 20.04 LTS | |||
During creation of this documentation it was also successfully tested on: | During creation of this documentation it was also successfully tested on: |
Revision as of 21:35, 16 January 2024
Summary
This documentation describes the usage of heapusage developed by Kristofer Berggren (d99kris) (https://github.com/d99kris/heapusage). With practical examples it is shown how this tool can be used to find memory leaks, double free, use after free and overflows.
Requirements
According to the GitHub documentation, heapusage can be used on Linux and macOS and has been tested on these operating systems:
- macOS Big Sur 11.0
- Ubuntu 20.04 LTS
Alternatives
- AddressSanitizer (https://github.com/google/sanitizers/wiki/AddressSanitizer)
- Electric Fence (https://linux.die.net/man/3/efence)
- mtrace (https://man7.org/linux/man-pages/man3/mtrace.3.html)
According to the GitHub documentation, heapusage can be used on Linux and macOS and has been tested on these operating systems:
- macOS Big Sur 11.0
- Ubuntu 20.04 LTS
During creation of this documentation it was also successfully tested on:
- Ubuntu 22.04.3 LTS
Installation
Step 1 - Pre-requisites
To use heapusage on Ubuntu, the following pre-requisites need to be installed:
sudo apt install git cmake build-essential
To show the source filenames and line-numbers in the call stacks, another pre-requisite can be installed optionally:
sudo apt install binutils-dev
Step 2 - Installing heapusage
To install heapusage simply clone the GitHub repository and build it:
git clone https://github.com/d99kris/heapusage cd heapusage mkdir -p build cd build cmake .. make -s
Optionally, you can install it in the system:
sudo make install
If it is installed in the system, you can simply run it using heapusage
. More information about using heapusage can be found in the Usage section.
Usage
General
Heapusage can be used as follows:
heapusage [-d] [-m minsize] [-n] [-o path] [-t tools] PROG [ARGS..]
This parameters can be used:
-d
(optional) enables the debug mode.-m <minsize>
(optional) sets the minimum allocation size heapusage is enabled for. For example, usingheapusage -t leak -m 50./app
only shows memory leaks larger than or equal to 50 bytes.-n
(optional) disables symbol lookup. This leads to a faster execution time, however less information is shown in the output. You can still see for example how many bytes are leaked, but no longer in which file/function.-o <path>
(optional) sets an output file to write the output to, instead of stderr.-t <tools>
(optional) sets which tools should be used for the analysis. If not specified, the toolleak
is used per default. The possible tools can be found below.PROG
sets the program to be analysed.ARGS...
(optional) are the parameters for the analysed program (PROG).
Furthermore, heapusage can be run using
heapusage --help heapusage --version
to show an overview of the usage or the installed version.
Tools
As mentioned above, using the -t <tools>
parameters, the tools to be used can be specified.
The following tools can be used:
leak
(default) detects memory allocations which are never free'd.double-free
detects when an already free'd buffer is free'd again.use-after-free
detects when an already free'd buffer is used afterwards.overflow
detects buffer overflows, like when an address beyond the allocated memory is accessed.all
enables all tools.
Multiple tools can be combined by separating them with a comma, like -t leak,double-free
.
-t all
is therefore the same as -t leak,double-free,use-after-free,overflow
.
Examples
In the following, a simple example for each of the tools is shown. Each of these examples is saved as .c
-file and compiled using gcc <filename>.c -o <output-filename>
.
leak
ex1-leak.c:
#include <stdlib.h> void* getPointer(int size) { return malloc(size); } int main() { void* p = getPointer(1337); p = getPointer(42); free(p); return 0; }
After compiling this code it is executed using heapusage: heapusage -t leak ./ex1
This leads to the following output:
==6367== Heapusage - https://github.com/d99kris/heapusage ==6367== ==6367== HEAP SUMMARY: ==6367== in use at exit: 1337 bytes in 1 blocks ==6367== total heap usage: 2 allocs, 79 frees, 1379 bytes allocated ==6367== peak heap usage: 1379 bytes allocated ==6367== ==6367== 1337 bytes in 1 block(s) are lost, originally allocated at: ==6367== at 0x00007fc5942aee5c: malloc (humain.cpp:175) ==6367== at 0x000055c4d3b9d185: getPointer ==6367== at 0x000055c4d3b9d19d: main ==6367== at 0x00007fc594029d90: __libc_start_call_main (libc_start_call_main.h:58) ==6367== at 0x00007fc594029e40: __libc_start_main (libc-start.c:128) ==6367== at 0x000055c4d3b9d0a5: _start ==6367== ==6367== LEAK SUMMARY: ==6367== definitely lost: 1337 bytes in 1 blocks ==6367==
It can be seen, that the second pointer initialization of 42 bytes which is free'd again is not shown here, whereas the 1337 bytes are shown as memory leak, since the are allocated and never free'd.
double-free
ex2-doubleFree.c:
#include <stdlib.h> void* getPointer(int size) { return malloc(size); } void free1(void* p) { free(p); } void free2(void* p) { free(p); } int main() { void* p = getPointer(1337); free1(p); free2(p); return 0; }
The similar functions free1
and free2
are used for a better demonstration of the heapusage output.
heapusage -t double-free ./ex2
:
==6438== Heapusage - https://github.com/d99kris/heapusage ==6438== ==6438== Invalid deallocation at: ==6438== at 0x00007f16a3399f4f: free (humain.cpp:188) ==6438== at 0x00005648bfc3f1c2: free2 ==6438== at 0x00005648bfc3f1f7: main ==6438== at 0x00007f16a3029d90: __libc_start_call_main (libc_start_call_main.h:58) ==6438== at 0x00007f16a3029e40: __libc_start_main (libc-start.c:128) ==6438== at 0x00005648bfc3f0a5: _start ==6438== Address 0x5648c0f6a190 is a block of size 1337 free'd at: ==6438== at 0x00007f16a3399f4f: free (humain.cpp:188) ==6438== at 0x00005648bfc3f1a3: free1 ==6438== at 0x00005648bfc3f1eb: main ==6438== at 0x00007f16a3029d90: __libc_start_call_main (libc_start_call_main.h:58) ==6438== at 0x00007f16a3029e40: __libc_start_main (libc-start.c:128) ==6438== at 0x00005648bfc3f0a5: _start ==6438== Block was alloc'd at: ==6438== at 0x00007f16a3399e5c: malloc (humain.cpp:175) ==6438== at 0x00005648bfc3f185: getPointer ==6438== at 0x00005648bfc3f1db: main ==6438== at 0x00007f16a3029d90: __libc_start_call_main (libc_start_call_main.h:58) ==6438== at 0x00007f16a3029e40: __libc_start_main (libc-start.c:128) ==6438== at 0x00005648bfc3f0a5: _start ==6438== ==6438== HEAP SUMMARY: ==6438== in use at exit: 0 bytes in 0 blocks ==6438== total heap usage: 1 allocs, 80 frees, 1337 bytes allocated ==6438== peak heap usage: 1337 bytes allocated ==6438== ==6438== LEAK SUMMARY: ==6438== definitely lost: 0 bytes in 0 blocks ==6438==
Here it shows an invalid deallocation in the free2
function and the reason for that, because the block was already free'd in the free1
function.
use-after-free
ex3-userAfterFree.c:
#include <stdlib.h> int* getIntPointer() { return (int*)malloc(sizeof(int)); } void free1(int* p) { free(p); } void setPointerValue(int* p, int value){ *p = value; } int main() { int *p = getIntPointer(); free1(p); setPointerValue(p, 42); return 0; }
heapusage -t use-after-free ./ex3
:
==6458== Heapusage - https://github.com/d99kris/heapusage ==6458== ==6458== Invalid memory access at: ==6458== at 0x00007fe4fb042520: __restore_rt (libc_sigaction.c:0) ==6458== at 0x0000559a8cbc71b2: setPointerValue ==6458== at 0x0000559a8cbc71ee: main ==6458== at 0x00007fe4fb029d90: __libc_start_call_main (libc_start_call_main.h:58) ==6458== at 0x00007fe4fb029e40: __libc_start_main (libc-start.c:128) ==6458== at 0x0000559a8cbc70a5: _start ==6458== Address 0x559a8d19b000 is 0 bytes inside a block of size 4 free'd at: ==6458== at 0x00007fe4fb3e9f4f: free (humain.cpp:188) ==6458== at 0x0000559a8cbc7199: free1 ==6458== at 0x0000559a8cbc71dd: main ==6458== at 0x00007fe4fb029d90: __libc_start_call_main (libc_start_call_main.h:58) ==6458== at 0x00007fe4fb029e40: __libc_start_main (libc-start.c:128) ==6458== at 0x0000559a8cbc70a5: _start ==6458== Block was alloc'd at: ==6458== at 0x00007fe4fb3e9e5c: malloc (humain.cpp:175) ==6458== at 0x0000559a8cbc717b: getIntPointer ==6458== at 0x0000559a8cbc71cd: main ==6458== at 0x00007fe4fb029d90: __libc_start_call_main (libc_start_call_main.h:58) ==6458== at 0x00007fe4fb029e40: __libc_start_main (libc-start.c:128) ==6458== at 0x0000559a8cbc70a5: _start ==6458== ==6458== HEAP SUMMARY: ==6458== in use at exit: 0 bytes in 0 blocks ==6458== total heap usage: 1 allocs, 79 frees, 4 bytes allocated ==6458== peak heap usage: 4 bytes allocated ==6458== ==6458== LEAK SUMMARY: ==6458== definitely lost: 0 bytes in 0 blocks ==6458==
The output looks similar to that of double-free, however in this case it now shows an invalid memory access instead of invalid deallocation. As before, it still shows where the block was free'd before and where it was allocated in the first place.
overflow
ex4-overflow.c:
#include <stdlib.h> #include <string.h> #include <stdio.h> char* getCharBuf(int size) { return (char*)malloc(size); } void setBufferText(char* buf, char* text) { strcpy(buf, text); } int main() { char* buf = getCharBuf(16); setBufferText(buf, "0123456789ABCDEF0123456789"); return 0; }
heapusage -t overflow ./ex4
:
==6519== Heapusage - https://github.com/d99kris/heapusage ==6519== ==6519== Invalid memory access at: ==6519== at 0x00007f978f842520: __restore_rt (libc_sigaction.c:0) ==6519== at 0x00007f978f98b423: __strcpy_ssse3 (strcpy-ssse3.S:2568) ==6519== at 0x000055717f4101ae: setBufferText ==6519== at 0x000055717f4101e1: main ==6519== at 0x00007f978f829d90: __libc_start_call_main (libc_start_call_main.h:58) ==6519== at 0x00007f978f829e40: __libc_start_main (libc-start.c:128) ==6519== at 0x000055717f4100a5: _start ==6519== Address 0x55717fb4c000 is 0 bytes after a block of size 16 alloc'd at: ==6519== at 0x00007f978fb7ee5c: malloc (humain.cpp:175) ==6519== at 0x000055717f410185: getCharBuf ==6519== at 0x000055717f4101c7: main ==6519== at 0x00007f978f829d90: __libc_start_call_main (libc_start_call_main.h:58) ==6519== at 0x00007f978f829e40: __libc_start_main (libc-start.c:128) ==6519== at 0x000055717f4100a5: _start ==6519== ==6519== HEAP SUMMARY: ==6519== in use at exit: 16 bytes in 1 blocks ==6519== total heap usage: 1 allocs, 78 frees, 16 bytes allocated ==6519== peak heap usage: 16 bytes allocated ==6519== ==6519== LEAK SUMMARY: ==6519== definitely lost: 16 bytes in 1 blocks ==6519==
This output shows an invalid memory access, however, as opposed to before, this time it shows the error because is 0 bytes after a block of size 16
. Meaning the code tries to access the memory outside of the allocated memory range.