Skip to content

Raise ELFError on out-of-range string-table offset instead of OverflowError - #667

Closed
eeshsaxena wants to merge 2 commits into
eliben:mainfrom
eeshsaxena:fix/strtab-offset-out-of-range
Closed

Raise ELFError on out-of-range string-table offset instead of OverflowError#667
eeshsaxena wants to merge 2 commits into
eliben:mainfrom
eeshsaxena:fix/strtab-offset-out-of-range

Conversation

@eeshsaxena

Copy link
Copy Markdown

Problem

StringTableSection.get_string() passes sh_offset + offset straight to parse_cstring_from_stream(), which seeks the stream to that absolute position. A corrupt name offset (easy to hit with a malformed or fuzzed ELF) can push the position out of range for stream.seek(), raising an exception that is not elftools' own ELFError:

import io
from elftools.elf.elffile import ELFFile
data = bytearray(open('test/testfiles_for_readelf/simple_aarch64_gcc.o.elf','rb').read())
data[511] ^= 0xFF                       # corrupt a section-name offset
elf = ELFFile(io.BytesIO(bytes(data)))
[s.name for s in elf.iter_sections()]   # OverflowError: Python int too large to convert to C ssize_t

The exact exception depends on the stream type: OverflowError/ValueError on a BytesIO, or ValueError/OSError on a real file. Any of them can escape ELFFile.iter_sections() when a section (or symbol) name is read.

Fix

Wrap the string-table lookup in get_string() and re-raise an out-of-range seek as ELFError, consistent with how elftools reports other malformed input.

Test

Added test_string_table_offset_out_of_range to test/test_corrupt_files.py, asserting get_string() raises ELFError for both a huge and a negative offset. It fails with OverflowError before this change and passes after. The full unit-test suite still passes (120 tests).

StringTableSection.get_string() passes sh_offset + offset straight to
parse_cstring_from_stream(), which seeks the stream to that absolute
position. A corrupt name offset (common in a malformed/fuzzed ELF) can put
the position out of range for stream.seek(), which raises OverflowError or
ValueError on a BytesIO, or ValueError/OSError on a real file, none of
which are elftools' own ELFError. This can escape ELFFile.iter_sections()
when reading a section (or symbol) name.

Wrap the lookup and re-raise as ELFError. Add a regression test.
Comment thread elftools/elf/sections.py Outdated
# raises OverflowError/ValueError (BytesIO) or ValueError/OSError
# (a real file). Surface any of them as an ELFError.
raise ELFError(
'Invalid string offset %s in string table' % offset) from e

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Could you use an f-string here?

@eeshsaxena

Copy link
Copy Markdown
Author

Done, switched it to an f-string.

@sevaa

sevaa commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Not to rain on the parade, but there are a myriad places where DWARF can be malformed in such a way that an offset leads nowhere. Are we really trying to put a semi-friendly exception on each of those cases?

@eliben

eliben commented Aug 12, 2026

Copy link
Copy Markdown
Owner

Not to rain on the parade, but there are a myriad places where DWARF can be malformed in such a way that an offset leads nowhere. Are we really trying to put a semi-friendly exception on each of those cases?

That's a fair point!

@eeshsaxena did you encounter this in some real file you were working with?

@sevaa

sevaa commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

Also, the "offset leads nowhere" condition doesn't necessarily mean "the offset is being generated wrongly". With DWARF being in no way self synchronizing, chances are the parser has misinterpreted a data structure elsewhere and went on to read an offset from bytes that were never meant to be one. The friendly exception might be misleading re: the root cause of the misparse. See #482.

@eeshsaxena

Copy link
Copy Markdown
Author

Honest answer: no, I did not hit this on a real binary. I was feeding deliberately malformed input, a string-table offset pointing past the end of the section, so treat it as a robustness nit rather than something from the wild.

@sevaa's point is fair and I do not want to carpet every dead-offset path with a friendly message, especially since a bad offset here is often a symptom of a misparse upstream rather than the real fault. The only thing that nudged me on this particular spot is the exception type: an out-of-range index leaks a bare OverflowError (or IndexError) where the surrounding code raises ELFError for malformed structures, so a caller wrapping ELFError gets surprised. If you would rather keep the parser thin and not special-case it, I am happy to close, no hard feelings. Your call.

@eeshsaxena

Copy link
Copy Markdown
Author

Honest answer to your question, @eliben: I hit this through malformed / fuzzed input while probing error handling, not a real-world file, so I don't have a genuine corrupt binary to point at.

@sevaa's point is fair and I think it's correct: since ELF/DWARF parsing isn't self-synchronizing, an out-of-range string offset here is usually a symptom of a misparse upstream rather than a genuinely bad offset in the file. So my message ("Invalid string offset ...") editorializes about a root cause it can't actually know.

The narrow thing I was after was just the exception type: get_string() currently lets a raw OverflowError/ValueError/OSError from stream.seek() escape, so code parsing an untrusted file can't catch it as a pyelftools error. Converting it to ELFError keeps the library's failure surface consistent for callers.

If that's worth having, I'm happy to reword the message to something neutral that doesn't claim the offset itself is wrong (e.g. "string table read failed at offset N"). And if you'd rather handle this closer to the root of the misparse per #482 instead, that's completely reasonable — feel free to close this one.

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.

3 participants