Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: indefinite strings can't contain indefinite strings #114

Merged
merged 1 commit into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 47 additions & 9 deletions ciborium-ll/src/seg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,14 +158,20 @@ impl<'r, R: Read, P: Parser> Segment<'r, R, P> {
}
}

#[derive(Eq, PartialEq)]
enum State {
Initial,
Continue,
Finished,
}

/// A sequence of CBOR segments
///
/// CBOR allows for bytes or text items to be segmented. This type represents
/// the state of that segmented input stream.
pub struct Segments<'r, R: Read, P: Parser> {
reader: &'r mut Decoder<R>,
finish: bool,
nested: usize,
state: State,
parser: PhantomData<P>,
unwrap: fn(Header) -> Result<Option<usize>, ()>,
}
Expand All @@ -178,8 +184,7 @@ impl<'r, R: Read, P: Parser> Segments<'r, R, P> {
) -> Self {
Self {
reader: decoder,
finish: false,
nested: 0,
state: State::Initial,
parser: PhantomData,
unwrap,
}
Expand All @@ -190,16 +195,26 @@ impl<'r, R: Read, P: Parser> Segments<'r, R, P> {
/// Returns `Ok(None)` at the conclusion of the stream.
#[inline]
pub fn pull(&mut self) -> Result<Option<Segment<R, P>>, Error<R::Error>> {
while !self.finish {
while self.state != State::Finished {
let offset = self.reader.offset();
match self.reader.pull()? {
Header::Break if self.nested == 1 => return Ok(None),
Header::Break if self.nested > 1 => self.nested -= 1,
Header::Break => {
self.state = State::Finished;
return Ok(None);
}
header => match (self.unwrap)(header) {
Err(..) => return Err(Error::Syntax(offset)),
Ok(None) => self.nested += 1,
Ok(None) => {
if self.state == State::Initial {
self.state = State::Continue;
} else {
return Err(Error::Syntax(offset));
}
}
Ok(Some(len)) => {
self.finish = self.nested == 0;
if self.state == State::Initial {
self.state = State::Finished;
}
return Ok(Some(Segment {
reader: self.reader,
unread: len,
Expand All @@ -214,3 +229,26 @@ impl<'r, R: Read, P: Parser> Segments<'r, R, P> {
Ok(None)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn segments() {
fn t(data: &[u8], len: usize) {
let mut dec = Decoder::from(data);
let mut segs = Segments::<_, Bytes>::new(&mut dec, |header| match header {
Header::Bytes(len) => Ok(len),
_ => Err(()),
});
while let Some(mut seg) = segs.pull().unwrap() {
let mut b = [0; 1];
assert_eq!(Some(&b"0"[..]), seg.pull(&mut b).unwrap());
}
assert_eq!(len, dec.offset());
}
t(b"\x410\x00", 2);
t(b"\x5f\x410\xff\x00", 4);
}
}
4 changes: 2 additions & 2 deletions ciborium/tests/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ fn eof() -> Error<std::io::Error> {
case("7f4100ff", Error::Syntax(1)),

// Indefinite-length string chunks not definite length:
//case("5f5f4100ffff", Error::Syntax(0)), These should fail, but do not currently.
//case("7f7f6100ffff", Error::Syntax(0)),
case("5f5f4100ffff", Error::Syntax(1)),
case("7f7f6100ffff", Error::Syntax(1)),

// Break occurring on its own outside of an indefinite-length item:
case("ff", Error::Semantic(None, "invalid type: break, expected non-break".into())),
Expand Down
4 changes: 2 additions & 2 deletions ciborium/tests/recursion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ fn map() {
fn bytes() {
let bytes = [0x5f; 128 * 1024];
match from_reader::<Value, _>(&bytes[..]).unwrap_err() {
Error::Io(..) => (),
Error::Syntax(..) => (),
e => panic!("incorrect error: {:?}", e),
}
}
Expand All @@ -42,7 +42,7 @@ fn bytes() {
fn text() {
let bytes = [0x7f; 128 * 1024];
match from_reader::<Value, _>(&bytes[..]).unwrap_err() {
Error::Io(..) => (),
Error::Syntax(..) => (),
e => panic!("incorrect error: {:?}", e),
}
}
Expand Down
Loading