{"record":{"id":"c0456a3b17cf5d3d","repo":"tursodatabase/turso","slug":"buffer-too-short-for-u64-at-offset-offset","errorCode":null,"errorMessage":"buffer too short for u64 at offset {offset}","messagePattern":"buffer too short for u64 at offset (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":500,"severity":"error","filePath":"cli/sync_server.rs","lineNumber":1228,"sourceCode":"    if end_magic != MVCC_TX_END_MAGIC {\n        return Err(anyhow!(\n            \"invalid MVCC logical log frame end magic at offset {offset}\"\n        ));\n    }\n    Ok(Some((frame_end, stored_crc)))\n}\n\nfn read_u32_le(buf: &[u8], offset: usize) -> Result<u32> {\n    let bytes = buf\n        .get(offset..offset + 4)\n        .ok_or_else(|| anyhow!(\"buffer too short for u32 at offset {offset}\"))?;\n    Ok(u32::from_le_bytes(bytes.try_into().unwrap()))\n}\n\nfn read_u64_le(buf: &[u8], offset: usize) -> Result<u64> {\n    let bytes = buf\n        .get(offset..offset + 8)\n        .ok_or_else(|| anyhow!(\"buffer too short for u64 at offset {offset}\"))?;\n    Ok(u64::from_le_bytes(bytes.try_into().unwrap()))\n}\n\nfn current_db_size_pages(conn: &Connection, max_frame: u64) -> Result<u64> {\n    if max_frame > 0 {\n        let frame_size = WAL_FRAME_HEADER_SIZE + PAGE_SIZE;\n        let mut last_frame = vec![0u8; frame_size];\n        let last_info = conn.wal_get_frame(max_frame, &mut last_frame)?;\n        Ok(last_info.db_size as u64)\n    } else {\n        Ok(0)\n    }\n}\n\nfn current_snapshot_db_size_pages(conn: &Connection, max_frame: u64) -> Result<u64> {\n    if max_frame > 0 {\n        return current_db_size_pages(conn, max_frame);\n    }","sourceCodeStart":1210,"sourceCodeEnd":1246,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/cli/sync_server.rs#L1210-L1246","documentation":"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.","triggerScenarios":"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.","commonSituations":"Reusing the readers on test fixtures or captured partial logs; refactors that bypass the frame-boundary length pre-checks; embedded scanner misuse.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"fn has_u64_at(buf: &[u8], offset: usize) -> bool {\n    buf.len() >= offset.saturating_add(8)\n}","typeGuard":"fn can_read_u64(buf: &[u8], offset: usize) -> bool {\n    offset.checked_add(8).is_some_and(|end| end <= buf.len())\n}","tryCatchPattern":"let size = read_u64_le(&log, offset + 4)\n    .map_err(|err| err.context(format!(\"short frame buffer: len={} offset={}\", log.len(), offset)))?;","preventionTips":["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"],"tags":["mvcc","sync-server","buffer","bounds","parser"],"backgroundTag":"buffer-overrun","analyzedSha":"492c4a71cd7c2649e7df83da1471b74f4b1c7aa9","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}