Repository navigation
fix(rocr): Bounds-check ELF loader section and symbol indices - #9031
Conversation
✅ All Policy Checks Passed
📖 Need help? See the Policy FAQ for details on every check and how to fix failures. |
|
🚫 Please fix the failed policies before requesting reviews. The following policy checks failed:
The |
e1dfbfb to
5afef59
Compare
cfreeamd
left a comment
There was a problem hiding this comment.
🤖 AI-assisted pre-review (for @cfreeamd)
Reviewed the ELF-loader bounds-checking hardening. executable.cpp null guards (LoadDefinitionSymbol, SymbolAddress, ApplyStaticRelocation, ApplyDynamicRelocation) and the section()/symbol()/getStringTable() bounds checks in amd_elf_image.cpp look correct and fail closed. A few non-blocking notes inline.
cfreeamd
left a comment
There was a problem hiding this comment.
🤖 AI-assisted pre-review (for @cfreeamd) — follow-up after author update
All three prior findings are resolved in commit 3df30b6:
- [Question]
section(0)— confirmed viapullElf()that anSHT_NULLsection is stored as an emptyunique_ptr, sosection(0)genuinely returnsnullptr; the routing rationale holds. Concern dropped. - [Optional]
getStringTable()type confusion — fixed: now rejects an in-range wrong-type section (!s || s->type() != SHT_STRTAB). - [Optional] audit remaining
section()callers —getReferencedSymbolTable()had the same rawstatic_castand is now hardened to requireSHT_SYMTAB/SHT_DYNSYM; other callers were already null-guarded.
The follow-up commit adds real type-confusion hardening beyond the original scope. One non-blocking, out-of-scope note for a possible separate follow-up: the amd_hsa_code.cpp symbol accessors (Symbol::IsAgent/IsSegment/GetAlignment/IsConst) still dereference elfsym->section() without a null check — a crafted out-of-range st_shndx would now yield a deterministic null-deref (DoS only, not the prior exploitable OOB + garbage virtual dispatch), so this PR is a strict improvement there. Looks good to me.
944563a to
0322055
Compare
0322055 to
6c82450
Compare
6c82450 to
5690c8a
Compare
Harden the ROCr ELF code-object loader against out-of-bounds heap reads driven by attacker-controlled section/symbol indices in untrusted code objects. GElfImage::section() indexed the sections vector with unvalidated sh_link, sh_info and st_shndx fields, and GElfSymbolTable::symbol() indexed the symbols vector with the unvalidated relocation r_info symbol index. - section(): return nullptr for out-of-range indices instead of reading past the vector. - getStringTable(): reject out-of-range sh_link. - getSymtab()/getDynsym(): null-check the section() result before ->type(). - GElfSymbol::section(): reject reserved indices (>= SHN_LORESERVE) so a crafted st_shndx cannot bypass the SHN_UNDEF check. - GElfSymbolTable::symbol(): bounds-check the r_info symbol index. - GElfSymbolTable/GElfRelocationSection::pullData(): fail (rejecting the code object) when the referenced string/symbol/target sections are invalid. - executable.cpp: null-check GetSection()/section()/symbol() results in LoadDefinitionSymbol, SymbolAddress, ApplyStaticRelocation and ApplyDynamicRelocation, returning HSA_STATUS_ERROR_INVALID_CODE_OBJECT. JIRA ID : SWSPLAT-45972 Co-authored-by: Cursor <cursoragent@cursor.com>
Address review feedback on the ELF loader hardening: getStringTable() and getReferencedSymbolTable() bounds-checked the section index but still static_cast an in-range section of the wrong type, leaving a residual type-confusion. Verify the referenced section is actually SHT_STRTAB (string table) or SHT_SYMTAB/SHT_DYNSYM (symbol table) respectively, and return nullptr otherwise; callers already handle nullptr. JIRA ID : SWSPLAT-45972 Co-authored-by: Cursor <cursoragent@cursor.com>
5690c8a to
5176805
Compare
|
Will merge given code owner approval.
|
Harden the ROCr ELF code-object loader against out-of-bounds heap reads driven by attacker-controlled section/symbol indices in untrusted code objects. GElfImage::section() indexed the sections vector with unvalidated sh_link, sh_info and st_shndx fields, and GElfSymbolTable::symbol() indexed the symbols vector with the unvalidated relocation r_info symbol index. - section(): return nullptr for out-of-range indices instead of reading past the vector. - getStringTable(): reject out-of-range sh_link. - getSymtab()/getDynsym(): null-check the section() result before ->type(). - GElfSymbol::section(): reject reserved indices (>= SHN_LORESERVE) so a crafted st_shndx cannot bypass the SHN_UNDEF check. - GElfSymbolTable::symbol(): bounds-check the r_info symbol index. - GElfSymbolTable/GElfRelocationSection::pullData(): fail (rejecting the code object) when the referenced string/symbol/target sections are invalid. - executable.cpp: null-check GetSection()/section()/symbol() results in LoadDefinitionSymbol, SymbolAddress, ApplyStaticRelocation and ApplyDynamicRelocation, returning HSA_STATUS_ERROR_INVALID_CODE_OBJECT. JIRA ID : SWSPLAT-45972 Co-authored-by: Cursor <cursoragent@cursor.com> [rocm-systems] ROCm/rocm-systems#9031 (commit 388b5ee)
table lookups Address review feedback on the ELF loader hardening: getStringTable() and getReferencedSymbolTable() bounds-checked the section index but still static_cast an in-range section of the wrong type, leaving a residual type-confusion. Verify the referenced section is actually SHT_STRTAB (string table) or SHT_SYMTAB/SHT_DYNSYM (symbol table) respectively, and return nullptr otherwise; callers already handle nullptr. JIRA ID : SWSPLAT-45972 Co-authored-by: Cursor <cursoragent@cursor.com> [rocm-systems] ROCm/rocm-systems#9031 (commit 388b5ee)
TheRock Submodule Bump ActivityNewest first
|
Summary
Hardens the ROCr ELF code-object loader against out-of-bounds heap reads driven by attacker-controlled section/symbol indices when parsing untrusted code objects (reported in SWSPLAT-45972).
GElfImage::section()indexed thesectionsvector with unvalidatedsh_link,sh_info, andst_shndxfields, andGElfSymbolTable::symbol()indexed thesymbolsvector with the unvalidated relocationr_infosymbol index. A crafted.hsacocould supply an out-of-range index, causingstd::vector::operator[]to read past the vector's backing array and later dereference a garbage pointer (deterministic SIGSEGV / DoS, with conditional info-disclosure).This complements the already-merged loader hardening in #6994 (segment loading, note parsing, relocation offsets), which did not cover these index-based vectors.
Changes in
libamdhsacode/amd_elf_image.cpp:section(): returnnullptrfor out-of-range indices instead of reading past the vector.getStringTable(): reject out-of-rangesh_link.getSymtab()/getDynsym(): null-check thesection()result before->type().GElfSymbol::section(): reject reserved indices (>= SHN_LORESERVE) so a craftedst_shndxcannot bypass theSHN_UNDEFcheck.GElfSymbolTable::symbol(): bounds-check ther_infosymbol index.GElfSymbolTable::pullData()/GElfRelocationSection::pullData(): reject the code object when the referenced string/symbol/target sections are out of range. Notesh_info == 0(the null section) is preserved as legitimate for dynamic relocation sections, so the index is range-checked rather than the returned pointer.Changes in
loader/executable.cpp:GetSection()/section()/symbol()results inLoadDefinitionSymbol,SymbolAddress,ApplyStaticRelocation, andApplyDynamicRelocation, returningHSA_STATUS_ERROR_INVALID_CODE_OBJECTfor malformed input.JIRA ID
JIRA ID : SWSPLAT-45972
Test plan
Verified on a gfx1100 (Radeon RX 7900) system, building
libhsa-runtime64against a current TheRock toolchain (ROCm 7.14.0a20260612, amdclang++). Compared this branch ("FIXED") against the unmodifieddevelopbuild ("BASELINE").vadd(1,048,576 elements) launched via the loader: correct results.hsa_executable_load_agent_code_objecton valid gfx1100 code objects (with and without dynamic relocations):HSA_STATUS_SUCCESS(identical to baseline). This caught and fixed a regression where a dynamic-relocation section (sh_info == 0) was initially over-rejected.sh_link/sh_info/st_shndx/r_infosymbol index; confirm clean rejection instead of a crash.develop)HSA_STATUS_SUCCESSHSA_STATUS_SUCCESSsh_linkOOB in.symtabINVALID_CODE_OBJECTsh_linkOOB in.dynsymINVALID_CODE_OBJECTsh_infoOOB in.rela.dynINVALID_CODE_OBJECTsh_linkOOB in.rela.dynINVALID_CODE_OBJECTst_shndxOOB in symbol entryINVALID_CODE_OBJECTr_infosymbol index OOB in relocationINVALID_CODE_OBJECTAll six attacker-controlled index vectors crash the unpatched loader and are cleanly rejected with
HSA_STATUS_ERROR_INVALID_CODE_OBJECTafter this change.libhsa-runtime64.unit/module/ModuleTest: 60 passed / 2 failed / 5 skipped — identical on FIXED and BASELINE. The 2 failures (hipModuleGetGlobal.cc:38, "invalid argument") reproduce byte-for-byte on the unmodifieddevelopbuild, so they are pre-existing/environmental (single-GPU box) and not introduced by this change.unit/module/module: all 8 test cases pass (1,000,397 assertions) on both FIXED and BASELINE.