{"record":{"id":"e05e4439a0204aa5","repo":"tikv/tikv","slug":"log-file-name-range-start-end-exceeds-cac","errorCode":null,"errorMessage":"log file {name} range [{start}, {end}) exceeds cached physical file {} length {}","messagePattern":"log file (.+?) range \\[(.+?), (.+?)\\) exceeds cached physical file (.+?) length (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"components/compact-log-backup/src/cache.rs","lineNumber":371,"sourceCode":"        io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"log file {name} offset {offset} is too large\"),\n        )\n    })?;\n    let end = usize::try_from(offset.checked_add(length).ok_or_else(|| {\n        io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"log file {name} offset {offset} + length {length} overflows\"),\n        )\n    })?)\n    .map_err(|_| {\n        io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\"log file {name} offset {offset} + length {length} is too large\"),\n        )\n    })?;\n    if end > content.len() {\n        return Err(io::Error::new(\n            io::ErrorKind::InvalidData,\n            format!(\n                \"log file {name} range [{start}, {end}) exceeds cached physical file {} length {}\",\n                name,\n                content.len()\n            ),\n        ));\n    }\n    Ok(content.slice(start..end))\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    fn ready_part(decision: CacheDecision) -> Bytes {\n        match decision {\n            CacheDecision::Ready(part) => part,","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/tikv/tikv/blob/78aedc1c81ef3f7d8bacc6e9d09f56460f134937/components/compact-log-backup/src/cache.rs#L353-L389","documentation":"checked_get_range validates that the requested [start, end) byte range fits within the cached physical file's content before slicing. If end exceeds the cached content length, it throws this InvalidData io::Error, preventing out-of-bounds reads on the in-memory cache buffer.","triggerScenarios":"cache_decision requests a range whose end (offset + length) is larger than the actual size of the cached log file — e.g. the cached copy is shorter than the metadata claims (truncated download or stale cache entry).","commonSituations":"A log file was truncated on object storage but metadata still refers to the original length; a partially downloaded/prefetched cache entry; mismatch between cached file version and the referenced record offsets.","solutions":["Invalidate and refetch the cached file to ensure the cache holds the complete content.","Re-download the log file from storage and verify its checksum/length against metadata.","Clamp or validate requested ranges against the actual file length recorded when caching.","Re-run log backup compaction if the source file itself is truncated."],"exampleFix":"// before\nlet slice = checked_get_range(&content, name, off, len)?;\n// after\nlet end = off.checked_add(len).ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, \"overflow\"))?;\nassert!(end <= content.len(), \"range exceeds cached file {name}\");\nlet slice = checked_get_range(&content, name, off, len)?;","handlingStrategy":"validation","validationCode":"fn range_fits(offset: u64, length: u64, cached_len: usize) -> bool {\n    offset.checked_add(length).map_or(false, |end| (end as usize) <= cached_len)\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = cache_decision(...).await {\n    if e.kind() == std::io::ErrorKind::InvalidData && e.to_string().contains(\"exceeds cached physical file\") {\n        cache.invalidate(name); // refetch the full file\n    }\n    return Err(e);\n}","preventionTips":["Record the cached content length at fetch time and clamp requests to it.","Invalidate cache entries when the underlying object changes or a length mismatch is detected.","Compare metadata-declared file size with the downloaded size before caching."],"tags":["io","cache","bounds-check","log-backup","rust"],"backgroundTag":"range-out-of-bounds","analyzedSha":"78aedc1c81ef3f7d8bacc6e9d09f56460f134937","analyzedAt":"2026-09-03T23:31:32.398Z","contentChangedAt":"2026-09-03T23:31:32.398Z","schemaVersion":2},"datasetVersion":"2026-09-11T07:07:21.782Z"}