@@ -32,6 +32,28 @@ const FORMAT_VERSION_WITH_NULL_FLAGS: u8 = 1;
3232const FIRST_KEY_IS_NULL : u8 = 1 ;
3333const LAST_KEY_IS_NULL : u8 = 1 << 1 ;
3434
35+ fn invalid_meta ( message : & ' static str ) -> io:: Error {
36+ io:: Error :: new ( io:: ErrorKind :: InvalidData , message)
37+ }
38+
39+ fn read_key ( data : & [ u8 ] , pos : & mut usize ) -> io:: Result < Vec < u8 > > {
40+ let remaining = data
41+ . get ( * pos..)
42+ . ok_or_else ( || invalid_meta ( "BTreeIndexMeta key offset out of bounds" ) ) ?;
43+ let length_bytes = remaining
44+ . get ( ..4 )
45+ . ok_or_else ( || invalid_meta ( "BTreeIndexMeta key length is truncated" ) ) ?;
46+ let length = i32:: from_le_bytes ( length_bytes. try_into ( ) . unwrap ( ) ) ;
47+ let length = usize:: try_from ( length)
48+ . map_err ( |_| invalid_meta ( "BTreeIndexMeta key length is negative" ) ) ?;
49+ let key = remaining
50+ . get ( 4 ..)
51+ . and_then ( |bytes| bytes. get ( ..length) )
52+ . ok_or_else ( || invalid_meta ( "BTreeIndexMeta key data is truncated" ) ) ?;
53+ * pos += 4 + length;
54+ Ok ( key. to_vec ( ) )
55+ }
56+
3557/// Index meta for each BTree index file.
3658#[ derive( Debug , Clone ) ]
3759pub struct BTreeIndexMeta {
@@ -166,26 +188,22 @@ impl BTreeIndexMeta {
166188
167189 let mut pos = 0 ;
168190
169- let fk_len = i32:: from_le_bytes ( data[ pos..pos + 4 ] . try_into ( ) . unwrap ( ) ) as usize ;
170- pos += 4 ;
171- let mut first_key = {
172- let key = data[ pos..pos + fk_len] . to_vec ( ) ;
173- pos += fk_len;
174- Some ( key)
175- } ;
176-
177- let lk_len = i32:: from_le_bytes ( data[ pos..pos + 4 ] . try_into ( ) . unwrap ( ) ) as usize ;
178- pos += 4 ;
179- let mut last_key = {
180- let key = data[ pos..pos + lk_len] . to_vec ( ) ;
181- pos += lk_len;
182- Some ( key)
183- } ;
184-
185- let has_nulls = data[ pos] == 1 ;
191+ let mut first_key = Some ( read_key ( data, & mut pos) ?) ;
192+ let mut last_key = Some ( read_key ( data, & mut pos) ?) ;
193+ let has_nulls = * data
194+ . get ( pos)
195+ . ok_or_else ( || invalid_meta ( "BTreeIndexMeta has_nulls flag is missing" ) ) ?
196+ == 1 ;
186197 pos += 1 ;
187198
188- if data. len ( ) . saturating_sub ( pos) >= 2 {
199+ let trailer_len = data. len ( ) . saturating_sub ( pos) ;
200+ if trailer_len == 1 {
201+ return Err ( invalid_meta (
202+ "BTreeIndexMeta null flags trailer is truncated" ,
203+ ) ) ;
204+ }
205+
206+ if trailer_len >= 2 {
189207 let format_version = data[ pos] ;
190208 pos += 1 ;
191209 if format_version == FORMAT_VERSION_WITH_NULL_FLAGS {
@@ -197,7 +215,10 @@ impl BTreeIndexMeta {
197215 last_key = None ;
198216 }
199217 }
200- } else if fk_len == 0 && lk_len == 0 && has_nulls {
218+ } else if first_key. as_ref ( ) . is_some_and ( Vec :: is_empty)
219+ && last_key. as_ref ( ) . is_some_and ( Vec :: is_empty)
220+ && has_nulls
221+ {
201222 first_key = None ;
202223 last_key = None ;
203224 }
@@ -253,4 +274,36 @@ mod tests {
253274 assert ! ( !decoded. has_nulls) ;
254275 assert ! ( !decoded. only_nulls( ) ) ;
255276 }
277+
278+ #[ test]
279+ fn test_meta_rejects_invalid_key_lengths ( ) {
280+ let mut negative_first = vec ! [ 0 ; 9 ] ;
281+ negative_first[ ..4 ] . copy_from_slice ( & ( -1i32 ) . to_le_bytes ( ) ) ;
282+ let mut truncated_first = vec ! [ 0 ; 9 ] ;
283+ truncated_first[ ..4 ] . copy_from_slice ( & 10i32 . to_le_bytes ( ) ) ;
284+ let mut negative_last = vec ! [ 0 ; 9 ] ;
285+ negative_last[ 4 ..8 ] . copy_from_slice ( & ( -1i32 ) . to_le_bytes ( ) ) ;
286+ let mut truncated_last = vec ! [ 0 ; 9 ] ;
287+ truncated_last[ 4 ..8 ] . copy_from_slice ( & 10i32 . to_le_bytes ( ) ) ;
288+
289+ for encoded in [
290+ negative_first,
291+ truncated_first,
292+ negative_last,
293+ truncated_last,
294+ ] {
295+ let error = BTreeIndexMeta :: deserialize ( & encoded) . unwrap_err ( ) ;
296+ assert_eq ! ( error. kind( ) , io:: ErrorKind :: InvalidData ) ;
297+ }
298+ }
299+
300+ #[ test]
301+ fn test_meta_rejects_truncated_null_flags_trailer ( ) {
302+ let meta = BTreeIndexMeta :: new ( Some ( Vec :: new ( ) ) , Some ( Vec :: new ( ) ) , true ) ;
303+ let mut encoded = meta. serialize ( ) ;
304+ assert_eq ! ( encoded. pop( ) , Some ( 0 ) ) ;
305+
306+ let error = BTreeIndexMeta :: deserialize ( & encoded) . unwrap_err ( ) ;
307+ assert_eq ! ( error. kind( ) , io:: ErrorKind :: InvalidData ) ;
308+ }
256309}
0 commit comments