Skip to content

fix: support multi-segment ELF layouts in DT_STRTAB resolution#46

Open
f-pound wants to merge 1 commit into
elfmaster:masterfrom
f-pound:fix/multi-segment-dynstr-resolution
Open

fix: support multi-segment ELF layouts in DT_STRTAB resolution#46
f-pound wants to merge 1 commit into
elfmaster:masterfrom
f-pound:fix/multi-segment-dynstr-resolution

Conversation

@f-pound
Copy link
Copy Markdown

@f-pound f-pound commented May 14, 2026

Summary

build_dynsym_data() crashes with SIGSEGV on ELF shared objects that use 3+ PT_LOAD segments where .dynstr resides in a separate RW segment rather than the text segment.

Reproduction

Any ELF .so where .dynstr resides in a separate RW LOAD segment triggers the crash. Example: PySide2/Shiboken2 binaries from Binary Ninja's Python distribution.

Root Cause

load_dynamic_segment_data() resolves DT_STRTAB using:

obj->dynstr = &obj->mem[entry.value - elf_text_base(obj)];

This assumes DT_STRTAB always lives in the text segment. For 3-segment layouts the VMA-to-file-offset mapping differs per segment:

LOAD #1: vaddr=0x000000  offset=0x000000  (text, r-x)
LOAD #2: vaddr=0x240a88  offset=0x040a88  (data, rw-)
LOAD #3: vaddr=0x243000  offset=0x054000  (dynstr+dynamic, rw-)

DT_STRTAB=0x2433c0 yields offset 0x2433c0 (past 355KB file end) instead of correct offset 0x543c0.

Additionally, build_dynsym_data() has no bounds check on st_name before indexing into dynstr, unlike build_symtab_data() which does.

Fix

  1. Use elf_address_pointer() to resolve DT_STRTAB virtual address to file offset via PT_LOAD segment walk. Handles any number of LOAD segments. Legacy text-base math kept as fallback.

  2. Add st_name < dynseg.dynstr.size bounds check in build_dynsym_data() for both 32/64-bit paths, matching the existing pattern in build_symtab_data().

  3. Relax DT_SYMTAB/DT_STRTAB anomaly checks to accept addresses in any LOAD segment via elf_address_pointer() != NULL.

Testing

Verified against the crashing binary (libshiboken2.abi3.so.5.14, 497 dynamic symbols) — all symbols now resolve correctly.


Patch by AstroSec LLC — discovered during recursive filesystem scanning.

PROBLEM
-------
build_dynsym_data() crashes with SIGSEGV (EXC_BAD_ACCESS) on ELF
shared objects that use 3+ PT_LOAD segments where .dynstr resides
in a separate RW segment rather than the text segment.

Reproduced against PySide2/Shiboken2 .so files from Binary Ninja,
but affects any ELF linked with a layout that places .dynstr
outside the text segment.

ROOT CAUSE
----------
load_dynamic_segment_data() resolves DT_STRTAB using:

    obj->dynstr = &obj->mem[entry.value - elf_text_base(obj)];

This assumes DT_STRTAB always lives in the text segment. For
3-segment layouts the VMA-to-file-offset mapping differs per
segment:

    LOAD elfmaster#1: vaddr=0x000000  offset=0x000000  (text, r-x)
    LOAD elfmaster#2: vaddr=0x240a88  offset=0x040a88  (data, rw-)
    LOAD elfmaster#3: vaddr=0x243000  offset=0x054000  (dynstr, rw-)

DT_STRTAB=0x2433c0 yields offset 0x2433c0 (past 355KB file end)
instead of correct offset 0x543c0.

Additionally, build_dynsym_data() has no bounds check on st_name
before indexing into dynstr, unlike build_symtab_data() which does.

FIX
---
1. Use elf_address_pointer() to resolve DT_STRTAB virtual address
   to file offset via PT_LOAD segment walk. This handles any number
   of LOAD segments correctly. Legacy text-base math kept as
   fallback for edge cases.

2. Add st_name < dynseg.dynstr.size bounds check in
   build_dynsym_data() for both 32-bit and 64-bit paths, matching
   the existing pattern in build_symtab_data(). Out-of-bounds
   indices resolve to "invalid_name_index" instead of crashing.

3. Relax DT_SYMTAB and DT_STRTAB anomaly checks to accept
   addresses in any LOAD segment (via elf_address_pointer != NULL)
   rather than only the text segment.

NOTE: The existing sanity_check_st_name() stub (marked XXX TODO)
remains a no-op; the bounds check in build_dynsym_data effectively
covers the dynstr case.

TESTING
-------
Verified against the crashing binary (libshiboken2.abi3.so.5.14,
497 dynamic symbols) -- all symbols now resolve correctly.
@elfmaster
Copy link
Copy Markdown
Owner

I have run into this. Thanks I'll take a peek at the patch

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.

2 participants