bootflow_scan_first()/bootflow_scan_next() try each candidate
(bootdev, method, partition) in turn. When a candidate fails and is
not returned to the caller (no BOOTFLOWIF_ALL), the bootflow is simply
abandoned: the next candidate's bootflow_init() memsets the struct,
orphaning everything the failed attempt allocated, starting with the
name allocated in bootdev_find_in_blk().
Each failed candidate therefore leaks its allocations on every scan.
A single failing 'bootflow scan' on a sandbox MMC with a RAUC A/B
layout leaks about 1 KB across ~30 abandoned candidates, and scans can
be retried indefinitely from the U-Boot prompt.
Free the bootflow when it failed and is not passed back to the caller.
Add a check to the bootflow_rauc test that repeating a failing scan
does not change the number of allocated bytes. Together with the
previous RAUC fixes this makes the failed-scan path leak-free.
Fixes: a8f5be178db5 ("bootstd: Add support for bootflows")
Signed-off-by: Aristo Chen <[email protected]>
---
boot/bootflow.c | 2 ++
test/boot/bootflow.c | 8 ++++++++
2 files changed, 10 insertions(+)
diff --git a/boot/bootflow.c b/boot/bootflow.c
index d8a4a81a838..7ee940460af 100644
--- a/boot/bootflow.c
+++ b/boot/bootflow.c
@@ -580,6 +580,7 @@ int bootflow_scan_first(struct udevice *dev, const char
*label,
if (iter->flags & BOOTFLOWIF_ALL)
return log_msg_ret("all", ret);
}
+ bootflow_free(bflow);
iter->err = ret;
ret = bootflow_scan_next(iter, bflow);
if (ret)
@@ -614,6 +615,7 @@ int bootflow_scan_next(struct bootflow_iter *iter, struct
bootflow *bflow)
if (iter->flags & BOOTFLOWIF_ALL)
return log_msg_ret("all", ret);
}
+ bootflow_free(bflow);
} else {
log_debug("incr failed, err=%d\n", ret);
iter->err = ret;
diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c
index 8fa8835c20c..ef4638ec700 100644
--- a/test/boot/bootflow.c
+++ b/test/boot/bootflow.c
@@ -1403,6 +1403,7 @@ static int bootflow_rauc(struct unit_test_state *uts)
struct udevice *bootstd;
static const char *order[] = {NULL, NULL};
const char **old_order;
+ ulong mem_start;
ofnode root;
ofnode node;
@@ -1458,6 +1459,13 @@ static int bootflow_rauc(struct unit_test_state *uts)
ut_assert_skip_to_line("(0 bootflows, 0 valid)");
ut_assert_console_end();
+ /* Repeating the failed scan must not leak memory */
+ mem_start = ut_check_delta(0);
+ ut_assertok(run_command("bootflow scan", 0));
+ ut_assert_nextline("No bootflows found; try again with -l");
+ ut_assert_console_end();
+ ut_asserteq(0, ut_check_delta(mem_start));
+
/*
* A failed scan with -a stores the failed bootflows; the next scan
* removes them, freeing bootmeth_priv. This used to double free the
--
2.43.0