Skip to content

smex: harden ELF parsing against malformed input#10922

Open
lgirdwood wants to merge 5 commits into
thesofproject:mainfrom
lgirdwood:fix-smex
Open

smex: harden ELF parsing against malformed input#10922
lgirdwood wants to merge 5 commits into
thesofproject:mainfrom
lgirdwood:fix-smex

Conversation

@lgirdwood

Copy link
Copy Markdown
Member

Harden smex against malformed/untrusted ELF input (e.g. a build pipeline
running smex on a contributed object):

  • validate the section-header string table index and reject a zero-size
    string section before indexing
  • bound section name offsets against the string-table size
  • clear the freed section-buffer pointer on a read error to avoid a
    double-free in the caller
  • reject an undersized .fw_ready section before casting it to a struct
  • bound the extended-manifest element walk (reject elem_size == 0 or one that
    runs past the section)

Copilot AI review requested due to automatic review settings June 15, 2026 14:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Harden smex ELF parsing when handling malformed/untrusted objects by adding bounds checks and safer error handling around string tables, section lookups, and manifest parsing.

Changes:

  • Validate section-header string table index/size and bound section-name offsets against the string table size.
  • Prevent double-free by nulling the caller buffer on elf_read_section() error.
  • Add size/bounds checks for .fw_ready and extended manifest element walking.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
smex/ldc.c Adds defensive size/bounds checks when parsing .fw_ready and extended manifest elements.
smex/elf.c Validates shstrndx/string-table invariants, bounds section name offsets, and hardens error cleanup.

Comment thread smex/ldc.c
Comment on lines +60 to 67
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;
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — before reading the dbg-abi element it now checks elem_size >= sizeof(struct ext_man_dbg_abi) and that the whole struct lies within the section; otherwise it returns -ENOEXEC.

Comment thread smex/ldc.c Outdated
Comment on lines +68 to +75
/* 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;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed — a malformed element (elem_size == 0 or one that runs past the section) now returns -ENOEXEC (frees the buffer first) instead of breaking out as if parsing succeeded. The "no DBG_ABI extension present" case still completes normally and keeps the kernel ABI version.

Comment thread smex/elf.c Outdated
Comment on lines 66 to 67
module->strings = calloc(1, section[hdr->shstrndx].size);
if (!module->strings) {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — the string table is now allocated with one extra byte (calloc(1, size + 1), which zeroes it), so it's always NUL-terminated and the name lookups/compares can't run off the end even if the section itself lacks a terminator.

Comment thread smex/elf.c
Comment on lines +425 to +429
if (hdr->shstrndx >= hdr->shnum) {
fprintf(stderr, "error: invalid shstrndx %u >= shnum %u\n",
hdr->shstrndx, hdr->shnum);
return -EINVAL;
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SHN_XINDEX (extended section numbering) only applies when a file has >= 0xff00 sections, which doesn't occur for SOF firmware images (they have a handful of sections). Supporting it would mean reading the real index from section[0].sh_link, which is out of scope here — rejecting is the safe behaviour for these inputs. I can add an explicit SHN_XINDEX detection + clearer message if you'd prefer.

Comment thread smex/elf.c Outdated
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;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Zephyr ELF format bugs are usually marked with ENOEXEC, might be good to do the same here

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — switched the ELF-format rejections (bad shstrndx, zero-size string section, out-of-range section name offset) to -ENOEXEC to match the Zephyr convention.

lrgirdwo added 5 commits June 16, 2026 10:50
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 <liam.r.girdwood@linux.intel.com>
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 <liam.r.girdwood@linux.intel.com>
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 <liam.r.girdwood@linux.intel.com>
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 <liam.r.girdwood@linux.intel.com>
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 <liam.r.girdwood@linux.intel.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants