fix: Output buffer length checking

This commit is contained in:
Mike Dilger 2024-02-15 15:18:37 +13:00
parent c157b9e03c
commit cdf202c3f3
4 changed files with 95 additions and 37 deletions

View File

@ -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;

View File

@ -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))
}

View File

@ -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<bool, Error
}
pub fn read_id(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 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());

View File

@ -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(())
}
}