From f2509c37ecfc62e9ae0b210605bec59657dee5c1 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 11 Jun 2026 13:09:56 +0100 Subject: [PATCH 1/5] smex: validate ELF string section index and size The section name string table was located using a header index without bounding it against the section count, and was null terminated at size minus one without checking for a zero-size section. Reject an out-of-range index and a zero-size string section before use. Signed-off-by: Liam Girdwood --- smex/elf.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/smex/elf.c b/smex/elf.c index eb0994530c6b..2c7c1f86089f 100644 --- a/smex/elf.c +++ b/smex/elf.c @@ -44,6 +44,24 @@ static int elf_read_sections(struct elf_module *module, bool verbose) return count > 0 ? -ENODATA : -errno; } + /* the string-table section index comes from the ELF header and is used + * to index section[]; reject an out-of-range value before dereferencing + */ + if (hdr->shstrndx >= hdr->shnum) { + fprintf(stderr, "error: %s invalid shstrndx %u >= shnum %u\n", + module->elf_file, hdr->shstrndx, hdr->shnum); + return -EINVAL; + } + + /* a zero-size string section leaves module->strings unusable and would + * break later string lookups; reject it explicitly + */ + if (section[hdr->shstrndx].size == 0) { + fprintf(stderr, "error: %s has zero-size string section\n", + module->elf_file); + return -EINVAL; + } + /* read in strings */ module->strings = calloc(1, section[hdr->shstrndx].size); if (!module->strings) { @@ -392,8 +410,22 @@ int elf_find_section(const struct elf_module *module, const char *name) return -EINVAL; } + if (hdr->shstrndx >= hdr->shnum) { + fprintf(stderr, "error: invalid shstrndx %u >= shnum %u\n", + hdr->shstrndx, hdr->shnum); + return -EINVAL; + } + section = &module->section[hdr->shstrndx]; + /* a zero-size string section would make the buffer[size - 1] + * NUL-termination below write before the allocation + */ + if (section->size == 0) { + fprintf(stderr, "error: zero-size string section\n"); + return -EINVAL; + } + /* alloc data data */ buffer = calloc(1, section->size); if (!buffer) From f0c7938d922f513a62acfc8d6fd755a38d2ff920 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 11 Jun 2026 13:09:56 +0100 Subject: [PATCH 2/5] smex: clear freed section buffer pointer on error On a read error the section reader freed the output buffer but left the caller's pointer set, so a caller cleanup path could free it again. Clear the pointer after freeing. Signed-off-by: Liam Girdwood --- smex/elf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/smex/elf.c b/smex/elf.c index 2c7c1f86089f..22e04beb2c37 100644 --- a/smex/elf.c +++ b/smex/elf.c @@ -514,6 +514,10 @@ int elf_read_section(const struct elf_module *module, const char *section_name, error: free(*dst_buff); + /* clear the caller's pointer so a caller cleanup path (e.g. ldc.c's + * "if (buffer) free(buffer)") does not free the same buffer again + */ + *dst_buff = NULL; return ret; } From ed62cc6c1d1d47dd284d0a7a43224c0a19c0f9ba Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 11 Jun 2026 14:30:13 +0100 Subject: [PATCH 3/5] smex: bound section name offsets to the string table Section names are used as offsets into the string table; an out-of-range name offset from a crafted ELF read past the table. Validate every section name offset against the string-table size after loading the sections. Signed-off-by: Liam Girdwood --- smex/elf.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/smex/elf.c b/smex/elf.c index 22e04beb2c37..7a2e66f14f6f 100644 --- a/smex/elf.c +++ b/smex/elf.c @@ -85,6 +85,18 @@ static int elf_read_sections(struct elf_module *module, bool verbose) return count > 0 ? -ENODATA : -errno; } + /* every section name is used as an offset into the string table; make + * sure each stays within it so later "module->strings + name" reads + * cannot run past the table + */ + for (i = 0; i < hdr->shnum; i++) { + if (section[i].name >= section[hdr->shstrndx].size) { + fprintf(stderr, "error: %s section %d name offset %u out of range\n", + module->elf_file, i, section[i].name); + return -EINVAL; + } + } + module->bss_index = elf_find_section(module, ".bss"); if (module->bss_index < 0) { fprintf(stderr, "Can't find .bss section in %s", From c4d28763a968058a641a811843398d6ca0c71163 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 11 Jun 2026 14:32:42 +0100 Subject: [PATCH 4/5] smex: reject undersized fw-ready section The fw-ready section was cast to a struct and field-read without checking its size, reading past a short section. Reject a section smaller than the struct. Signed-off-by: Liam Girdwood --- smex/ldc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/smex/ldc.c b/smex/ldc.c index 4eccdb1c0918..737b1d05869f 100644 --- a/smex/ldc.c +++ b/smex/ldc.c @@ -26,6 +26,13 @@ static int fw_version_copy(const struct elf_module *src, if (section_size < 0) return section_size; + if ((size_t)section_size < sizeof(struct sof_ipc_fw_ready)) { + fprintf(stderr, "error: .fw_ready section too small: %d\n", + section_size); + free(buffer); + return -EINVAL; + } + memcpy(&header->version, &((struct sof_ipc_fw_ready *)buffer)->version, sizeof(header->version)); From bc44143e689a99fef6dfa9c03fec0a6158686082 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 11 Jun 2026 14:32:42 +0100 Subject: [PATCH 5/5] smex: bound the extended manifest walk The extended-manifest walk advanced by an element size read from the section without validating it, so a zero size looped forever and a large size read past the section. Stop on a zero size or one that would leave the section. Signed-off-by: Liam Girdwood --- smex/ldc.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/smex/ldc.c b/smex/ldc.c index 737b1d05869f..e9f8b75d5983 100644 --- a/smex/ldc.c +++ b/smex/ldc.c @@ -57,13 +57,22 @@ static int fw_version_copy(const struct elf_module *src, return section_size; ext_hdr = (struct ext_man_elem_header *)buffer; - while ((uintptr_t)ext_hdr < (uintptr_t)buffer + section_size) { + while ((uintptr_t)ext_hdr + sizeof(*ext_hdr) <= + (uintptr_t)buffer + section_size) { if (ext_hdr->type == EXT_MAN_ELEM_DBG_ABI) { header->version.abi_version = ((struct ext_man_dbg_abi *) ext_hdr)->dbg_abi.abi_dbg_version; break; } + /* elem_size must advance the cursor and keep the next header + * within the section; otherwise stop instead of looping + * forever (elem_size == 0) or reading past the section + */ + if (ext_hdr->elem_size == 0 || + (uintptr_t)ext_hdr + ext_hdr->elem_size > + (uintptr_t)buffer + section_size) + break; //move to the next entry ext_hdr = (struct ext_man_elem_header *) ((uint8_t *)ext_hdr + ext_hdr->elem_size);