tursodatabase/turso · error · anyhow::Error
buffer too short for u64 at offset
Error message
buffer too short for u64 at offset {offset} What it means
read_u64_le is the bounds-checked little-endian u64 reader; it errors when fewer than 8 bytes remain at the requested offset. Within read_mvcc_frame_boundary the callers only invoke it after verifying header_size + trailer fit, so a live occurrence means a truncated/garbage buffer reached the reader or a new call site skipped its length guard.
Solutions
- Go through read_mvcc_frame_boundary / scan_mvcc_log so header+trailer length checks run first
- Require buf.len() >= offset + 8 before any read_u64_le call
- Add a regression test with a buffer cut exactly at each read boundary
Defensive patterns
Strategy: validation
Validate before calling
fn has_u64_at(buf: &[u8], offset: usize) -> bool {
buf.len() >= offset.saturating_add(8)
} Type guard
fn can_read_u64(buf: &[u8], offset: usize) -> bool {
offset.checked_add(8).is_some_and(|end| end <= buf.len())
} Try / catch
let size = read_u64_le(&log, offset + 4)
.map_err(|err| err.context(format!("short frame buffer: len={} offset={}", log.len(), offset)))?; Prevention
- Use read_mvcc_frame_boundary rather than reading frame fields manually
- Assert buffer length covers the full fixed header before field reads
- Fuzz parsers with inputs cut at 4- and 8-byte read boundaries
When it happens
Trigger: read_u64_le at offset+4 (payload size) or offset+24 (extension size) on a buffer shorter than offset + 8 - e.g. an extension frame claimed to fit but the slice was clipped.
Common situations: Reusing the readers on test fixtures or captured partial logs; refactors that bypass the frame-boundary length pre-checks; embedded scanner misuse.
Related errors
- buffer too short for u32 at offset
- invalid MVCC logical log frame end magic at offset
- invalid MVCC logical log frame magic at offset
- invalid MVCC logical log header flags
- invalid MVCC logical log header length
AI-assisted analysis of tursodatabase/turso@492c4a71cd (2026-08-20).
Data as JSON: /api/errors/c0456a3b17cf5d3d.
Report an issue: GitHub.
Appendix: source
Thrown at cli/sync_server.rs:1228
if end_magic != MVCC_TX_END_MAGIC {
return Err(anyhow!(
"invalid MVCC logical log frame end magic at offset {offset}"
));
}
Ok(Some((frame_end, stored_crc)))
}
fn read_u32_le(buf: &[u8], offset: usize) -> Result<u32> {
let bytes = buf
.get(offset..offset + 4)
.ok_or_else(|| anyhow!("buffer too short for u32 at offset {offset}"))?;
Ok(u32::from_le_bytes(bytes.try_into().unwrap()))
}
fn read_u64_le(buf: &[u8], offset: usize) -> Result<u64> {
let bytes = buf
.get(offset..offset + 8)
.ok_or_else(|| anyhow!("buffer too short for u64 at offset {offset}"))?;
Ok(u64::from_le_bytes(bytes.try_into().unwrap()))
}
fn current_db_size_pages(conn: &Connection, max_frame: u64) -> Result<u64> {
if max_frame > 0 {
let frame_size = WAL_FRAME_HEADER_SIZE + PAGE_SIZE;
let mut last_frame = vec![0u8; frame_size];
let last_info = conn.wal_get_frame(max_frame, &mut last_frame)?;
Ok(last_info.db_size as u64)
} else {
Ok(0)
}
}
fn current_snapshot_db_size_pages(conn: &Connection, max_frame: u64) -> Result<u64> {
if max_frame > 0 {
return current_db_size_pages(conn, max_frame);
}View on GitHub (pinned to 492c4a71cd)