-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Perform additional checks in PEHeaders
.
#113048
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Overview
This PR improves the robustness of PE file parsing by adding additional validity checks for object files (COFF files). The key changes include:
- Introducing stricter validation for COFF files by verifying SizeOfOptionalHeader, PointerToSymbolTable, and Characteristics.
- Updating section header reading to ensure file boundaries are not exceeded.
- Adding a per-section check to confirm that the VirtualSize is zero in COFF files.
Reviewed Changes
File | Description |
---|---|
src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaders.cs | Added COFF file validation and enhanced section header boundary checks |
Copilot reviewed 1 out of 1 changed files in this pull request and generated no comments.
Comments suppressed due to low confidence (3)
src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaders.cs:102
- [nitpick] Verify that the check for '_coffHeader.PointerToSymbolTable >= size' correctly handles edge cases where the pointer equals the file size, ensuring no valid COFF files are mistakenly rejected.
if (_coffHeader.SizeOfOptionalHeader != 0 || _coffHeader.PointerToSymbolTable >= size || (_coffHeader.Characteristics & ImageOnlyCharacteristics) != 0)
src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaders.cs:308
- The boundary check for section headers is important; please verify that the arithmetic correctly prevents buffer overreads when the reader's offset is near the end of the file.
if (numberOfSections < 0 || numberOfSections * SectionHeader.Size > size - reader.Offset)
src/libraries/System.Reflection.Metadata/src/System/Reflection/PortableExecutable/PEHeaders.cs:318
- [nitpick] The new check ensuring 'VirtualSize' is zero in COFF files is a critical validation step; confirm that this does not inadvertently flag any edge case valid files.
if (isCoffOnly && sectionHeader.VirtualSize != 0)
@@ -301,7 +314,12 @@ private ImmutableArray<SectionHeader> ReadSectionHeaders(ref PEBinaryReader read | |||
|
|||
for (int i = 0; i < numberOfSections; i++) | |||
{ | |||
builder.Add(new SectionHeader(ref reader)); | |||
var sectionHeader = new SectionHeader(ref reader); | |||
if (isCoffOnly && sectionHeader.VirtualSize != 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#112830 (comment) said that VirtualAddress
is the field that must be zero in object files, but according to documentation it's VirtualSize
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That sounds right. For VirtualAddress
the doc says "For object files, this field is the address of the first byte before relocation is applied; for simplicity, compilers should set this to zero.". So yeah, we probably don't want to enforce zero for object files. Even for executable files this may be zero for some sections like .debug
.
Test failures:
|
Most test failures were due to typos, but in one test we were opening an |
@@ -50,6 +50,13 @@ public void Ctor_Streams() | |||
Assert.Equal(0, s.Position); | |||
} | |||
|
|||
[Fact] | |||
public void Ctor_InvalidFile() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What case is this catching from above, and related question is what coverage do we have on the new checks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It catches the "size of optional header is nonzero" case. I can add more tests.
Should this be labeled as a breaking change? |
The list of heuristics I gave in #112830 (comment) is somewhat haphazard. Now that I'm looking at the actual code, I wonder if it would be better to do fewer of these validations (it also helps in having to write fewer tests since there's fewer codepaths that need to be covered). The things that we need to validate are things that would make us crash in bad ways or are structurally undefined:
We'll allow more COFF object file-like files but looking at the APIs on
The 99% use case of PEHeader is for getting MetadataStartOffset and MetadataSize so that we can get a metadata reader. And this is going to be pretty well defined if we do the above validation that prevents crashes only. |
Updated PR to check only for |
@@ -93,13 +93,21 @@ public PEHeaders(Stream peStream, int size, bool isLoadedImage) | |||
_coffHeaderStartOffset = reader.Offset; | |||
_coffHeader = new CoffHeader(ref reader); | |||
|
|||
if (!isCoffOnly) | |||
if (isCoffOnly) | |||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{ | |
{ | |
if (isLoadedImage) | |
{ | |
throw new BadImageFormatException(SR.InvalidPESignature); | |
} |
What do you think about adding this check (a bit earlier)? The OS can't load an object file, can it?
Fixes #48419.
Supersedes #112830 for now.
See also #112830 (comment).
This PR updates the constructor of
PEHeaders
to perform some additional checks to detect files with an obviously invalid structure. Specifically:CoffHeader.SizeOfOptionalHeader
is checked to be zero, in object files.CoffHeader.PointerToSymbolTable
is checked to point inside the file, in object files.CoffHeader.Characteristics
is checked to not have the image-only bits set, in object files.SectionHeader.VirtualSize
is checked to be zero, in object files.CoffHeader.NumberOfSections
sections, in both object and image files.