{"record":{"id":"72729396fdf6b661","repo":"rustfs/rustfs","slug":"invalid-number-of-entries","errorCode":null,"errorMessage":"invalid number of entries","messagePattern":"invalid number of entries","errorType":"exception","errorClass":"io::Error","httpStatus":null,"severity":"warning","filePath":"crates/rio-v2/src/s2_index.rs","lineNumber":205,"sourceCode":"\n        let (total_uncompressed, used) = read_varint(bytes)?;\n        if total_uncompressed < 0 {\n            return Err(io::Error::new(io::ErrorKind::InvalidData, \"invalid uncompressed size\"));\n        }\n        bytes = &bytes[used..];\n\n        let (total_compressed, used) = read_varint(bytes)?;\n        bytes = &bytes[used..];\n\n        let (est_block_uncompressed, used) = read_varint(bytes)?;\n        if est_block_uncompressed < 0 {\n            return Err(io::Error::new(io::ErrorKind::InvalidData, \"invalid block size\"));\n        }\n        bytes = &bytes[used..];\n\n        let (entries, used) = read_varint(bytes)?;\n        if entries < 0 || entries > MAX_INDEX_ENTRIES as i64 {\n            return Err(io::Error::new(io::ErrorKind::InvalidData, \"invalid number of entries\"));\n        }\n        bytes = &bytes[used..];\n\n        if bytes.is_empty() {\n            return Err(io::Error::new(io::ErrorKind::UnexpectedEof, \"buffer too small\"));\n        }\n\n        let has_uncompressed = bytes[0];\n        if has_uncompressed & 1 != has_uncompressed {\n            return Err(io::Error::new(io::ErrorKind::InvalidData, \"invalid uncompressed flag\"));\n        }\n        bytes = &bytes[1..];\n\n        let mut info = vec![\n            S2IndexInfo {\n                compressed_offset: 0,\n                uncompressed_offset: 0,\n            };","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/rustfs/rustfs/blob/9e6e02ea09c86bedf44c7bd64a74ea02a0cff1de/crates/rio-v2/src/s2_index.rs#L187-L223","documentation":"The entry count must be between 0 and 65536 inclusive (MAX_INDEX_ENTRIES = 1<<16). load rejects negative or oversized counts with InvalidData before allocating the info vector, which also guards against absurd allocations driven by corrupt counts.","triggerScenarios":"A corrupted count varint decoding to a huge or negative i64; an index written by a non-conforming writer with more than 65536 blocks.","commonSituations":"Corruption class; extremely large objects compressed with very small block sizes legitimately exceeding the entry cap.","solutions":["Fall back to no-index reads.","If more than 65536 blocks are genuinely required, raise MAX_INDEX_ENTRIES deliberately and simultaneously in writer and reader — they must agree.","Alternatively re-compress with a larger block size so the entry count stays under the cap."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"fn entry_count_plausible(entries: i64) -> bool {\n    (0..=(1 << 16)).contains(&entries)\n}","typeGuard":null,"tryCatchPattern":"match decode_minio_index_bytes(&bytes) {\n    Some(index) => use_index(index),\n    None => sequential_decompress(),\n}","preventionTips":["Keep MAX_INDEX_ENTRIES identical in writer and reader; change both atomically.","Choose compression block sizes so entry counts stay well under the cap for your largest objects."],"tags":["s2","compression","index","entry-limit","data-corruption","rust"],"backgroundTag":"corrupt-index-metadata","analyzedSha":"9e6e02ea09c86bedf44c7bd64a74ea02a0cff1de","analyzedAt":"2026-08-16T20:34:17.560Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}