validate aiString length to prevent out-of-bounds read/write#6599
validate aiString length to prevent out-of-bounds read/write#6599jmestwa-coder wants to merge 3 commits into
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughRead now reads the length via Read<ai_uint32>, rejects oversized lengths, verifies the exact number of bytes read for string data and null-terminates. AssbinImporter::InternReadFile wraps opened IOStream in a ChangesAssbin loader + test
Sequence Diagram(s)sequenceDiagram
participant Importer
participant IOHandler
participant IOSystem
participant Stream
participant Reader
Importer->>IOHandler: Open(file)
IOHandler-->>Importer: IOStream*
Note right of Importer: wrap IOStream* in std::unique_ptr with deleter calling IOSystem::Close
Importer->>Stream: use stream.get() for seeks/reads
Importer->>Reader: Read<ai_uint32> (s.length)
Reader->>Stream: read 4 bytes (length)
alt s.length >= AI_MAXLEN
Reader-->>Importer: throw DeadlyImportError("ASSBIN: String length too large, potential buffer overflow attempt")
else s.length valid
Reader->>Stream: read s.length bytes into s.data
alt bytesRead != s.length
Reader-->>Importer: throw DeadlyImportError("ASSBIN: Unexpected EOF reading string data")
else
Reader-->>Importer: null-terminate and continue parsing
end
end
Note left of Importer: unique_ptr deleter calls IOSystem::Close on stream destruction
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
kimkulling
left a comment
There was a problem hiding this comment.
Looks good. I love the way you are testing the out-of-bounds access while reading an aiString. Thanks for your great work!
|
The sanitizer is not happy. Could you please take a look? |
|
Needs some RAII here: assimp/code/AssetLib/Assbin/AssbinLoader.cpp Line 680 in 17c12da This bug is not caused by this PR but merely exposed. |
44b9638 to
3534712
Compare
|
Thanks for the feedback. I’ve updated the code to use RAII for IOStream so it is properly closed on all paths, including exceptions. This resolves the sanitizer issue without changing existing logic. |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
code/AssetLib/Assbin/AssbinLoader.cpp (1)
728-747:⚠️ Potential issue | 🟠 MajorUse
std::vectorfor exception-safe buffer management in compressed data decompression.The current code has a memory leak:
ReadBinaryScene(&io, pScene)at line 747 can throw fromRead<aiString>()validation, bypassing the cleanup code at lines 749-750. Replace raw pointer allocations withstd::vector<unsigned char>for automatic cleanup. Also replace theai_assertat line 735 with a proper exception throw since read failures are recoverable errors, not programming bugs.♻️ Proposed RAII fix
`#include` <assimp/scene.h> `#include` <memory> +#include <vector> @@ - unsigned char *compressedData = new unsigned char[compressedSize]; - size_t len = stream->Read(compressedData, 1, compressedSize); - ai_assert(len == compressedSize); + std::vector<unsigned char> compressedData(compressedSize); + const size_t len = stream->Read(compressedData.data(), 1, compressedSize); + if (len != compressedSize) { + throw DeadlyImportError("ASSBIN: Unexpected EOF while reading compressed data"); + } - unsigned char *uncompressedData = new unsigned char[uncompressedSize]; + std::vector<unsigned char> uncompressedData(uncompressedSize); - int res = uncompress(uncompressedData, &uncompressedSize, compressedData, (uLong)len); + int res = uncompress(uncompressedData.data(), &uncompressedSize, compressedData.data(), static_cast<uLong>(len)); if (res != Z_OK) { - delete[] uncompressedData; - delete[] compressedData; throw DeadlyImportError("Zlib decompression failed."); } - MemoryIOStream io(uncompressedData, uncompressedSize); + MemoryIOStream io(uncompressedData.data(), uncompressedSize); ReadBinaryScene(&io, pScene); - - delete[] uncompressedData; - delete[] compressedData;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@code/AssetLib/Assbin/AssbinLoader.cpp` around lines 728 - 747, Replace the raw new[] buffers and ai_assert with exception-safe RAII: allocate compressed and uncompressed buffers as std::vector<unsigned char> (use compressedData.data() and uncompressedData.data() when calling stream->Read and uncompress) so they automatically free on exceptions, and replace the ai_assert(len == compressedSize) with a proper throw (e.g., throw DeadlyImportError) when the read returns a different size; keep the rest of the flow (Read<uint32_t>(stream.get()), MemoryIOStream(io uses uncompressedData.data(), uncompressedSize), and ReadBinaryScene(&io, pScene)) unchanged so Read<aiString>() exceptions propagate safely without leaking memory.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@code/AssetLib/Assbin/AssbinLoader.cpp`:
- Around line 728-747: Replace the raw new[] buffers and ai_assert with
exception-safe RAII: allocate compressed and uncompressed buffers as
std::vector<unsigned char> (use compressedData.data() and
uncompressedData.data() when calling stream->Read and uncompress) so they
automatically free on exceptions, and replace the ai_assert(len ==
compressedSize) with a proper throw (e.g., throw DeadlyImportError) when the
read returns a different size; keep the rest of the flow
(Read<uint32_t>(stream.get()), MemoryIOStream(io uses uncompressedData.data(),
uncompressedSize), and ReadBinaryScene(&io, pScene)) unchanged so
Read<aiString>() exceptions propagate safely without leaking memory.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5f9c5743-26f5-4ff9-89f5-1f050060d142
📒 Files selected for processing (2)
code/AssetLib/Assbin/AssbinLoader.cpptest/unit/utAssbinImportExport.cpp
…ssbinLoader conflict)
|
|
@kimkulling , I resolved the merge conflicts and updated the branch. |
|
Summary
Validate
aiStringlength during Assbin import to prevent out-of-bounds memory access from malformed input.Problem
aiString::lengthis read from input and used to fill a fixed-size buffer (AI_MAXLEN) without bounds checking.This allows malformed
.assbindata to trigger out-of-bounds writes and undefined behaviour.Fix
aiString::length >= AI_MAXLENbytesRead == length)DeadlyImportErrorKey Points
Read<aiString>()(covers all Assbin string reads)Test
.assbinaiStringlengthSummary by CodeRabbit
Bug Fixes
Tests