-
Notifications
You must be signed in to change notification settings - Fork 932
Fix XNNPACK FlatBuffer verification and header bounds checking #18784
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1816,16 +1816,19 @@ ET_NODISCARD Error XNNCompiler::compileModel( | |
| Result<XNNHeader> header = XNNHeader::Parse(buffer_pointer, num_bytes); | ||
| const uint8_t* flatbuffer_data = nullptr; | ||
| const uint8_t* constant_data = nullptr; | ||
| size_t flatbuffer_size = 0; | ||
| CompileAllocator compile_allocator; | ||
|
|
||
| // Header status can only either be Error::Ok or Error::NotFound | ||
| if (header.ok()) { | ||
| flatbuffer_data = reinterpret_cast<const uint8_t*>(buffer_pointer) + | ||
| header->flatbuffer_offset; | ||
| flatbuffer_size = header->flatbuffer_size; | ||
| constant_data = reinterpret_cast<const uint8_t*>(buffer_pointer) + | ||
| header->constant_data_offset; | ||
| } else if (header.error() == Error::NotFound) { | ||
| flatbuffer_data = reinterpret_cast<const uint8_t*>(buffer_pointer); | ||
| flatbuffer_size = num_bytes; | ||
| } else { | ||
| ET_LOG(Error, "XNNHeader may be corrupt"); | ||
| return header.error(); | ||
|
|
@@ -1843,6 +1846,15 @@ ET_NODISCARD Error XNNCompiler::compileModel( | |
| "XNNPACK Delegate Serialization Format version identifier '%.4s' != expected XN00 or XN01'", | ||
| flatbuffers::GetBufferIdentifier(flatbuffer_data)); | ||
|
|
||
| // 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( | ||
| verifier.VerifyBuffer<fb_xnnpack::XNNGraph>(nullptr), | ||
| DelegateInvalidCompatibility, | ||
| "FlatBuffer verification failed; data may be truncated or corrupt"); | ||
|
Comment on lines
+1849
to
+1856
|
||
|
|
||
| auto flatbuffer_graph = fb_xnnpack::GetXNNGraph(flatbuffer_data); | ||
| // initialize xnnpack | ||
| xnn_status status = xnn_initialize(/*allocator =*/nullptr); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ | |
|
|
||
| #include <executorch/backends/xnnpack/runtime/XNNHeader.h> | ||
|
|
||
| #include <cinttypes> | ||
| #include <cstring> | ||
|
|
||
| #include <executorch/runtime/core/error.h> | ||
|
|
@@ -64,6 +65,38 @@ Result<XNNHeader> XNNHeader::Parse(const void* data, size_t size) { | |
| uint64_t constant_data_size = | ||
| GetUInt64LE(header_data + XNNHeader::kConstantDataSizeOffset); | ||
|
|
||
| // 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); | ||
|
Comment on lines
+69
to
+76
|
||
| // Validate that constant data region does not overflow or exceed the buffer. | ||
| ET_CHECK_OR_RETURN_ERROR( | ||
| constant_data_offset <= size && | ||
| constant_data_size <= size - constant_data_offset, | ||
| InvalidArgument, | ||
| "constant_data_offset: %" PRIu32 " and constant_data_size: %" PRIu64 | ||
| " are invalid for buffer of size: %zu", | ||
| constant_data_offset, | ||
| constant_data_size, | ||
| size); | ||
|
|
||
| // Validate that constant data region does not overlap with flatbuffer region. | ||
| // flatbuffer should come before constant data. | ||
| ET_CHECK_OR_RETURN_ERROR( | ||
| constant_data_offset >= flatbuffer_offset && | ||
| constant_data_offset - flatbuffer_offset >= flatbuffer_size, | ||
| InvalidArgument, | ||
| "constant_data_offset: %" PRIu32 " and flatbuffer_offset: %" PRIu32 | ||
| " with flatbuffer_size: %" PRIu32 " are overlapping.", | ||
| constant_data_offset, | ||
| flatbuffer_offset, | ||
| flatbuffer_size); | ||
|
|
||
| return XNNHeader{ | ||
| flatbuffer_offset, | ||
| flatbuffer_size, | ||
|
|
||
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.
flatbuffers::GetBufferIdentifier(flatbuffer_data)is used before any minimum-length/verification check. Ifflatbuffer_sizeis < 8 (e.g., truncated/corrupt input), this can read past the provided buffer. Add an explicit minimum-size check before calling it (at leastsizeof(flatbuffers::uoffset_t) + flatbuffers::kFileIdentifierLength), or perform verification/bounds checking prior to identifier access.