tursodatabase/turso · error · anyhow::Error

MVCC logical log frame offset overflow

Error message

MVCC logical log frame offset overflow

What it means

The trailer offset is computed as offset + header_size + payload_size + extension_size with checked_add at every step. This error means the sum overflowed usize — reachable only when corrupted length fields carry absurd values (near u64::MAX on 64-bit, or sums >= 2^32 on 32-bit). It converts what would be silent wraparound into a clean rejection before any slicing happens.

Source

Thrown at cli/sync_server.rs:1080

        if extension_size == 0 && extension_record_count != 0 {
            return Err(anyhow!(
                "MVCC logical log extension record count without extension block at offset {offset}"
            ));
        }
        if extension_size > 0 && frame_flags & MVCC_TX_FRAME_FLAG_HAS_EXTENSION_BLOCK == 0 {
            return Err(anyhow!(
                "MVCC logical log extension block missing flag at offset {offset}"
            ));
        }
        extension_size
    } else {
        0
    };
    let trailer_start = offset
        .checked_add(header_size)
        .and_then(|value| value.checked_add(payload_size))
        .and_then(|value| value.checked_add(extension_size))
        .ok_or_else(|| anyhow!("MVCC logical log frame offset overflow"))?;
    let frame_end = trailer_start
        .checked_add(MVCC_TX_TRAILER_SIZE)
        .ok_or_else(|| anyhow!("MVCC logical log frame end overflow"))?;
    if frame_end > log.len() {
        return Ok(None);
    }
    let expected_crc = crc32c::crc32c_append(running_crc, &log[offset..trailer_start]);
    let stored_crc = read_u32_le(log, trailer_start)?;
    if stored_crc != expected_crc {
        return Err(anyhow!(
            "MVCC logical log frame checksum mismatch at offset {offset}"
        ));
    }
    let end_magic = read_u32_le(log, trailer_start + 4)?;
    if end_magic != MVCC_TX_END_MAGIC {
        return Err(anyhow!(
            "invalid MVCC logical log frame end magic at offset {offset}"
        ));

View on GitHub (pinned to bad083fafb)

Solutions

  1. Treat the log as corrupt or hostile and re-pull it from a known-good source.
  2. On 32-bit targets, run a 64-bit build to eliminate the narrower overflow window.
  3. Pre-check that payload + extension sizes are <= remaining log length before scanning.
Defensive patterns

Strategy: try-catch

Validate before calling

fn frame_sizes_within_log(log: &[u8], offset: usize, header_size: usize) -> bool {
    let payload = log.get(offset + 4..offset + 12).map(|b| u64::from_le_bytes(b.try_into().unwrap()));
    let ext = log.get(offset + 24..offset + 32).map(|b| u64::from_le_bytes(b.try_into().unwrap()));
    payload.unwrap_or(0) + ext.unwrap_or(0) <= (log.len() - offset) as u64
}

Try / catch

match scan_mvcc_log(&log) {
    Ok(snapshot) => { /* serve deltas */ }
    Err(err) if err.to_string().contains("frame offset overflow") => {
        // corrupted or hostile length fields: discard and re-pull the log
    }
    Err(err) => return Err(err),
}

Prevention

When it happens

Trigger: A frame whose payload/extension u64 fields sum with the offset past usize::MAX — corrupted or deliberately hostile length metadata; on 32-bit builds, any frame whose sizes sum to 4 GiB or more.

Common situations: Fuzzed or maliciously crafted log files with maximal length fields; 32-bit builds scanning logs with large length values; storage corruption striking multiple length bytes at once.

Related errors


AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16). Data as JSON: /api/errors/66a4311508940468. Report an issue: GitHub.