[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