Quote from a private correspondence with a Qualys vulnerability researcher (used with permission):> Interesting (great malloc by the way, clean design and implementation > and lots of security checks)!
Additionally, there are a few features that can be switched on runtime that implement even more strict checks, at the cost of performance and/or increased memory usage. You can read more about that in the man page of malloc(3) and my EuroBSDCon 2023 presentation slides or video.
Around 2011 worked on improving the speed of the allocation of small chunks. Working on that, I really got into the mood to return to the old goal of adding a feature that is very useful to developers: memory leak detection. Once I realized I could use a compiler feature to determine the address of the code malloc has been called from, I got on a roll. While the original code worked, it had some drawbacks and for various reasons the leak detection code was never compiled by default until I rewrote it in 2023. OpenBSD 7.4, released in 2023 has leak detection available, switched off by default. In this page I'd like to show how to use this feature.
$ MALLOC_OPTIONS=D ktrace -tu ./a.outThis will produce a file called ktrace.out. The output then can be viewed using kdump To shows the malloc utrace records in in a nice way, use the argument -u malloc:
$ kdump -u mallocI made a little leaky program to illustrate some points.
$ cat -n x.c 1 #include <stdlib.h> 2 #include <stdio.h> 3 4 int 5 main() 6 { 7 void *p; 8 int i; 9 10 for (i = 0; i < 3; i++) 11 printf("%p\n", p = malloc(10240)); 12 free(p); 13 for (i = 0; i < 5; i++) 14 printf("%p\n", malloc(1000)); 15 } $ cc -g x.c $ MALLOC_OPTIONS=D ktrace -tu ./a.out 0x1098469000 0x114c10b000 0x11447c8000 0x1112865800 0x1112865000 0x111283c800 0x1112865400 0x111283c400 $ kdump -u malloc ******** Start dump a.out ******* M=8 I=1 F=0 U=0 J=1 R=0 X=0 C=0 cache=64 G=0 Leak report: f sum # avg 0x6e3248fa5b 20480 2 10240 addr2line -e ./a.out 0x1a5b 0x709a34626c 65536 1 65536 addr2line -e /usr/lib/libc.so.100.3 0x5326c 0x6e3248faa2 5120 5 1024 addr2line -e ./a.out 0x1aa2 ******** End dump a.out *******The sum column shows the total number of bytes leaked, the # column shows the number of calls to an allocation that leaked and the avg column shows the average size of the allocations. We can conclude that this program has 5 leaks of size 1024, two leaks of size 10240 and one leak of size 65536. For the smaller allocations the size is rounded up to 1024, while malloc was called wit a size argument of 1000.
$ addr2line -e ./a.out 0x1a5b /home/otto/x.c:11 $ addr2line -e ./a.out 0x1aa2 /home/otto/x.c:14 $This shows that both instances of malloc calls introduced leaks. Fixing the leaks is left as an exercise to the reader.
The third case is a bit different: the leak originates from libc and on my arm64 I get:
$ addr2line -e /usr/lib/libc.so.97.1 0x5326c BFD: Dwarf Error: found dwarf version '4', this reader only handles version 2 information. ... $This is nasty, the addr2line command in base cannot read the debug info. Luckily there's a workaround (which is needed on e.g. arm64 machines): install the package binutils and use gaddr2line:
$ doas pkg_add binutils $ gaddr2line -e /usr/lib/libc.so.97.1 0x5326c /usr/src/lib/libc/stdio/makebuf.c:62 $If you look at makebuf.c and the rest of the standard io library you can learn that on this line a buffer used by the stdout stream is allocated. This is strictly speaking a leak, but it is not harmful. When leak hunting we are more interested in leaks that grow with the process doing work.
awk '/addr2line/{ printf($0); cmd=("addr2line -f " $6 " " $7 " " $8 " | tr \\\\n \" \""); printf(" "); system(cmd); print("") } !/addr2line/ { print } 'This script uses system(), use at your own risk.
$ MALLOC_OPTIONS=4 ktrace -tu ./a.out 0x79464ac8000 0x7944d6ce000 0x7940e0f4000 0x793ec27d400 0x793ec27d800 0x793ec26a000 0x793ec27d000 0x793ec27dc00 $ kdump -u malloc | malloc_pp ******** Start dump a.out ******* M=8 I=1 F=0 U=0 J=1 R=0 X=0 C=0 cache=64 G=0 Leak report: f sum # avg 0x791bb9dea5b 20480 2 10240 addr2line -e ./a.out 0x1a5b main /home/otto/x.c:11 0x791bb9de84b - - - addr2line -e ./a.out 0x184b _start x.c:0 0x794666af26c 65536 1 65536 addr2line -e /usr/lib/libc.so.100.3 0x5326c __smakebuf /usr/src/lib/libc/stdio/makebuf.c:0 0x794666a6d7f - - - addr2line -e /usr/lib/libc.so.100.3 0x4ad7f __swsetup /usr/src/lib/libc/stdio/wsetup.c:82 0x79466708804 - - - addr2line -e /usr/lib/libc.so.100.3 0xac804 __vfprintf /usr/src/lib/libc/stdio/vfprintf.c:477 0x79466708763 - - - addr2line -e /usr/lib/libc.so.100.3 0xac763 _libc_vfprintf /usr/src/lib/libc/stdio/vfprintf.c:279 0x791bb9deaa2 5120 5 1024 addr2line -e ./a.out 0x1aa2 main /home/otto/x.c:14 0x791bb9de84b - - - addr2line -e ./a.out 0x184b _start x.c:0