Repository navigation
Fix page hash size limit for large PE files - #512
francoisgirinon-netizen wants to merge 3 commits into
Conversation
pe_page_hash_calc() sized its buffer from the whole file size, so data outside the sections (e.g. an overlay appended by an installer) was counted even though it is not hashed. For a 3.7 GB installer with a 4 KB SectionAlignment the estimate was 31 MB, above the 16 MB limit: "Page hash size exceeds limit" and signing with -ph failed, while the real page hash is a few KB. Compute the number of pages from the section table instead. The bounds of each section are checked before its size is used, and the estimate never exceeds the previous one based on the file size, so a crafted section table cannot cause a larger allocation than before. Raise the sanity limit to 512 MB: with a 4 GB PE file limit and 512 byte pages the page hash cannot be larger. Add optional large file tests (-DLARGE_FILE_TESTS=ON) that sign and verify a 2.6 GB PE with -ph, with the data in the section and as an overlay.
bf69f75 to
d60a3a3
Compare
olszomal
left a comment
There was a problem hiding this comment.
The fix looks good overall. Thank you for your contribution!
| OPENSSL_free(res); | ||
| return NULL; /* FAILED */ | ||
| } | ||
| for (l = 0; l < rs; l += pagesize, pi++) { |
There was a problem hiding this comment.
Increasing the page hash limit from 16 MB to 512 MB makes an existing uint32_t overflow in the page hashing loop reachable.
For a section close to 4 GB (e.g. SizeOfRawData = 0xFFFFF200 with 4096-byte pages), l += pagesize wraps to zero, causing the loop to hash the section again until the page hash buffer limit is reached.
Previously, such files were rejected by the 16 MB limit before reaching this loop.
I suggest changing l to uint64_t as part of this PR.
Raising the page hash limit to 512 MB makes the uint32_t overflow of 'l += pagesize' reachable for a section close to 4 GB: l wraps to zero and the section is hashed again until the page hash buffer is full.
Covers the 32-bit page offset overflow in pe_page_hash_calc(). The signed file is over 4 GB and cannot be opened again, so it is not verified.
|
Good catch, thanks. Fixed in 00e425e: I added a test in e87d27c ( Note that the signed output is over 4 GB and cannot be opened again (32-bit file size limit in |
Pull Request Type
Related Issue
Issue number: N/A
Current Behavior
Signing a large PE file with
-phfails with:pe_page_hash_calc()sizes its buffer from the size of the whole file(
fileend / pagesize) and rejects anything above 16 MB. Data that is not partof a section, such as an overlay appended by an installer, is not hashed, but it
was still counted. Reproduced on a real 3.7 GB Inno Setup installer (SectionAlignment 4096):
the estimate was 31 MB while the actual page hash is about 4.5 KB. Without
-phthe same file signs fine.
With SHA-256 and 4 KB pages the limit was reached at about 1.9 GB (SHA-1: 2.9 GB),
even for files whose sections are small.
New Behavior
The number of pages is computed from the section table, so only hashed data counts.
The bounds of each section are checked before its size is used, and the estimate
never exceeds the previous one based on the file size, so a crafted section table
cannot cause a larger allocation than before. The sanity limit is raised to 512 MB. A PE file with a certificate table is limited
to 4 GB, and with 512 byte pages (the minimum FileAlignment) the page hash cannot be
larger than that.
The same installer now signs with
-phandverifyreports the signature, themessage digest and the page hash as valid. For files that already worked the output
is unchanged: signing
tests/files/unsigned.exewith-ph -time 1556668800gives abyte-identical file before and after the change, with both SHA-1 and SHA-256.
Scope of Changes
pe.c:pe_page_hash_calc()computes the size of the page hash buffer fromthe
SizeOfRawDataof each section instead of the file size.osslsigncode.h: addSIZE_512M.cmake/CMakeTest.cmake,tests/make_large_pe.py: optional tests, enabled with-DLARGE_FILE_TESTS=ON(off by default: they need about 16 GB of disk space and a64-bit build).
They sign and verify a 2.6 GB PE with
-ph, with the data in a section and as an overlay.A third test signs a PE with a section of 0xFFFFF200 bytes (about 4 GB, so about
16 GB of disk space in total). It is not verified: the signed file is over 4 GB and
cannot be opened again.
pe.c: the page offsetlinpe_page_hash_calc()isuint64_t. With the higherlimit,
l += pagesizecould wrap to zero for a section close to 4 GB.Testing
Existing tests
New tests added
Manual testing
ctest: 381/381 passed on Linux x86-64 (OpenSSL 3.5), with-DLARGE_FILE_TESTS=ON.Both new tests fail without the change with
Page hash size exceeds limit: 22851684.Manual:
sign -h sha256 -phthenverifyon a 3.7 GB installer.A crafted 8 KB PE with a section size of 0xFFFFFE00 is rejected with the same
"invalid bounds" error as before, without a large allocation.
Not tested: Windows, macOS, 32-bit builds.
Additional Notes
size of the certificate table) and is not changed here.
License Declaration