From c361ea1c3fc4b1f308285b087dda6592d02e291c Mon Sep 17 00:00:00 2001 From: Dmitry Ilyin <6576495+widgetii@users.noreply.github.com> Date: Tue, 2 Jun 2026 18:52:16 +0300 Subject: [PATCH] mtd: fix use-after-free in find_ubi_for_mtd (segfault on musl arm64) POSIX leaves struct dirent invalid after closedir(); glibc happens to keep the buffer alive across the close, musl invalidates it immediately. find_ubi_for_mtd() did: closedir(d); int ubi_num; sscanf(de->d_name, "ubi%d", &ubi_num); /* <-- read freed memory */ return ubi_num; That's a segfault on the Bootlin-musl-built aarch64 release binary (reproducible in `ipctool` -> "rom" step -> cb_mtd_info -> mtd4 ubifs volume), while the same source builds clean and runs through to full YAML on glibc. Restructure to parse de->d_name BEFORE closedir(), with a single local `ubi_num` initialised to -1 that's returned at the end. Also flatten the `if (f)` block to early-`continue` on fopen() failure to keep the control flow obvious. Same fix would be the right thing for the equivalent dirent-after- closedir pattern elsewhere in the tree if it surfaces. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/mtd.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/mtd.c b/src/mtd.c index b7cbadd..64398a5 100644 --- a/src/mtd.c +++ b/src/mtd.c @@ -125,6 +125,7 @@ int find_ubi_for_mtd(int mtd_num) { DIR *d = opendir("/sys/class/ubi"); if (!d) return -1; + int ubi_num = -1; struct dirent *de; while ((de = readdir(d))) { if (strncmp(de->d_name, "ubi", 3) != 0) @@ -134,20 +135,23 @@ int find_ubi_for_mtd(int mtd_num) { char path[128]; snprintf(path, sizeof(path), "/sys/class/ubi/%s/mtd_num", de->d_name); FILE *f = fopen(path, "r"); - if (f) { - int num; - if (fscanf(f, "%d", &num) == 1 && num == mtd_num) { - fclose(f); - closedir(d); - int ubi_num; - sscanf(de->d_name, "ubi%d", &ubi_num); - return ubi_num; - } - fclose(f); + if (!f) + continue; + int num; + bool match = fscanf(f, "%d", &num) == 1 && num == mtd_num; + fclose(f); + if (match) { + /* Parse the number BEFORE closedir(): POSIX leaves the + * dirent invalid after closedir, and musl actively + * invalidates it (where glibc happens to keep it alive), + * so reading de->d_name after closedir is a segfault on + * the aarch64 musl build. */ + sscanf(de->d_name, "ubi%d", &ubi_num); + break; } } closedir(d); - return -1; + return ubi_num; } int enum_ubi_volumes(int ubi_num, ubi_vol_info_t *vols, int max_vols) {