[PATCH 03/31] btf_encoder: Fix interior pointer free and missing NULL check

Arnaldo Carvalho de Melo <[email protected]> Wed, 29 Jul 2026 16:07:03 -0300
Newsgroups org.kernel.vger.dwarves,org.kernel.vger.bpf
Message-ID <[email protected]>
From: Arnaldo Carvalho de Melo <[email protected]>

Two bugs in btf_encoder:

1. btf_encoder__save_func() called free(state) on error, but state is
   an interior pointer into func_states.array (returned by
   btf_encoder__alloc_func_state as &array[cnt++]), not a standalone
   malloc'd block.  Calling free() on it is undefined behavior that
   corrupts the heap allocator's metadata.

   Fix: clear the slot with memset and decrement func_states.cnt to
   release it back.  This is safe because no further func_state
   allocations happen between the alloc at the top of save_func and
   the error path, so state is always the last element.

2. btf_encoder__new() allocated func_states.array with zalloc() but
   did not check for NULL.  A failed allocation would cause a NULL
   pointer dereference in btf_encoder__alloc_func_state().

   Fix: add NULL check, goto out_delete on failure.

Fixes: 4bff1141bb48ae58 ("btf_encoder: Record BTF-centric function state instead of DWARF-centric")
Reported-by: Sashiko:gemini-3-1-pro-preview # Running on a local machine
Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
 btf_encoder.c | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/btf_encoder.c b/btf_encoder.c
index 9f8cd279fa92af1c..5d0901da73b618d7 100644
--- a/btf_encoder.c
+++ b/btf_encoder.c
@@ -1393,9 +1393,21 @@ static int32_t btf_encoder__save_func(struct btf_encoder *encoder, struct functi
 	}
 	return 0;
 out:
+	/*
+	 * state is an interior pointer into func_states.array (returned by
+	 * btf_encoder__alloc_func_state), not a standalone allocation.
+	 * Calling free(state) here was heap corruption.
+	 *
+	 * Since no further func_state allocations happen between the alloc
+	 * at the top of this function and this error path, state is always
+	 * the last element (index cnt-1), so decrementing cnt releases it.
+	 * If this invariant ever changes (e.g. nested alloc calls are added),
+	 * this cleanup must be revised.
+	 */
 	zfree(&state->annots);
 	zfree(&state->parms);
-	free(state);
+	memset(state, 0, sizeof(*state));
+	encoder->func_states.cnt--;
 	return err;
 }
 
@@ -2814,6 +2826,8 @@ struct btf_encoder *btf_encoder__new(struct cu *cu, const char *detached_filenam
 
 		/* Start with funcs->cnt. The array may grow in btf_encoder__alloc_func_state() */
 		encoder->func_states.array = zalloc(sizeof(*encoder->func_states.array) * funcs->cnt);
+		if (encoder->func_states.array == NULL && funcs->cnt > 0)
+			goto out_delete;
 		encoder->func_states.cap = funcs->cnt;
 		encoder->func_states.cnt = 0;
 
-- 
2.55.0