Skip to content

Fix integer overflow in safeAddToBufferSize on MSVC#3120

Open
Ashutosh0x wants to merge 2 commits intogoogle-deepmind:mainfrom
Ashutosh0x:fix/safe-overflow-msvc
Open

Fix integer overflow in safeAddToBufferSize on MSVC#3120
Ashutosh0x wants to merge 2 commits intogoogle-deepmind:mainfrom
Ashutosh0x:fix/safe-overflow-msvc

Conversation

@Ashutosh0x
Copy link
Copy Markdown
Contributor

@Ashutosh0x Ashutosh0x commented Feb 20, 2026

Hey there 👋

While digging through the model loading code, I noticed that the overflow protection in safeAddToBufferSize() only kicks in on GCC/Clang (via __builtin_*_overflow). On MSVC — which is what most Windows users build with — the #else fallback just does raw arithmetic with no overflow checks:

// TODO: offer a safe implementation for MSVC or other compilers that don't have the builtins
*nbuffer += SKIP(*offset) + type_size*nr*nc;
*offset += SKIP(*offset) + type_size*nr*nc;

There's even a TODO comment about it! The values nr and nc come straight from the .mjb binary file header, so a maliciously crafted model file could overflow the multiplication, trick the allocator into creating a too-small buffer, and then bufread happily writes past the end of it.

What this PR does

src/engine/engine_io.c Replaces the unchecked fallback with proper manual overflow detection. Each multiplication and addition step is individually guarded using SIZE_MAX / INTPTR_MAX comparisons before proceeding. If any step would overflow, we bail out early and return 0 (same behavior as the GCC/Clang path).

test/engine/engine_io_test.cc Adds a regression test (LoadModelBufferRejectsOverflowingSizes) that saves a trivial model to a binary buffer, mutates one of the size fields to something absurdly large, and checks that mj_loadModelBuffer gracefully returns NULL instead of crashing.

How I tested it

  • The new test exercises the overflow detection path on any compiler
  • On GCC/Clang the __builtin_*_overflow path still runs as before (no behavior change)
  • On MSVC the new manual checks now match the same safety guarantees

Why this matters

Without this fix, any MSVC-built binary (including the official pip wheels on Windows) would accept a crafted .mjb file and hit a heap buffer overflow. At best that's a crash, at worst it's exploitable.

Happy to iterate if you'd like any changes to the approach!

cc @yuvaltassa @quagla

The MSVC fallback path in safeAddToBufferSize() performed unchecked
arithmetic (type_size*nr*nc) on attacker-controlled values read from
.mjb binary model files. This could cause integer overflow, leading
to an undersized heap allocation followed by a heap buffer overflow
when data is copied into the buffer.

The fix adds manual overflow detection using SIZE_MAX/INTPTR_MAX
comparisons, matching the behavior of the existing __builtin_*_overflow
path used on GCC/Clang.

Also adds a regression test that crafts a binary model buffer with
overflow-inducing size fields and asserts safe rejection.
The MuJoCo test suite fixture translates mju_warning into ADD_FAILURE.
Since mj_loadModelBuffer intentionally calls mju_warning when it safely
rejects our crafted overflow buffer, this caused the test to fail.

We now intercept the warning to assert it is triggered without failing the test.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant