Skip to content

Fix heap overflow in mpMergePatch via malicious map count - #15

Merged
khanaffan merged 1 commit into
mainfrom
fix/mergepatch-heap-overflow
Jul 2, 2026
Merged

Fix heap overflow in mpMergePatch via malicious map count#15
khanaffan merged 1 commit into
mainfrom
fix/mergepatch-heap-overflow

Conversation

@khanaffan

Copy link
Copy Markdown
Owner

Summary

Fixes a heap-buffer-overflow in mpMergePatch (the RFC-7386 msgpack_patch() SQL function), found via a 30-second libFuzzer + ASan run against fuzz_msgpack.

Root cause

A patch blob's MP_MAP32 header can declare a pair count up to 0x80000001 (attacker-controlled). This count was multiplied by sizeof(MpPatchEntry)/sizeof(int) and passed to sqlite3_malloc(int), which takes a signed 32-bit int — the 64-bit product silently truncates, producing a tiny allocation. The code then writes pCount entries into that undersized buffer → heap overflow.

Crash trigger: df 80 00 00 01 31 01 02 fed as the patch blob to msgpack_patch(target, patch).

Fix

  1. Reject patch map headers whose declared pair count can't fit in the remaining buffer (every pair needs ≥2 bytes) — catches malformed/truncated input before any allocation happens.
  2. Replace sqlite3_malloc(int) with sqlite3_malloc64(sqlite3_uint64) for the pIdx and phash allocations, eliminating the signed-int truncation entirely.

Testing

  • Added 3 regression checks (6.10–6.12) in tests/test_spec_p5_mutation.c exercising bogus MAP32/MAP16 counts and a truncated MAP32 header — all must return SQLITE_ERROR, not crash.
  • Added the minimized crash input as a new seed in tests/fuzz_corpus/ (per CONTRIBUTING.md guidance).
  • Full ctest suite: 16/16 passing.
  • Replayed the original crash input under ASan post-fix: clean, no abort.
  • Fresh 30s libFuzzer run post-fix: 68,480 execs, 0 crashes.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

An MP_MAP32 header can declare a pair count up to 0x80000001. This
value was multiplied by sizeof(MpPatchEntry)/sizeof(int) and passed
to sqlite3_malloc(int), which truncates 64-bit sizes to a signed
32-bit int, causing a tiny under-allocation. The subsequent loop then
wrote pCount entries into the undersized buffer, producing a
heap-buffer-overflow reachable via msgpack_patch() on untrusted
msgpack blobs (found via libFuzzer + ASan).

Fixes:
- Reject patch map headers whose declared pair count cannot fit in
  the remaining buffer (every pair needs >=2 bytes), rejecting
  malformed/truncated input before any allocation.
- Switch the pIdx and phash allocations from sqlite3_malloc(int) to
  sqlite3_malloc64(sqlite3_uint64), removing the signed-int
  truncation entirely.

Adds regression checks in test_spec_p5_mutation.c (6.10-6.12) for
bogus MAP32/MAP16 counts and a truncated MAP32 header, plus the
minimized crash input as a new fuzz corpus seed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@khanaffan
khanaffan merged commit 702ab47 into main Jul 2, 2026
6 checks passed
@khanaffan
khanaffan deleted the fix/mergepatch-heap-overflow branch July 2, 2026 16:03
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