libctf: fix leak of entire dict when dict opening fails
commit37ed36fc8b6e766824c7a4aab20b866c5b7a3ff1
authorNick Alcock <nick.alcock@oracle.com>
Fri, 26 Apr 2024 17:19:15 +0000 (26 18:19 +0100)
committerNick Alcock <nick.alcock@oracle.com>
Fri, 17 May 2024 11:58:18 +0000 (17 12:58 +0100)
tree5eee194ad5037320d757ca19261eab0e1aad0ed5
parent8c59ec70063f697e2088ac58ba350fa6ea8d8680
libctf: fix leak of entire dict when dict opening fails

Ever since commit 1fa7a0c24e78e7f ("libctf: sort out potential refcount
loops") ctf_dict_close has only freed anything if the refcount on entry
to the function is precisely 1.  >1 obviously just decrements the
refcount, but the linker machinery can sometimes cause freeing to recurse
from a dict to another dict and then back to the first dict again, so
we interpret a refcount of 0 as an indication that this is a recursive call
and we should just return, because a caller is already freeing this dict.

Unfortunately there is one situation in which this is not true: the bad:
codepath in ctf_bufopen entered when opening fails.  Because the refcount is
bumped only at the very end of ctf_bufopen, any failure causes
ctf_dict_close to be entered with a refcount of zero, and it frees nothing
and we leak the entire dict.

The solution is to bump the refcount to 1 right before freeing... but this
codepath is clearly delicate enough that we need to properly validate it,
so we add a test that uses malloc interposition to count allocations and
frees, creates a dict, writes it out, intentionally corrupts it (by setting
a bunch of bytes after the header to a value high enough that it is
definitely not a valid CTF type kind), then tries to open it again and
counts the malloc/free pairs to make sure they're matched.  (Test run only
on *-linux-gnu, because malloc interposition is not a thing you can rely
upon working everywhere, and this test is not arch-dependent so if it
passes on one arch it can be assumed to pass on all of them.)

libctf/
* ctf-open.c (ctf_bufopen): Bump the refcount on failure.
* testsuite/libctf-regression/open-error-free.*: New test.
libctf/ctf-open.c
libctf/testsuite/libctf-regression/open-error-free.c [new file with mode: 0644]
libctf/testsuite/libctf-regression/open-error-free.lk [new file with mode: 0644]