pub const MAX_RMPV_DEPTH: usize = 128;
pub fn check_msgpack_depth(bytes: &[u8], max_depth: usize) -> Result<(), String> {
let len = bytes.len();
let mut pos: usize = 0;
let mut depth: usize = 0;
let mut remaining: Vec<usize> = Vec::new();
while pos < len {
let b = bytes[pos];
pos += 1;
let (skip, children) = match b {
0x00..=0x7f => (0, 0),
0x80..=0x8f => (0, ((b & 0x0f) as usize) * 2),
0x90..=0x9f => (0, (b & 0x0f) as usize),
0xa0..=0xbf => ((b & 0x1f) as usize, 0),
0xc0..=0xc3 => (0, 0),
0xc4 => {
if pos >= len {
break;
}
(1 + bytes[pos] as usize, 0)
}
0xc5 => {
if pos + 1 >= len {
break;
}
let n = u16::from_be_bytes([bytes[pos], bytes[pos + 1]]) as usize;
(2 + n, 0)
}
0xc6 => {
if pos + 3 >= len {
break;
}
let n = u32::from_be_bytes([
bytes[pos],
bytes[pos + 1],
bytes[pos + 2],
bytes[pos + 3],
]) as usize;
(4 + n, 0)
}
0xc7 => {
if pos >= len {
break;
}
(2 + bytes[pos] as usize, 0)
}
0xc8 => {
if pos + 1 >= len {
break;
}
let n = u16::from_be_bytes([bytes[pos], bytes[pos + 1]]) as usize;
(3 + n, 0)
}
0xc9 => {
if pos + 3 >= len {
break;
}
let n = u32::from_be_bytes([
bytes[pos],
bytes[pos + 1],
bytes[pos + 2],
bytes[pos + 3],
]) as usize;
(5 + n, 0)
}
0xca => (4, 0),
0xcb => (8, 0),
0xcc | 0xd0 => (1, 0),
0xcd | 0xd1 => (2, 0),
0xce | 0xd2 => (4, 0),
0xcf | 0xd3 => (8, 0),
0xd4 => (2, 0),
0xd5 => (3, 0),
0xd6 => (5, 0),
0xd7 => (9, 0),
0xd8 => (17, 0),
0xd9 => {
if pos >= len {
break;
}
(1 + bytes[pos] as usize, 0)
}
0xda => {
if pos + 1 >= len {
break;
}
let n = u16::from_be_bytes([bytes[pos], bytes[pos + 1]]) as usize;
(2 + n, 0)
}
0xdb => {
if pos + 3 >= len {
break;
}
let n = u32::from_be_bytes([
bytes[pos],
bytes[pos + 1],
bytes[pos + 2],
bytes[pos + 3],
]) as usize;
(4 + n, 0)
}
0xdc => {
if pos + 1 >= len {
break;
}
let n = u16::from_be_bytes([bytes[pos], bytes[pos + 1]]) as usize;
pos += 2;
(0, n)
}
0xdd => {
if pos + 3 >= len {
break;
}
let n = u32::from_be_bytes([
bytes[pos],
bytes[pos + 1],
bytes[pos + 2],
bytes[pos + 3],
]) as usize;
pos += 4;
(0, n)
}
0xde => {
if pos + 1 >= len {
break;
}
let n = u16::from_be_bytes([bytes[pos], bytes[pos + 1]]) as usize;
pos += 2;
(0, n * 2)
}
0xdf => {
if pos + 3 >= len {
break;
}
let n = u32::from_be_bytes([
bytes[pos],
bytes[pos + 1],
bytes[pos + 2],
bytes[pos + 3],
]) as usize;
pos += 4;
(0, n * 2)
}
0xe0..=0xff => (0, 0),
};
pos += skip;
if children > 0 {
let remaining_bytes = len.saturating_sub(pos);
if children > remaining_bytes {
return Err(format!(
"msgpack container declares {children} elements but only {remaining_bytes} bytes remain"
));
}
depth += 1;
if depth > max_depth {
return Err(format!("msgpack nesting depth exceeds limit ({max_depth})"));
}
remaining.push(children);
} else {
while let Some(count) = remaining.last_mut() {
*count -= 1;
if *count == 0 {
remaining.pop();
depth -= 1;
} else {
break;
}
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn accepts_flat_structures() {
assert!(check_msgpack_depth(&[0x01, 0x02, 0x03], 128).is_ok());
}
#[test]
fn accepts_empty_input() {
assert!(check_msgpack_depth(&[], 128).is_ok());
}
#[test]
fn rejects_truncated_container() {
assert!(check_msgpack_depth(&[0x81], 128).is_err());
assert!(check_msgpack_depth(&[0x91], 128).is_err());
}
#[test]
fn rejects_forged_element_count() {
let mut bytes = vec![0xdf];
bytes.extend_from_slice(&0xFFFF_FFFFu32.to_be_bytes());
bytes.extend_from_slice(&[0xa1, b'k', 0x01]);
let err = check_msgpack_depth(&bytes, 128).unwrap_err();
assert!(err.contains("elements"));
}
#[test]
fn rejects_deeply_nested() {
let mut bytes = vec![0x01]; for _ in 0..200 {
let mut wrapper = vec![0x81, 0xa1, b'a'];
wrapper.extend_from_slice(&bytes);
bytes = wrapper;
}
assert!(check_msgpack_depth(&bytes, 128).is_err());
assert!(check_msgpack_depth(&bytes, 300).is_ok());
}
#[test]
fn max_rmpv_depth_matches_serde_json() {
assert_eq!(MAX_RMPV_DEPTH, 128);
}
}