[PATCH 08/31] dwarf_loader: Allocate type_dcu via dwarf_cu__new to fix dangling stack pointer

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

In cus__load_module(), type_dcu was declared on the stack and passed
to __cus__load_debug_types(), which stores the pointer in type_cu->priv.
After cus__load_module() returns, type_cu->priv points to a dead stack
frame.  When dwarf_cu__delete() later calls cu__free(cu, cu->priv), it
passes an invalid stack address to free(), causing heap corruption or a
crash.

Move the allocation into __cus__load_debug_types() using dwarf_cu__new(),
which allocates through cu__zalloc() — respecting the CU's obstack
setting so that the type_dcu lifetime is tied to the type_cu that owns
it and cleanup goes through the normal dwarf_cu__delete() path.

Fixes: 2938a70e1e2a73ff ("Add support for .debug_types sections.")
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]>
---
 dwarf_loader.c | 41 +++++++++++++++++++++++++----------------
 1 file changed, 25 insertions(+), 16 deletions(-)

diff --git a/dwarf_loader.c b/dwarf_loader.c
index 14c90080f538e10d..b87519fa73405ff7 100644
--- a/dwarf_loader.c
+++ b/dwarf_loader.c
@@ -3889,7 +3889,7 @@ static int cu__set_common(struct cu *cu, struct conf_load *conf,
 
 static int __cus__load_debug_types(struct cus *cus, struct conf_load *conf, Dwfl_Module *mod, Dwarf *dw, Elf *elf,
 				   const char *filename, const unsigned char *build_id,
-				   int build_id_len, struct cu **cup, struct dwarf_cu *dcup)
+				   int build_id_len, struct cu **cup, struct dwarf_cu **dcup)
 {
 	Dwarf_Off off = 0, noff, type_off;
 	size_t cuhl;
@@ -3897,6 +3897,7 @@ static int __cus__load_debug_types(struct cus *cus, struct conf_load *conf, Dwfl
 	uint64_t signature;
 
 	*cup = NULL;
+	*dcup = NULL;
 
 	while (dwarf_next_unit(dw, off, &noff, &cuhl, NULL, NULL, &pointer_size,
 			       &offset_size, &signature, &type_off)
@@ -3904,6 +3905,7 @@ static int __cus__load_debug_types(struct cus *cus, struct conf_load *conf, Dwfl
 
 		if (*cup == NULL) {
 			struct cu *cu;
+			struct dwarf_cu *dcu;
 
 			cu = cu__new("", pointer_size, build_id,
 				     build_id_len, filename, conf->use_obstack);
@@ -3913,17 +3915,18 @@ static int __cus__load_debug_types(struct cus *cus, struct conf_load *conf, Dwfl
 				return DWARF_CB_ABORT;
 			}
 
-			if (dwarf_cu__init(dcup, cu) != 0) {
+			dcu = dwarf_cu__new(cu);
+			if (dcu == NULL) {
 				cu__delete(cu);
 				return DWARF_CB_ABORT;
 			}
-			dcup->cu = cu;
 			/* Funny hack.  */
-			dcup->type_unit = dcup;
-			cu->priv = dcup;
+			dcu->type_unit = dcu;
+			cu->priv = dcu;
 			cu->dfops = &dwarf__ops;
 
 			*cup = cu;
+			*dcup = dcu;
 			cus__add(cus, cu);
 		}
 
@@ -4488,27 +4491,30 @@ static int cus__load_module(struct cus *cus, struct conf_load *conf,
 	int build_id_len = 0;
 #endif
 	struct cu *type_cu;
-	struct dwarf_cu type_dcu;
+	struct dwarf_cu *type_dcu;
 	int type_lsk = LSK__KEEPIT;
 	int lsk_worker_status = LSK__ABORT;
 
 	int res = __cus__load_debug_types(cus, conf, mod, dw, elf, filename, build_id, build_id_len, &type_cu, &type_dcu);
-	if (res != 0) {
+	if (res != 0)
 		return res;
-	}
 
 	if (type_cu != NULL) {
 		cu__finalize(type_cu, cus, conf);
-		type_lsk = cus__steal_now(cus, type_cu, conf);
-		if (type_lsk == LSK__DELETE) {
+		if (conf && conf->steal)
+			type_lsk = conf->steal(type_cu, conf);
+		/* Defer cu__delete() for LSK__DELETE until after main
+		 * CUs are processed — they need type_dcu alive for
+		 * DW_FORM_ref_sig8 resolution.  Remove from the list
+		 * now so the consumer doesn't see it twice. */
+		if (type_lsk == LSK__DELETE)
 			cus__remove(cus, type_cu);
-		}
 	}
 
 	if (cus__merging_cu(dw, elf)) {
 		res = cus__merge_and_process_cu(cus, conf, mod, dw, elf, filename,
 						build_id, build_id_len,
-						type_cu ? &type_dcu : NULL);
+						type_cu ? type_dcu : NULL);
 	} else {
 		struct dwarf_cus dcus = {
 			.off      = 0,
@@ -4518,7 +4524,7 @@ static int cus__load_module(struct cus *cus, struct conf_load *conf,
 			.dw       = dw,
 			.elf      = elf,
 			.filename = filename,
-			.type_dcu = type_cu ? &type_dcu : NULL,
+			.type_dcu = type_cu ? type_dcu : NULL,
 			.build_id = build_id,
 			.build_id_len = build_id_len,
 			.nr_cus_created = 0,
@@ -4527,12 +4533,15 @@ static int cus__load_module(struct cus *cus, struct conf_load *conf,
 		lsk_worker_status = dcus.lsk_status;
 	}
 
+	/* Deferred type CU cleanup: now that main CUs have finished
+	 * resolving DW_FORM_ref_sig8 refs through type_dcu, it is
+	 * safe to free the type CU. */
+	if (type_cu != NULL && type_lsk == LSK__DELETE)
+		cu__delete(type_cu);
+
 	if (res && lsk_worker_status == LSK__ABORT)
 		return res;
 
-	if (type_lsk == LSK__DELETE)
-		cu__delete(type_cu);
-
 	return DWARF_CB_OK;
 }
 
-- 
2.55.0