Skip to content

MD3: Fix MD3Importer surface header bounds checks to prevent heap overflow#6441

Merged
kimkulling merged 2 commits intoassimp:masterfrom
ydsa:develop
Jan 19, 2026
Merged

MD3: Fix MD3Importer surface header bounds checks to prevent heap overflow#6441
kimkulling merged 2 commits intoassimp:masterfrom
ydsa:develop

Conversation

@ydsa
Copy link
Copy Markdown
Contributor

@ydsa ydsa commented Jan 12, 2026

Improve bounds checks in MD3Importer::ValidateSurfaceHeaderOffsets to prevent pcSurf from accessing data outside the MD3 buffer (fixes #6070, CVE-2025-3549).

Verification

[root@localhost assimp]# ./assimp_fuzzer /root/pocs/issues6070/Assimp_MD3Importer_ValidateSurfaceHeaderOffsets-hbo
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 4073518378
INFO: Loaded 1 modules   (7 inline 8-bit counters): 7 [0x55bdbd2868c8, 0x55bdbd2868cf), 
INFO: Loaded 1 PC tables (7 PCs): 7 [0x55bdbd2868d0,0x55bdbd286940), 
./assimp_fuzzer: Running 1 inputs 1 time(s) each.
Running: /root/pocs/issues6070/Assimp_MD3Importer_ValidateSurfaceHeaderOffsets-hbo
Executed /root/pocs/issues6070/Assimp_MD3Importer_ValidateSurfaceHeaderOffsets-hbo in 2 ms
***
*** NOTE: fuzzing was not performed, you have only
***       executed the target code on a fixed set of inputs.
***

Summary by CodeRabbit

  • Bug Fixes
    • Improved file-validation to prevent crashes when loading corrupted or malformed model files; more thorough boundary checks stop invalid data from being processed and provide consistent error handling/reporting.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 12, 2026

📝 Walkthrough

Walkthrough

Added a local boundary-checking lambda and replaced composite offset checks in MD3Importer's ValidateSurfaceHeaderOffsets to ensure surface data ranges (triangles, shaders, texcoords, vertices) lie within the file bounds and throw on invalid ranges.

Changes

Cohort / File(s) Summary
MD3 Surface Offset Validation
code/AssetLib/MD3/MD3Loader.cpp
Added an inRange lambda to validate surface data offsets (OFS_TRIANGLES, OFS_SHADERS, OFS_ST, OFS_XYZNORMAL) against file size; replaced previous composite checks with uniform range checks and throw DeadlyImportError on failure.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

Bug, Fuzzer, Security Risk

Suggested reviewers

  • kimkulling

Poem

🐰 I hopped through bytes both near and far,
Found a wild pointer hiding like a star.
With careful checks and bounds in sight,
I kept the MD3s safe tonight—
Hooray for lambdas, cozy and tight! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: fixing bounds checks in MD3Importer to prevent heap overflow, which aligns with the code modifications adding boundary validation.
Linked Issues check ✅ Passed The code changes directly address the heap-buffer-overflow vulnerability in ValidateSurfaceHeaderOffsets by adding comprehensive boundary checks for all surface data chunks against file size.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing the bounds checking vulnerability in MD3Loader.cpp with no extraneous modifications introduced outside the security fix objectives.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
code/AssetLib/MD3/MD3Loader.cpp (1)

397-402: Potential integer overflow in total calculation.

While the multiplication overflow is correctly checked on line 399, the subsequent addition on line 400 (abs_offset + size_t(count) * elem_size) could still overflow if abs_offset is large. For a security-critical fix, consider adding an explicit check.

Suggested safer implementation
 auto inRange = [this, ofs](uint32_t rel_offset, uint32_t count, size_t elem_size) -> bool {
     size_t abs_offset = ofs + rel_offset;
+    if (abs_offset > fileSize) return false;
     if (count > 0 && elem_size > 0 && count > SIZE_MAX / elem_size) return false;
-    size_t total = abs_offset + size_t(count) * elem_size;
-    return abs_offset <= fileSize && total <= fileSize;
+    size_t chunk_size = size_t(count) * elem_size;
+    if (chunk_size > fileSize - abs_offset) return false;  // overflow-safe subtraction
+    return true;
 };

This approach checks abs_offset first, then uses subtraction (fileSize - abs_offset) which is safe since we've confirmed abs_offset <= fileSize.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 522c703 and 94b4f29.

📒 Files selected for processing (1)
  • code/AssetLib/MD3/MD3Loader.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: ubuntu-latest-clang++-build-and-test
  • GitHub Check: ubuntu-latest-g++-build-and-test
  • GitHub Check: windows-latest-clang.exe-build-and-test
  • GitHub Check: windows-hunter-latest-cl.exe-build-and-test
  • GitHub Check: macos-latest-clang++-build-and-test
  • GitHub Check: windows-latest-cl.exe-build-and-test
  • GitHub Check: Fuzzing
  • GitHub Check: undefined-behavior-sanitizer
  • GitHub Check: adress-sanitizer
🔇 Additional comments (1)
code/AssetLib/MD3/MD3Loader.cpp (1)

393-408: Solid defensive bounds checking for CVE-2025-3549.

The fix correctly:

  1. Validates the surface header itself is within file bounds before reading its fields
  2. Validates all four data chunk offsets (triangles, shaders, texcoords, vertices) with their respective sizes and counts
  3. Uses a reusable lambda to ensure consistent validation logic

This addresses the heap-buffer-overflow vulnerability by preventing pcSurf from being used to read data outside the MD3 buffer.

…rflow

Improve bounds checks in MD3Importer::ValidateSurfaceHeaderOffsets to prevent pcSurf from accessing data outside the MD3 buffer (fixes assimp#6070, CVE-2025-3549).

Signed-off-by: mapengyuan <mapengyuan@xfusion.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@code/AssetLib/MD3/MD3Loader.cpp`:
- Around line 391-402: The subtraction pcSurf - this->mBuffer can be UB if
pcSurf lies outside the buffer and the inRange closure can overflow on 32-bit;
first validate that pcSurf is within [this->mBuffer, this->mBuffer + fileSize)
before computing ofs, using uintptr_t or size_t pointer-to-integer casts (e.g.
compute base = (uintptr_t)this->mBuffer, ptr = (uintptr_t)pcSurf and ensure ptr
>= base && ptr < base + fileSize), then compute ofs = ptr - base; in inRange
(and callers) switch arithmetic to use size_t/uintptr_t and perform
overflow-safe checks (verify count == 0 || elem_size == 0 early, ensure count <=
SIZE_MAX/elem_size before multiplying, and check abs_offset <= fileSize &&
abs_offset + count*elem_size <= fileSize with addition overflow guard) so no
wraparound or UB can bypass the boundary checks (references: pcSurf, mBuffer,
ofs, inRange, fileSize, MD3::Surface).
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 94b4f29 and c4c39f4.

📒 Files selected for processing (1)
  • code/AssetLib/MD3/MD3Loader.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: windows-latest-clang.exe-build-and-test
  • GitHub Check: macos-latest-clang++-build-and-test
  • GitHub Check: ubuntu-latest-g++-build-and-test
  • GitHub Check: Fuzzing
  • GitHub Check: windows-latest-cl.exe-build-and-test
  • GitHub Check: windows-hunter-latest-cl.exe-build-and-test
  • GitHub Check: ubuntu-latest-clang++-build-and-test
  • GitHub Check: adress-sanitizer
  • GitHub Check: undefined-behavior-sanitizer

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines 391 to +402
// Calculate the relative offset of the surface
const int32_t ofs = int32_t((const unsigned char *)pcSurf - this->mBuffer);
if (ofs + sizeof(MD3::Surface) > fileSize) {
throw DeadlyImportError("Surface header is outside file bounds");
}

auto inRange = [this, ofs](uint32_t rel_offset, uint32_t count, size_t elem_size) -> bool {
size_t abs_offset = ofs + rel_offset;
if (count > 0 && elem_size > 0 && count > SIZE_MAX / elem_size) return false;
size_t total = abs_offset + size_t(count) * elem_size;
return abs_offset <= fileSize && total <= fileSize;
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Harden offset arithmetic to avoid UB and overflow bypass.

pcSurf - mBuffer is undefined if pcSurf is outside the buffer (possible after a malicious OFS_END), and abs_offset + count*elem_size can overflow on 32‑bit, allowing the range check to wrap. Consider pointer-as-integer validation and overflow‑safe arithmetic.

🛠️ Proposed fix
-    const int32_t ofs = int32_t((const unsigned char *)pcSurf - this->mBuffer);
-    if (ofs + sizeof(MD3::Surface) > fileSize) {
+    const auto base = reinterpret_cast<uintptr_t>(this->mBuffer);
+    const auto surf = reinterpret_cast<uintptr_t>(pcSurf);
+    if (surf < base || surf + sizeof(MD3::Surface) > base + static_cast<uintptr_t>(fileSize)) {
         throw DeadlyImportError("Surface header is outside file bounds");
     }
+    const size_t ofs = static_cast<size_t>(surf - base);

-    auto inRange = [this, ofs](uint32_t rel_offset, uint32_t count, size_t elem_size) -> bool {
-        size_t abs_offset = ofs + rel_offset;
-        if (count > 0 && elem_size > 0 && count > SIZE_MAX / elem_size) return false;
-        size_t total = abs_offset + size_t(count) * elem_size;
-        return abs_offset <= fileSize && total <= fileSize;
-    };
+    auto inRange = [this, ofs](uint32_t rel_offset, uint32_t count, size_t elem_size) -> bool {
+        if (rel_offset > fileSize) return false;
+        const size_t abs_offset = ofs + static_cast<size_t>(rel_offset);
+        if (abs_offset > fileSize) return false;
+        if (count == 0) return true;
+        if (elem_size == 0) return false;
+        const size_t remaining = static_cast<size_t>(fileSize) - abs_offset;
+        return static_cast<size_t>(count) <= remaining / elem_size;
+    };
🤖 Prompt for AI Agents
In `@code/AssetLib/MD3/MD3Loader.cpp` around lines 391 - 402, The subtraction
pcSurf - this->mBuffer can be UB if pcSurf lies outside the buffer and the
inRange closure can overflow on 32-bit; first validate that pcSurf is within
[this->mBuffer, this->mBuffer + fileSize) before computing ofs, using uintptr_t
or size_t pointer-to-integer casts (e.g. compute base =
(uintptr_t)this->mBuffer, ptr = (uintptr_t)pcSurf and ensure ptr >= base && ptr
< base + fileSize), then compute ofs = ptr - base; in inRange (and callers)
switch arithmetic to use size_t/uintptr_t and perform overflow-safe checks
(verify count == 0 || elem_size == 0 early, ensure count <= SIZE_MAX/elem_size
before multiplying, and check abs_offset <= fileSize && abs_offset +
count*elem_size <= fileSize with addition overflow guard) so no wraparound or UB
can bypass the boundary checks (references: pcSurf, mBuffer, ofs, inRange,
fileSize, MD3::Surface).

@sonarqubecloud
Copy link
Copy Markdown

@kimkulling kimkulling merged commit cf7b652 into assimp:master Jan 19, 2026
15 checks passed
@kimkulling
Copy link
Copy Markdown
Member

Merged, thank you for the contribution.

@kimkulling kimkulling added Bug Global flag to mark a deviation from expected behaviour MD3 Bugs/questions related to MD3 3D format labels Jan 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Global flag to mark a deviation from expected behaviour MD3 Bugs/questions related to MD3 3D format

Projects

Development

Successfully merging this pull request may close these issues.

Bug: Heap-based Buffer Overflow in Assimp::MD3Importer::ValidateSurfaceHeaderOffsets

2 participants