Skip to content

Commit 7bcdf1a

Browse files
fix(table): reject invalid global index metadata (#578)
1 parent defdd24 commit 7bcdf1a

2 files changed

Lines changed: 229 additions & 35 deletions

File tree

crates/paimon/src/btree/meta.rs

Lines changed: 72 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,28 @@ const FORMAT_VERSION_WITH_NULL_FLAGS: u8 = 1;
3232
const FIRST_KEY_IS_NULL: u8 = 1;
3333
const 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)]
3759
pub 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

Comments
 (0)