|
14 | 14 | // [u32 LE] index count (= cell count) |
15 | 15 | // [zigzag-varint deltas ...] index deltas (first value as-is, then deltas) |
16 | 16 |
|
17 | | -use crate::codec::limits::{MAX_CELLS_PER_TILE, MAX_DICT_CARDINALITY, check_decoded_size}; |
| 17 | +use crate::codec::limits::{ |
| 18 | + MAX_CELLS_PER_TILE, MAX_DICT_CARDINALITY, check_decoded_size, checked_capacity, |
| 19 | +}; |
18 | 20 | use crate::error::{ArrayError, ArrayResult}; |
19 | 21 | use crate::tile::sparse_tile::DimDict; |
20 | 22 | use crate::types::coord::value::CoordValue; |
@@ -150,9 +152,15 @@ pub fn decode_coord_axis(data: &[u8], pos: &mut usize) -> ArrayResult<DimDict> { |
150 | 152 | .expect("invariant: preceding bounds check guarantees 4 bytes available"), |
151 | 153 | ) as usize; |
152 | 154 | *pos += 4; |
153 | | - check_decoded_size(dict_count, MAX_DICT_CARDINALITY, "coord_delta dict_count")?; |
| 155 | + let capacity = checked_capacity( |
| 156 | + dict_count, |
| 157 | + MAX_DICT_CARDINALITY, |
| 158 | + data.len() - *pos, |
| 159 | + 4, |
| 160 | + "coord_delta dict_count", |
| 161 | + )?; |
154 | 162 |
|
155 | | - let mut out: Vec<CoordValue> = Vec::with_capacity(dict_count); |
| 163 | + let mut out: Vec<CoordValue> = Vec::with_capacity(capacity); |
156 | 164 | for _ in 0..dict_count { |
157 | 165 | if *pos + 4 > data.len() { |
158 | 166 | return Err(ArrayError::SegmentCorruption { |
@@ -238,9 +246,15 @@ pub fn decode_coord_axis(data: &[u8], pos: &mut usize) -> ArrayResult<DimDict> { |
238 | 246 | .expect("invariant: preceding bounds check guarantees 4 bytes available"), |
239 | 247 | ) as usize; |
240 | 248 | *pos += 4; |
241 | | - check_decoded_size(idx_count, MAX_CELLS_PER_TILE, "coord_delta idx_count")?; |
| 249 | + let capacity = checked_capacity( |
| 250 | + idx_count, |
| 251 | + MAX_CELLS_PER_TILE, |
| 252 | + data.len() - *pos, |
| 253 | + 1, |
| 254 | + "coord_delta idx_count", |
| 255 | + )?; |
242 | 256 |
|
243 | | - let mut indices: Vec<u32> = Vec::with_capacity(idx_count); |
| 257 | + let mut indices: Vec<u32> = Vec::with_capacity(capacity); |
244 | 258 | let mut prev: i64 = 0; |
245 | 259 | for _ in 0..idx_count { |
246 | 260 | let (zz, consumed) = |
@@ -363,6 +377,18 @@ mod tests { |
363 | 377 | assert_eq!(out.values, d.values); |
364 | 378 | } |
365 | 379 |
|
| 380 | + #[test] |
| 381 | + fn huge_msgpack_dictionary_count_in_tiny_payload_is_rejected_before_allocation() { |
| 382 | + let mut data = Vec::new(); |
| 383 | + data.push(DICT_TAG_MSGPACK); |
| 384 | + data.extend_from_slice(&(MAX_DICT_CARDINALITY as u32).to_le_bytes()); |
| 385 | + let result = decode_coord_axis(&data, &mut 0); |
| 386 | + assert!(matches!( |
| 387 | + result, |
| 388 | + Err(crate::error::ArrayError::SegmentCorruption { .. }) |
| 389 | + )); |
| 390 | + } |
| 391 | + |
366 | 392 | #[test] |
367 | 393 | fn zero_tag_byte_is_corruption() { |
368 | 394 | // Tag 0 is reserved/forbidden. A payload starting with 0x00 must |
|
0 commit comments