Fix XNNPACK FlatBuffer verification and header bounds checking#18784
Fix XNNPACK FlatBuffer verification and header bounds checking#18784
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/18784
Note: Links to docs will display an error until the docs builds have been completed. ❗ 1 Active SEVsThere are 1 currently active SEVs. If your PR is affected, please view them below: ❌ 1 New Failure, 2 Unrelated FailuresAs of commit 40c83d4 with merge base 21d9c64 ( NEW FAILURE - The following job has failed:
BROKEN TRUNK - The following jobs failed but were present on the merge base:👉 Rebase onto the `viable/strict` branch to avoid these failures
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
This PR needs a
|
There was a problem hiding this comment.
Pull request overview
This PR hardens XNNPACK delegate deserialization by validating wrapper-header regions and verifying the embedded FlatBuffer before traversing it, aiming to prevent OOB reads on malformed/corrupt inputs.
Changes:
- Add bounds/overlap checks for the flatbuffer and constant-data regions in
XNNHeader::Parse. - Track the effective flatbuffer byte length and run FlatBuffers verifier before
GetXNNGraphaccess inXNNCompiler::compileModel.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| backends/xnnpack/runtime/XNNHeader.cpp | Adds header-based range checks for flatbuffer/constant-data offsets & sizes, including non-overlap enforcement. |
| backends/xnnpack/runtime/XNNCompiler.cpp | Introduces FlatBuffer verification using the computed flatbuffer size before parsing the graph. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Verify the FlatBuffer data integrity before accessing it. Without this, | ||
| // malformed data could cause out-of-bounds reads when traversing the | ||
| // FlatBuffer's internal offset tables. | ||
| flatbuffers::Verifier verifier(flatbuffer_data, flatbuffer_size); | ||
| ET_CHECK_OR_RETURN_ERROR( | ||
| fb_xnnpack::VerifyXNNGraphBuffer(verifier), | ||
| DelegateInvalidCompatibility, | ||
| "FlatBuffer verification failed; data may be truncated or corrupt"); |
There was a problem hiding this comment.
flatbuffers::Verifier is run via fb_xnnpack::VerifyXNNGraphBuffer(verifier), but the generated verifier typically enforces the schema’s file_identifier. In this repo, the runtime schema uses file_identifier "XN00" while the Python serializer produces buffers with identifier XN01 (and this function already intends to support both). As written, verification will likely reject valid XN01 payloads and break backward compatibility.
Consider verifying the buffer without enforcing the identifier (e.g., verifier.VerifyBuffer<fb_xnnpack::XNNGraph>(nullptr)), or selecting the expected identifier string ("XN00"/"XN01") based on the earlier identifier check and passing it to VerifyBuffer<...>(expected_id) instead of calling the generated VerifyXNNGraphBuffer.
There was a problem hiding this comment.
Can you double check BC with this change? We don't have good CI coverage for this currently (I need to fix this).
There was a problem hiding this comment.
Ah this is updated to use verifier.VerifyBuffer.. which doesn't specify the magic number
| "XNNPACK Delegate Serialization Format version identifier '%.4s' != expected XN00 or XN01'", | ||
| flatbuffers::GetBufferIdentifier(flatbuffer_data)); |
There was a problem hiding this comment.
flatbuffers::GetBufferIdentifier(flatbuffer_data) is used before any minimum-length/verification check. If flatbuffer_size is < 8 (e.g., truncated/corrupt input), this can read past the provided buffer. Add an explicit minimum-size check before calling it (at least sizeof(flatbuffers::uoffset_t) + flatbuffers::kFileIdentifierLength), or perform verification/bounds checking prior to identifier access.
| ET_CHECK_OR_RETURN_ERROR( | ||
| flatbuffer_offset <= size && flatbuffer_size <= size - flatbuffer_offset, | ||
| InvalidArgument, | ||
| "flatbuffer_offset: %u and flatbuffer_size: %u are invalid for buffer of size: %zu", | ||
| flatbuffer_offset, | ||
| flatbuffer_size, | ||
| size); |
There was a problem hiding this comment.
The new error messages print uint32_t fields using %u. In this codebase there are already places using PRIu32 for uint32_t formatting; using the PRIu32 macros here as well avoids type/format mismatches on platforms where uint32_t isn’t unsigned int and keeps formatting consistent.
…XECUTORCH-33, TOB-EXECUTORCH-34) TOB-EXECUTORCH-33: XNNCompiler::compileModel() processed FlatBuffer data via fb_xnnpack::GetXNNGraph() without first running the FlatBuffer verifier. A malformed or truncated payload could cause out-of-bounds reads when the FlatBuffer library follows internal offset tables. This adds a flatbuffers::Verifier pass (matching the pattern used in program.cpp) before any FlatBuffer accessors are called, and tracks the flatbuffer_size so the verifier knows the exact bounds of the serialized data. TOB-EXECUTORCH-34: XNNHeader::Parse() read flatbuffer_offset, flatbuffer_size, constant_data_offset, and constant_data_size from untrusted header bytes but never validated that the resulting regions actually fit within the provided buffer. Crafted offset/size values could point past the end of the buffer, leading to out-of-bounds reads in compileModel(). This adds overflow-safe bounds checks that ensure both the flatbuffer and constant data regions fall within [0, size). This PR was authored with the assistance of Claude.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Validate that flatbuffer region does not overflow or exceed the buffer. | ||
| ET_CHECK_OR_RETURN_ERROR( | ||
| flatbuffer_offset <= size && flatbuffer_size <= size - flatbuffer_offset, | ||
| InvalidArgument, | ||
| "flatbuffer_offset: %" PRIu32 " and flatbuffer_size: %" PRIu32 | ||
| " are invalid for buffer of size: %zu", | ||
| flatbuffer_offset, | ||
| flatbuffer_size, | ||
| size); |
This PR was authored with the assistance of Claude.