{"record":{"id":"41a0118c497dd7cc","repo":"tursodatabase/turso","slug":"buffer-too-short-for-u32-at-offset-offset","errorCode":null,"errorMessage":"buffer too short for u32 at offset {offset}","messagePattern":"buffer too short for u32 at offset (.+?)","errorType":"http","errorClass":"anyhow::Error","httpStatus":500,"severity":"error","filePath":"cli/sync_server.rs","lineNumber":1221,"sourceCode":"    let stored_crc = read_u32_le(log, trailer_start)?;\n    if stored_crc != expected_crc {\n        return Err(anyhow!(\n            \"MVCC logical log frame checksum mismatch at offset {offset}\"\n        ));\n    }\n    let end_magic = read_u32_le(log, trailer_start + 4)?;\n    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)","sourceCodeStart":1203,"sourceCodeEnd":1239,"githubUrl":"https://github.com/tursodatabase/turso/blob/492c4a71cd7c2649e7df83da1471b74f4b1c7aa9/cli/sync_server.rs#L1203-L1239","documentation":"read_u32_le is the parser's bounds-checked little-endian u32 reader; it errors when fewer than 4 bytes remain at the requested offset. In the sync server's own flow the callers pre-check lengths (e.g. scan_mvcc_log requires 56 bytes before validate_mvcc_log_header), so hitting this error means a length precondition was violated upstream or the helpers were reused with an unchecked buffer.","triggerScenarios":"Calling read_u32_le (directly or via validate_mvcc_log_header / initial_mvcc_log_crc / read_mvcc_frame_boundary) with offset > buf.len() - 4, e.g. a log shorter than the fixed header passed straight to the header validator.","commonSituations":"Embedding or reusing these parsing helpers on truncated buffers; tests feeding short fixtures; refactors that skip the scan_mvcc_log length pre-checks.","solutions":["Route parsing through scan_mvcc_log, which enforces log.len() >= MVCC_LOG_HEADER_SIZE first","Check buffer length before reading: require buf.len() >= offset + 4","On error, log buffer length and offset to find which caller skipped its precondition"],"exampleFix":"// before\nlet salt = u64::from_le_bytes(log[SALT_START..SALT_END].try_into().unwrap()); // panics or reads short buffer\n\n// after\nlet bytes = log\n    .get(SALT_START..SALT_END)\n    .ok_or_else(|| anyhow!(\"buffer too short for u64 at offset {SALT_START}\"))?;\nlet salt = u64::from_le_bytes(bytes.try_into().expect(\"fixed-size salt slice\"));","handlingStrategy":"validation","validationCode":"fn has_u32_at(buf: &[u8], offset: usize) -> bool {\n    buf.len() >= offset.saturating_add(4)\n}","typeGuard":"fn can_read_u32(buf: &[u8], offset: usize) -> bool {\n    offset.checked_add(4).is_some_and(|end| end <= buf.len())\n}","tryCatchPattern":"match read_u32_le(&log, offset) {\n    Ok(v) => v,\n    Err(err) if err.to_string().starts_with(\"buffer too short for u32\") => {\n        // caller precondition failed: log len/offset and fix the missing length check\n        return Err(err.context(format!(\"len={} offset={}\", log.len(), offset)));\n    }\n    Err(err) => return Err(err),\n}","preventionTips":["Always route log parsing through scan_mvcc_log so header-length pre-checks run","Prefer bounds-checked readers (buf.get) over direct indexing in parsers","Add tests with buffers truncated at every field boundary"],"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"}