From cdf202c3f3dc5f46642fb1b926722d08c06fe290 Mon Sep 17 00:00:00 2001 From: Mike Dilger Date: Thu, 15 Feb 2024 15:18:37 +1300 Subject: [PATCH] fix: Output buffer length checking --- src/types/event/json_event.rs | 6 ++- src/types/filter/json_filter.rs | 72 ++++++++++++++++++++------------- src/types/parse/json_parse.rs | 44 +++++++++++++++----- src/types/parse/mod.rs | 10 +++++ 4 files changed, 95 insertions(+), 37 deletions(-) diff --git a/src/types/event/json_event.rs b/src/types/event/json_event.rs index 0ca56e0..4cbcf1d 100644 --- a/src/types/event/json_event.rs +++ b/src/types/event/json_event.rs @@ -5,11 +5,15 @@ use crate::types::parse::json_parse::*; /// Returns the count of consumed bytes and output bytes pub fn parse_json_event(input: &[u8], output: &mut [u8]) -> Result<(usize, usize), Error> { // Minimum-sized JSON event is 204 characters long - // NOTE: 152 is the minimum binary event if input.len() < 204 { return Err(ChorusError::JsonBadEvent("Too Short", 0).into()); } + // NOTE: 152 is the minimum binary event + if output.len() < 152 { + return Err(ChorusError::BufferTooSmall.into()); + } + // This tracks where we are currently looking in the input as we scan forward. // It is short for INput POSition. let mut inpos = 0; diff --git a/src/types/filter/json_filter.rs b/src/types/filter/json_filter.rs index e167af9..843ca1e 100644 --- a/src/types/filter/json_filter.rs +++ b/src/types/filter/json_filter.rs @@ -1,6 +1,7 @@ use crate::error::{ChorusError, Error}; use crate::types::parse::json_escape::json_unescape; use crate::types::parse::json_parse::*; +use crate::types::parse::put; /// Parses a JSON filter from the `input` buffer. Places the parsed filter into the `output` buffer. /// Returns the count of consumed bytes and output bytes @@ -35,16 +36,20 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz }; // Start structure with that of an empty filter - output[0..32].copy_from_slice(&[ - 0, 0, // length (we will fill it in later) - 0, 0, // 0 ids - 0, 0, // 0 authors - 0, 0, // 0 kinds - 255, 255, 255, 255, // max limit - 0, 0, 0, 0, // padding - 0, 0, 0, 0, 0, 0, 0, 0, // since 1970 - 255, 255, 255, 255, 255, 255, 255, 255, // until max unixtime - ]); + put( + output, + 0, + &[ + 0, 0, // length (we will fill it in later) + 0, 0, // 0 ids + 0, 0, // 0 authors + 0, 0, // 0 kinds + 255, 255, 255, 255, // max limit + 0, 0, 0, 0, // padding + 0, 0, 0, 0, 0, 0, 0, 0, // since 1970 + 255, 255, 255, 255, 255, 255, 255, 255, // until max unixtime + ], + )?; let mut end: usize = 32; @@ -143,7 +148,7 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz eat_colon_with_whitespace(input, &mut inpos)?; let since = read_u64(input, &mut inpos)?; - output[16..24].copy_from_slice(since.to_ne_bytes().as_slice()); + put(output, 16, since.to_ne_bytes().as_slice())?; found |= HAVE_SINCE; } else if inpos + 6 <= input.len() && &input[inpos..inpos + 6] == b"until\"" { @@ -155,7 +160,7 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz eat_colon_with_whitespace(input, &mut inpos)?; let until = read_u64(input, &mut inpos)?; - output[24..32].copy_from_slice(until.to_ne_bytes().as_slice()); + put(output, 24, until.to_ne_bytes().as_slice())?; found |= HAVE_UNTIL; } else if inpos + 6 <= input.len() && &input[inpos..inpos + 6] == b"limit\"" { @@ -168,7 +173,7 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz eat_colon_with_whitespace(input, &mut inpos)?; let limit = read_u64(input, &mut inpos)?; let limit: u32 = limit as u32; - output[8..12].copy_from_slice(limit.to_ne_bytes().as_slice()); + put(output, 8, limit.to_ne_bytes().as_slice())?; found |= HAVE_LIMIT; } else if inpos + 3 <= input.len() @@ -218,7 +223,7 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz } // Write num_ids - output[2..4].copy_from_slice(num_ids.to_ne_bytes().as_slice()); + put(output, 2, num_ids.to_ne_bytes().as_slice())?; } // Copy authors @@ -236,7 +241,7 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz } // write num_authors - output[4..6].copy_from_slice(num_authors.to_ne_bytes().as_slice()); + put(output, 4, num_authors.to_ne_bytes().as_slice())?; } // Copy kinds @@ -254,28 +259,35 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz ChorusError::JsonBadFilter("Filter has kind number too large", inpos).into(), ); } - output[end..end + 2].copy_from_slice((u as u16).to_ne_bytes().as_slice()); + put(output, end, (u as u16).to_ne_bytes().as_slice())?; num_kinds += 1; end += 2; } // write num_kinds - output[6..8].copy_from_slice(num_kinds.to_ne_bytes().as_slice()); + put(output, 6, num_kinds.to_ne_bytes().as_slice())?; } // Copy tags { let write_tags_start = end; // write number of tags - output[write_tags_start + 2..write_tags_start + 4] - .copy_from_slice((num_tag_fields as u16).to_ne_bytes().as_slice()); + put( + output, + write_tags_start + 2, + (num_tag_fields as u16).to_ne_bytes().as_slice(), + )?; // bump end past offset fields end += 4 + 2 * num_tag_fields; // Now pull in each tag + #[allow(clippy::needless_range_loop)] for w in 0..num_tag_fields { // Write it's offset - output[write_tags_start + 4 + (2 * w)..write_tags_start + 4 + (2 * w) + 2] - .copy_from_slice(((end - write_tags_start) as u16).to_ne_bytes().as_slice()); + put( + output, + write_tags_start + 4 + (2 * w), + ((end - write_tags_start) as u16).to_ne_bytes().as_slice(), + )?; let mut inpos = start_tags[w]; let letter = input[inpos]; @@ -283,7 +295,10 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz // bump past count output and write letter let countindex = end; end += 2; - output[end..end + 2].copy_from_slice(1_u16.to_ne_bytes().as_slice()); + put(output, end, 1_u16.to_ne_bytes().as_slice())?; + if output.len() < end + 2 { + return Err(crate::error::ChorusError::BufferTooSmall.into()); + } output[end + 2] = letter; // bump past what we just wrote @@ -305,18 +320,21 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz // copy data let (inlen, outlen) = json_unescape(&input[inpos..], &mut output[end + 2..])?; // write len - output[end..end + 2].copy_from_slice((outlen as u16).to_ne_bytes().as_slice()); + put(output, end, (outlen as u16).to_ne_bytes().as_slice())?; end += 2 + outlen; inpos += inlen + 1; count += 1; } // write count - output[countindex..countindex + 2].copy_from_slice(count.to_ne_bytes().as_slice()); + put(output, countindex, count.to_ne_bytes().as_slice())?; } // write length of tags section - output[write_tags_start..write_tags_start + 2] - .copy_from_slice(((end - write_tags_start) as u16).to_ne_bytes().as_slice()); + put( + output, + write_tags_start, + ((end - write_tags_start) as u16).to_ne_bytes().as_slice(), + )?; } if end > 65535 { @@ -324,7 +342,7 @@ pub fn parse_json_filter(input: &[u8], output: &mut [u8]) -> Result<(usize, usiz } // Write length of filter - output[..2].copy_from_slice((end as u16).to_ne_bytes().as_slice()); + put(output, 0, (end as u16).to_ne_bytes().as_slice())?; Ok((inpos, end)) } diff --git a/src/types/parse/json_parse.rs b/src/types/parse/json_parse.rs index d83e91c..54fd13b 100644 --- a/src/types/parse/json_parse.rs +++ b/src/types/parse/json_parse.rs @@ -1,4 +1,5 @@ use super::json_escape::json_unescape; +use super::put; use crate::error::{ChorusError, Error}; #[inline] @@ -52,6 +53,9 @@ pub fn next_object_field(input: &[u8], inposp: &mut usize) -> Result Result<(), Error> { + if output.len() < 32 { + return Err(ChorusError::BufferTooSmall.into()); + } verify_char(input, b'"', inposp)?; if *inposp + 64 >= input.len() { return Err(ChorusError::JsonBad("Too short reading id", *inposp).into()); @@ -64,6 +68,9 @@ pub fn read_id(input: &[u8], inposp: &mut usize, output: &mut [u8]) -> Result<() } pub fn read_pubkey(input: &[u8], inposp: &mut usize, output: &mut [u8]) -> Result<(), Error> { + if output.len() < 32 { + return Err(ChorusError::BufferTooSmall.into()); + } verify_char(input, b'"', inposp)?; if *inposp + 64 >= input.len() { return Err(ChorusError::JsonBad("Too short reading pubkey", *inposp).into()); @@ -126,14 +133,18 @@ pub fn read_tags_array( verify_char(input, b'[', inposp)?; // outer array open brace eat_whitespace(input, inposp); + if output.len() < 4 { + return Err(ChorusError::BufferTooSmall.into()); + } + // NOTE: we cannot write any tag strings until after we have counted the tags. // (our tags structure is optimized for reading, not writing) let num_tags: usize = count_tags(input, *inposp)?; - output[2..4].copy_from_slice((num_tags as u16).to_ne_bytes().as_slice()); + put(output, 2, (num_tags as u16).to_ne_bytes().as_slice())?; // Case where we have no tags if num_tags == 0 { - output[0..2].copy_from_slice(4_u16.to_ne_bytes().as_slice()); + put(output, 0, 4_u16.to_ne_bytes().as_slice())?; return Ok(4); } @@ -142,11 +153,18 @@ pub fn read_tags_array( let mut tag_num = 0; let mut outpos: usize = 4 + num_tags * 2; + if output.len() < outpos { + return Err(ChorusError::BufferTooSmall.into()); + } + loop { // Write the offset of this tag let offset_slot = 4 + tag_num * 2; - output[offset_slot..offset_slot + 2] - .copy_from_slice((outpos as u16).to_ne_bytes().as_slice()); + put( + output, + offset_slot, + (outpos as u16).to_ne_bytes().as_slice(), + )?; // Read the tag (bumps inpos and outpos) read_tag(input, inposp, output, &mut outpos)?; @@ -176,7 +194,7 @@ pub fn read_tags_array( } // Write length of tags section - output[0..2].copy_from_slice((outpos as u16).to_ne_bytes().as_slice()); + put(output, 0, (outpos as u16).to_ne_bytes().as_slice())?; Ok(outpos) } @@ -228,7 +246,7 @@ pub fn read_tag( // read string let (inlen, outlen) = json_unescape(&input[*inposp..], &mut output[*outposp + 2..])?; // write the length before it - output[*outposp..*outposp + 2].copy_from_slice((outlen as u16).to_ne_bytes().as_slice()); + put(output, *outposp, (outlen as u16).to_ne_bytes().as_slice())?; // bump the outposp past it *outposp += 2 + outlen; // bump the inpos past the string (and the ending quote which isn't counted in the len) @@ -252,7 +270,11 @@ pub fn read_tag( } // Write the count of strings at the very start - output[countpos..countpos + 2].copy_from_slice((num_strings as u16).to_ne_bytes().as_slice()); + put( + output, + countpos, + (num_strings as u16).to_ne_bytes().as_slice(), + )?; Ok(()) } @@ -270,16 +292,20 @@ pub fn read_content( *inposp += inlen + 1; // +1 to pass the end quote // Write content length - output[after_tags..after_tags + 4].copy_from_slice((outlen as u32).to_ne_bytes().as_slice()); + put(output, after_tags, (outlen as u32).to_ne_bytes().as_slice())?; // Write event size let event_len = after_tags + 4 + outlen; - output[0..4].copy_from_slice((event_len as u32).to_ne_bytes().as_slice()); + put(output, 0, (event_len as u32).to_ne_bytes().as_slice())?; Ok(()) } +// FIXME this is too event-offset specific pub fn read_sig(input: &[u8], inposp: &mut usize, output: &mut [u8]) -> Result<(), Error> { + if output.len() < 144 { + return Err(ChorusError::BufferTooSmall.into()); + } verify_char(input, b'"', inposp)?; if *inposp + 128 >= input.len() { return Err(ChorusError::JsonBad("Too short reading sig", *inposp).into()); diff --git a/src/types/parse/mod.rs b/src/types/parse/mod.rs index d159abd..ffb500c 100644 --- a/src/types/parse/mod.rs +++ b/src/types/parse/mod.rs @@ -3,3 +3,13 @@ pub mod json_escape; pub mod json_parse; pub mod utf8; + +#[inline] +pub fn put(output: &mut [u8], offset: usize, data: &[u8]) -> Result<(), crate::error::Error> { + if output.len() < offset + data.len() { + Err(crate::error::ChorusError::BufferTooSmall.into()) + } else { + output[offset..offset + data.len()].copy_from_slice(data); + Ok(()) + } +}