Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -738,18 +738,28 @@ namespace elf {
GElfStringTable* strtab() override;
GElfSymbolTable* getReferencedSymbolTable(uint16_t index)
{
return static_cast<GElfSymbolTable*>(section(index));
// index derives from an attacker-controlled sh_link. section() already
// bounds-checks and returns nullptr for an out-of-range index, but a
// crafted value may reference an in-range wrong-type section. Verify it
// is actually a symbol table so the static_cast cannot produce a
// mis-typed pointer later virtual-dispatched during relocation.
GElfSection* s = section(index);
if (s && (s->type() == SHT_SYMTAB || s->type() == SHT_DYNSYM))
return static_cast<GElfSymbolTable*>(s);
return nullptr;
}
GElfSymbolTable* getSymtab(uint16_t index) override
{
if (section(index)->type() == SHT_SYMTAB)
return static_cast<GElfSymbolTable*>(section(index));
GElfSection* s = section(index);
if (s && s->type() == SHT_SYMTAB)
return static_cast<GElfSymbolTable*>(s);
return nullptr;
}
GElfSymbolTable* getDynsym(uint16_t index) override
{
if (section(index)->type() == SHT_DYNSYM)
return static_cast<GElfSymbolTable*>(section(index));
GElfSection* s = section(index);
if (s && s->type() == SHT_DYNSYM)
return static_cast<GElfSymbolTable*>(s);
return nullptr;
}

Expand All @@ -772,7 +782,12 @@ namespace elf {
GElfSegment* segment(size_t i) override { return segments[i].get(); }
Segment* segmentByVAddr(uint64_t vaddr) override;
size_t sectionCount() override { return sections.size(); }
GElfSection* section(size_t i) override { return sections[i].get(); }
// Bounds-check attacker-controlled section indices (sh_link, sh_info,
// st_shndx). Out-of-range indices return nullptr instead of reading heap
// memory past the vector's internal array.
GElfSection* section(size_t i) override {
return i < sections.size() ? sections[i].get() : nullptr;
Comment thread
dayatsin-amd marked this conversation as resolved.
Resolved
}
Section* sectionByVAddr(uint64_t vaddr) override;
uint16_t machine() const;
uint16_t etype() const;
Expand Down Expand Up @@ -1132,10 +1147,15 @@ namespace elf {

Section* GElfSymbol::section()
{
if (Sym()->st_shndx != SHN_UNDEF) {
return symtab->elf->section(Sym()->st_shndx);
uint16_t shndx = Sym()->st_shndx;
// Reserved indices (SHN_LORESERVE..SHN_HIRESERVE, e.g. SHN_ABS,
// SHN_COMMON) do not refer to a real section, and any index >= the
// section count is out of bounds. section() itself bounds-checks, but
// guard here so a crafted st_shndx does not bypass the SHN_UNDEF check.
if (shndx == SHN_UNDEF || shndx >= SHN_LORESERVE) {
return 0;
}
return 0;
return symtab->elf->section(shndx);
}

bool GElfSymbol::push(const std::string& name, uint64_t value, uint64_t size, unsigned char type, unsigned char binding, uint16_t shndx, unsigned char other)
Expand Down Expand Up @@ -1170,7 +1190,11 @@ namespace elf {

bool GElfSymbolTable::pullData()
{
// sh_link identifies this symbol table's string table; a crafted value
// out of section-table range yields nullptr. Reject the code object
// rather than storing a garbage string-table pointer.
strtab = elf->getStringTable(hdr.sh_link);
if (!strtab) { return false; }
for (size_t i = 0; i < data0.size() / sizeof(GElf_Sym); ++i) {
symbols.push_back(std::unique_ptr<GElfSymbol>(new GElfSymbol(this, data0, i * sizeof(GElf_Sym))));
}
Expand Down Expand Up @@ -1204,7 +1228,10 @@ namespace elf {

Symbol* GElfSymbolTable::symbol(size_t i)
{
return symbols[i].get();
// i derives from the attacker-controlled relocation r_info symbol index
// (GELF_R_SYM); reject out-of-range values instead of reading past the
// symbols vector.
return i < symbols.size() ? symbols[i].get() : nullptr;
}

GElfNoteSection::GElfNoteSection(GElfImage* elf)
Expand Down Expand Up @@ -1319,8 +1346,21 @@ namespace elf {

bool GElfRelocationSection::pullData()
{
// sh_info/sh_link identify the target section and referenced symbol
// table and are attacker-controlled. sh_info == 0 (the SHN_UNDEF/null
// section) is legitimate: it marks a dynamic relocation section, and
// section() then returns nullptr, which the loader uses to route dynamic
// relocations. Only an out-of-range sh_info is malformed, so validate the
// index rather than the returned pointer.
if (hdr.sh_info >= elf->sectionCount()) { return false; }
section = elf->section(hdr.sh_info);
Comment thread
dayatsin-amd marked this conversation as resolved.
Resolved
// sh_link must reference the associated symbol table;
// getReferencedSymbolTable() returns nullptr for an out-of-range index or
// an in-range section that is not a symbol table. Reject rather than
// storing a garbage/mis-typed pointer that later gets virtual-dispatched
// during relocation.
symtab = elf->getReferencedSymbolTable(hdr.sh_link);
if (!symtab) { return false; }
Elf_Scn *lScn = elf_getscn(elf->e, ndxscn);
assert(lScn);
Elf_Data *lData = elf_getdata(lScn, nullptr);
Expand Down Expand Up @@ -1651,7 +1691,16 @@ namespace elf {

GElfStringTable* GElfImage::getStringTable(uint16_t index)
{
return static_cast<GElfStringTable*>(sections[index].get());
// index derives from the attacker-controlled sh_link field; reject
// out-of-range values instead of reading past the sections vector.
if (index >= sections.size()) { return nullptr; }
// A crafted sh_link may reference an in-range but wrong-type section
// (e.g. SHT_PROGBITS). Reject it so the static_cast below cannot produce
// a mis-typed pointer later used as a string table (type confusion).
// Note: section index 0 (SHT_NULL) is stored as a null unique_ptr.
GElfSection* s = sections[index].get();
if (!s || s->type() != SHT_STRTAB) { return nullptr; }
return static_cast<GElfStringTable*>(s);
}

GElfSymbolTable* GElfImage::addSymbolTable(const std::string& name, StringTable* stab)
Expand Down
32 changes: 26 additions & 6 deletions projects/rocr-runtime/runtime/hsa-runtime/loader/executable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1707,6 +1707,11 @@ hsa_status_t ExecutableImpl::LoadDefinitionSymbol(hsa_agent_t agent,
code::Symbol* sym,
uint32_t majorVersion)
{
// A definition symbol must reside in a real section. GetSection() returns
// nullptr for a crafted/reserved st_shndx; reject the code object here rather
// than dereferencing a null section pointer in the accessors below
// (IsAgent(), Allocation(), Alignment(), getData(), ...).
if (!sym->GetSection()) { return HSA_STATUS_ERROR_INVALID_CODE_OBJECT; }
bool isAgent = sym->IsAgent();
if (majorVersion >= 2) {
isAgent = agent.handle != 0;
Expand Down Expand Up @@ -1875,6 +1880,9 @@ Segment* ExecutableImpl::VirtualAddressSegment(uint64_t vaddr)
uint64_t ExecutableImpl::SymbolAddress(hsa_agent_t agent, code::Symbol* sym)
{
code::Section* sec = sym->GetSection();
// GetSection() returns nullptr for a crafted/reserved st_shndx; avoid
// dereferencing it in SectionSegment()/VAddr().
if (!sec) { return 0; }
Segment* seg = SectionSegment(agent, sec);
return nullptr == seg ? 0 : (uint64_t) (uintptr_t) seg->Address(sym->VAddr());
}
Expand Down Expand Up @@ -1954,6 +1962,9 @@ hsa_status_t ExecutableImpl::ApplyStaticRelocation(hsa_agent_t agent, amd::hsa::
{
hsa_status_t status = HSA_STATUS_SUCCESS;
amd::elf::Symbol* sym = rel->symbol();
// symbol() returns nullptr for a crafted r_info symbol index that is out of
// the symbol table's range; reject rather than dereferencing it below.
if (!sym) { return HSA_STATUS_ERROR_INVALID_CODE_OBJECT; }
code::RelocationSection* rsec = rel->section();
code::Section* sec = rsec->targetSection();
Segment* rseg = SectionSegment(agent, sec);
Expand Down Expand Up @@ -2016,13 +2027,16 @@ hsa_status_t ExecutableImpl::ApplyStaticRelocation(hsa_agent_t agent, amd::hsa::

case R_AMDGPU_V1_INIT_SAMPLER:
{
// section() returns nullptr for a crafted/reserved st_shndx.
elf::Section* ssec = sym->section();
if (!ssec) { return HSA_STATUS_ERROR_INVALID_CODE_OBJECT; }
if (STT_AMDGPU_HSA_METADATA != sym->type() ||
SHT_PROGBITS != sym->section()->type() ||
!(sym->section()->flags() & SHF_MERGE)) {
SHT_PROGBITS != ssec->type() ||
!(ssec->flags() & SHF_MERGE)) {
return HSA_STATUS_ERROR_INVALID_CODE_OBJECT;
}
amdgpu_hsa_sampler_descriptor_t desc;
if (!sym->section()->getData(sym->value(), &desc, sizeof(desc))) {
if (!ssec->getData(sym->value(), &desc, sizeof(desc))) {
return HSA_STATUS_ERROR_INVALID_CODE_OBJECT;
}
if (AMDGPU_HSA_METADATA_KIND_INIT_SAMP != desc.kind) {
Expand All @@ -2047,14 +2061,17 @@ hsa_status_t ExecutableImpl::ApplyStaticRelocation(hsa_agent_t agent, amd::hsa::

case R_AMDGPU_V1_INIT_IMAGE:
{
// section() returns nullptr for a crafted/reserved st_shndx.
elf::Section* ssec = sym->section();
if (!ssec) { return HSA_STATUS_ERROR_INVALID_CODE_OBJECT; }
if (STT_AMDGPU_HSA_METADATA != sym->type() ||
SHT_PROGBITS != sym->section()->type() ||
!(sym->section()->flags() & SHF_MERGE)) {
SHT_PROGBITS != ssec->type() ||
!(ssec->flags() & SHF_MERGE)) {
return HSA_STATUS_ERROR_INVALID_CODE_OBJECT;
}

amdgpu_hsa_image_descriptor_t desc;
if (!sym->section()->getData(sym->value(), &desc, sizeof(desc))) {
if (!ssec->getData(sym->value(), &desc, sizeof(desc))) {
return HSA_STATUS_ERROR_INVALID_CODE_OBJECT;
}
if (AMDGPU_HSA_METADATA_KIND_INIT_ROIMG != desc.kind &&
Expand Down Expand Up @@ -2131,6 +2148,9 @@ hsa_status_t ExecutableImpl::ApplyDynamicRelocation(hsa_agent_t agent, amd::hsa:
// VirtualAddressSegment() returns nullptr when no loaded segment covers the
// attacker-controlled r_offset; reject rather than dereferencing it.
if (!relSeg) { return HSA_STATUS_ERROR_INVALID_CODE_OBJECT; }
// symbol() returns nullptr for a crafted r_info symbol index that is out of
// the symbol table's range; reject rather than dereferencing it below.
if (!rel->symbol()) { return HSA_STATUS_ERROR_INVALID_CODE_OBJECT; }
uint64_t symAddr = 0;
switch (rel->symbol()->type()) {
case STT_OBJECT:
Expand Down
Loading