[PATCH 05/31] pahole: Fix instance memory leak on early returns in prototype__stdio_fprintf_value

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

prototype__stdio_fprintf_value() allocates an instance buffer via
malloc() but has 14 early return statements between the allocation
and the free(instance) at the out: label.  None of these early returns
free instance, leaking memory on every error path during --prettify
binary record processing.

Change all bare returns after the successful malloc to 'goto out',
setting printed to the error value before jumping.  The out: label
already does free(instance) and return printed.

The return -ENOMEM when instance == NULL (malloc failure) is kept
as-is since there is nothing to free.

Fixes: fdfc64ec44f4ad53 ("pahole: Introduce --range")
Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: Arnaldo Carvalho de Melo <[email protected]>
---
 pahole.c | 45 ++++++++++++++++++++++++++++-----------------
 1 file changed, 28 insertions(+), 17 deletions(-)

diff --git a/pahole.c b/pahole.c
index 390d5f2dd20e4dfd..0096dfa34e5047c8 100644
--- a/pahole.c
+++ b/pahole.c
@@ -2584,9 +2584,9 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty
 		return -ENOMEM;
 
 	if (type__instance_read_once(header, input) < 0) {
-		int err = --errno;
+		printed = --errno;
 		fprintf(stderr, "pahole: --header (%s) type couldn't be read\n", conf.header_type);
-		return err;
+		goto out;
 	}
 
 	if (conf.range || prototype->range) {
@@ -2598,14 +2598,16 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty
 				fprintf(stderr, "pahole: --header_type=%s not found\n", conf.header_type);
 			else
 				fprintf(stderr, "pahole: range (%s) requires --header\n", range);
-			return -ESRCH;
+			printed = -ESRCH;
+			goto out;
 		}
 
 		char *member_name = NULL;
 
 		if (asprintf(&member_name, "%s.%s", range, "offset") == -1) {
 			fprintf(stderr, "pahole: not enough memory for range=%s\n", range);
-			return -ENOMEM;
+			printed = -ENOMEM;
+			goto out;
 		}
 
 		int64_t value = type_instance__int_value(header, member_name);
@@ -2614,7 +2616,8 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty
 			fprintf(stderr, "pahole: couldn't read the '%s' member of '%s' for evaluating range=%s\n",
 				member_name, conf.header_type, range);
 			free(member_name);
-			return -ESRCH;
+			printed = -ESRCH;
+			goto out;
 		}
 
 		seek_bytes = value;
@@ -2628,7 +2631,8 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty
 		if (seek_bytes < total_read_bytes) {
 			fprintf(stderr, "pahole: can't go back in input, already read %" PRIu64 " bytes, can't go to position %#" PRIx64 "\n",
 					total_read_bytes, seek_bytes);
-			return -ENOMEM;
+			printed = -ENOMEM;
+			goto out;
 		}
 
 		if (global_verbose) {
@@ -2640,7 +2644,8 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty
 
 		if (asprintf(&member_name, "%s.%s", range, "size") == -1) {
 			fprintf(stderr, "pahole: not enough memory for range=%s\n", range);
-			return -ENOMEM;
+			printed = -ENOMEM;
+			goto out;
 		}
 
 		value = type_instance__int_value(header, member_name);
@@ -2649,7 +2654,8 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty
 			fprintf(stderr, "pahole: couldn't read the '%s' member of '%s' for evaluating range=%s\n",
 				member_name, conf.header_type, range);
 			free(member_name);
-			return -ESRCH;
+			printed = -ESRCH;
+			goto out;
 		}
 
 		size_bytes = value;
@@ -2661,9 +2667,9 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty
 		free(member_name);
 
 		if (pipe_seek(input, seek_bytes) < 0) {
-			int err = --errno;
+			printed = --errno;
 			fprintf(stderr, "Couldn't --seek_bytes %s (%" PRIu64 "\n", conf.seek_bytes, seek_bytes);
-			return err;
+			goto out;
 		}
 
 		goto do_read;
@@ -2676,7 +2682,8 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty
 			if (!header) {
 				fprintf(stderr, "pahole: --seek_bytes (%s) makes reference to --header but it wasn't specified\n",
 					conf.seek_bytes);
-				return -ESRCH;
+				printed = -ESRCH;
+				goto out;
 			}
 
 			const char *member_name = conf.seek_bytes + sizeof("$header.") - 1;
@@ -2684,7 +2691,8 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty
 			if (value < 0) {
 				fprintf(stderr, "pahole: couldn't read the '%s' member of '%s' for evaluating --seek_bytes=%s\n",
 					member_name, conf.header_type, conf.seek_bytes);
-				return -ESRCH;
+				printed = -ESRCH;
+				goto out;
 			}
 
 			seek_bytes = value;
@@ -2696,7 +2704,8 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty
 			if (seek_bytes < header->type->size) {
 				fprintf(stderr, "pahole: seek bytes evaluated from --seek_bytes=%s is less than the header type size\n",
 					conf.seek_bytes);
-				return -EINVAL;
+				printed = -EINVAL;
+				goto out;
 			}
 		} else  {
 			seek_bytes = strtol(conf.seek_bytes, NULL, 0);
@@ -2709,9 +2718,9 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty
 		}
 
 		if (pipe_seek(input, seek_bytes) < 0) {
-			int err = --errno;
+			printed = --errno;
 			fprintf(stderr, "Couldn't --seek_bytes %s (%" PRIu64 "\n", conf.seek_bytes, seek_bytes);
-			return err;
+			goto out;
 		}
 	}
 
@@ -2720,7 +2729,8 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty
 			if (!header) {
 				fprintf(stderr, "pahole: --size_bytes (%s) makes reference to --header but it wasn't specified\n",
 					conf.size_bytes);
-				return -ESRCH;
+				printed = -ESRCH;
+				goto out;
 			}
 
 			const char *member_name = conf.size_bytes + sizeof("$header.") - 1;
@@ -2728,7 +2738,8 @@ static int prototype__stdio_fprintf_value(struct prototype *prototype, struct ty
 			if (value < 0) {
 				fprintf(stderr, "pahole: couldn't read the '%s' member of '%s' for evaluating --size_bytes=%s\n",
 					member_name, conf.header_type, conf.size_bytes);
-				return -ESRCH;
+				printed = -ESRCH;
+				goto out;
 			}
 
 			size_bytes = value;
-- 
2.55.0