Handle defaulting trailing elements in truncated database records.

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk
2025-08-01 05:11:44 +00:00
parent 3612b04890
commit 16447f7383
2 changed files with 84 additions and 14 deletions

View File

@@ -22,7 +22,7 @@ pub(crate) fn from_slice<'a, T>(buf: &'a [u8]) -> Result<T>
where
T: Deserialize<'a>,
{
let mut deserializer = Deserializer { buf, pos: 0, rec: 0, seq: false };
let mut deserializer = Deserializer { buf, pos: 0, rec: 0, seq: 0 };
T::deserialize(&mut deserializer).debug_inspect(|_| {
deserializer
@@ -36,7 +36,7 @@ pub(crate) struct Deserializer<'de> {
buf: &'de [u8],
pos: usize,
rec: usize,
seq: bool,
seq: usize,
}
/// Directive to ignore a record. This type can be used to skip deserialization
@@ -70,9 +70,9 @@ impl<'de> Deserializer<'de> {
/// Called at the start of arrays and tuples
#[inline]
fn sequence_start(&mut self) {
debug_assert!(!self.seq, "Nested sequences are not handled at this time");
self.seq = true;
fn sequence_start(&mut self, len: usize) {
debug_assert!(self.seq == 0, "Nested sequences are not handled at this time");
self.seq = len;
}
/// Consume the current record to ignore it. Inside a sequence the next
@@ -80,7 +80,7 @@ impl<'de> Deserializer<'de> {
/// deserialization completes with self.finished() == Ok.
#[inline]
fn record_ignore(&mut self) {
if self.seq {
if self.seq > 0 {
self.record_next();
} else {
self.record_ignore_all();
@@ -123,12 +123,16 @@ impl<'de> Deserializer<'de> {
#[inline]
fn record_start(&mut self) {
let started = self.pos != 0 || self.rec > 0;
let input_done = self.pos >= self.buf.len();
let output_done = self.rec >= self.seq;
let incomplete = input_done && !output_done;
debug_assert!(
!started || self.buf[self.pos] == Self::SEP,
!started || incomplete || self.buf.get(self.pos) == Some(&Self::SEP),
"Missing expected record separator at current position"
);
self.inc_pos(started.into());
let inc = started && !incomplete;
self.inc_pos(inc.into());
self.inc_rec(1);
}
@@ -179,7 +183,7 @@ impl<'a, 'de: 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
where
V: Visitor<'de>,
{
self.sequence_start();
self.sequence_start(1);
visitor.visit_seq(self)
}
@@ -187,11 +191,11 @@ impl<'a, 'de: 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
unabridged,
tracing::instrument(level = "trace", skip(self, visitor))
)]
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value>
fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.sequence_start();
self.sequence_start(len);
visitor.visit_seq(self)
}
@@ -202,13 +206,13 @@ impl<'a, 'de: 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
fn deserialize_tuple_struct<V>(
self,
_name: &'static str,
_len: usize,
len: usize,
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.sequence_start();
self.sequence_start(len);
visitor.visit_seq(self)
}
@@ -462,7 +466,18 @@ impl<'a, 'de: 'a> de::SeqAccess<'de> for &'a mut Deserializer<'de> {
where
T: DeserializeSeed<'de>,
{
if self.pos >= self.buf.len() {
// Finished parsing the input.
let finished = self.pos >= self.buf.len();
// Completely satisfied the output.
let complete = self.rec >= self.seq;
// Leave after reaching the end of both the input and output. Leaving before
// reaching the end of the input trips the finished() assertion. Leaving before
// reaching the end of the output causes a length expectation panic on type T
// i.e. tuple of size X instead of Y and trailing defaulting elements will not
// be possible.
if finished && complete {
return Ok(None);
}

View File

@@ -240,6 +240,39 @@ fn de_tuple_incomplete() {
assert_eq!(a, user_id, "deserialized user_id does not match");
}
#[test]
fn de_tuple_incomplete_default() {
let user_id: &UserId = "@user:example.com".try_into().unwrap();
let raw: &[u8] = b"@user:example.com";
let (a, b): (&UserId, &str) = de::from_slice(raw).expect("failed to deserialize");
assert_eq!(a, user_id, "deserialized user_id does not match");
assert_eq!(b, "", "deserialized defaulted str does not match");
}
#[test]
#[should_panic(expected = "failed to deserialize")]
fn de_tuple_incomplete_nodefault() {
let user_id: &UserId = "@user:example.com".try_into().unwrap();
let raw: &[u8] = b"@user:example.com";
let (a, _): (&UserId, u64) = de::from_slice(raw).expect("failed to deserialize");
assert_eq!(a, user_id, "deserialized user_id does not match");
}
#[test]
fn de_tuple_incomplete_option() {
let user_id: &UserId = "@user:example.com".try_into().unwrap();
let raw: &[u8] = b"@user:example.com";
let (a, b): (&UserId, Option<&str>) = de::from_slice(raw).expect("failed to deserialize");
assert_eq!(a, user_id, "deserialized user_id does not match");
assert_eq!(b, None, "deserialized defaulted Option does not match");
}
#[test]
#[should_panic(expected = "failed to deserialize")]
fn de_tuple_incomplete_with_sep() {
@@ -480,6 +513,28 @@ fn serde_tuple_option_some_value() {
assert_eq!(cc.1, bb.1);
}
#[test]
fn serde_tuple_option_value_incomplete() {
let room_id: &RoomId = "!room:example.com".try_into().unwrap();
let user_id: &UserId = "@user:example.com".try_into().unwrap();
let mut aa = Vec::<u8>::new();
aa.extend_from_slice(room_id.as_bytes());
aa.push(0xFF);
aa.extend_from_slice(user_id.as_bytes());
let bb: (&RoomId, &UserId) = (room_id, user_id);
let bbs = serialize_to_vec(&bb).expect("failed to serialize tuple");
assert_eq!(aa, bbs);
let cc: (&RoomId, &UserId, Option<u64>) =
de::from_slice(&bbs).expect("failed to deserialize tuple");
assert_eq!(bb.0, cc.0);
assert_eq!(bb.1, cc.1);
assert_eq!(cc.2, None);
}
#[test]
fn serde_tuple_option_some_some() {
let room_id: &RoomId = "!room:example.com".try_into().unwrap();