Raise ELFError on out-of-range string-table offset instead of OverflowError - #667
Raise ELFError on out-of-range string-table offset instead of OverflowError#667eeshsaxena wants to merge 2 commits into
Conversation
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.
| # 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 |
|
Done, switched it to an f-string. |
|
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? |
|
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. |
|
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 |
|
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: 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. |
Problem
StringTableSection.get_string()passessh_offset + offsetstraight toparse_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 forstream.seek(), raising an exception that is not elftools' ownELFError:The exact exception depends on the stream type:
OverflowError/ValueErroron aBytesIO, orValueError/OSErroron a real file. Any of them can escapeELFFile.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 asELFError, consistent with how elftools reports other malformed input.Test
Added
test_string_table_offset_out_of_rangetotest/test_corrupt_files.py, assertingget_string()raisesELFErrorfor both a huge and a negative offset. It fails withOverflowErrorbefore this change and passes after. The full unit-test suite still passes (120 tests).