mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-01 08:16:43 +00:00
security: bound vendored hayro ASCII85 output by the stream decode limit (#253)
* security: bound ASCII85 output by the stream decode limit (CodeRabbit) * fix(ascii85): truncate terminal partial groups to decode limit
This commit is contained in:
1 parent
5354479ce3
commit
d6b3fb9671
2 files changed
+27
-3
No files matched your search
+26
-2
@@ -4,7 +4,9 @@ use crate::reader::Reader;
|
||||
use crate::trivia::is_white_space_character;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pub(crate) fn decode(data: &[u8]) -> Option<Vec<u8>> {
|
||||
/// PdfCraft patch: `limit` is the most bytes produced (see `filter::decode_limit`); each `z` costs
|
||||
/// one input byte and yields four, so a Flate stream of them would outgrow the budget.
|
||||
pub(crate) fn decode(data: &[u8], limit: usize) -> Option<Vec<u8>> {
|
||||
const POW_85: [u32; 5] = [52200625, 614125, 7225, 85, 1];
|
||||
|
||||
let mut reader = Reader::new(data);
|
||||
@@ -55,13 +57,21 @@ pub(crate) fn decode(data: &[u8]) -> Option<Vec<u8>> {
|
||||
Some(())
|
||||
};
|
||||
|
||||
let mut decoded = Vec::with_capacity(data.len() * 4 / 5);
|
||||
let mut decoded = Vec::with_capacity((data.len() * 4 / 5).min(limit));
|
||||
let mut group = Vec::with_capacity(5);
|
||||
|
||||
loop {
|
||||
if decoded.len() >= limit {
|
||||
decoded.truncate(limit);
|
||||
warn!("ascii85 stream stopped at its decode limit of {limit} bytes");
|
||||
|
||||
return Some(decoded);
|
||||
}
|
||||
|
||||
let Some(b) = read_byte() else {
|
||||
// Be lenient and accept what we have (see PDFBOX-5910).
|
||||
flush_group(&mut group, &mut decoded)?;
|
||||
decoded.truncate(limit);
|
||||
|
||||
return Some(decoded);
|
||||
};
|
||||
@@ -82,6 +92,7 @@ pub(crate) fn decode(data: &[u8]) -> Option<Vec<u8>> {
|
||||
// Technically requires a '>', but there is a PDF where it isn't
|
||||
// appended and decodes fine in other viewers.
|
||||
flush_group(&mut group, &mut decoded)?;
|
||||
decoded.truncate(limit);
|
||||
|
||||
return Some(decoded);
|
||||
}
|
||||
@@ -89,3 +100,16 @@ pub(crate) fn decode(data: &[u8]) -> Option<Vec<u8>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::decode;
|
||||
|
||||
#[test]
|
||||
fn output_stops_at_the_limit() {
|
||||
assert_eq!(decode(&[b'z'; 100], 10).unwrap().len(), 10);
|
||||
assert_eq!(decode(b"zz~>", 100).unwrap(), [0; 8]);
|
||||
assert_eq!(decode(b"!!!!~>", 2).unwrap().len(), 2);
|
||||
assert_eq!(decode(b"!!!!", 2).unwrap().len(), 2);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -95,7 +95,7 @@ impl Filter {
|
||||
Self::AsciiHexDecode => ascii_hex::decode(data)
|
||||
.map(FilterResult::from_data)
|
||||
.ok_or(DecodeFailure::StreamDecode),
|
||||
Self::Ascii85Decode => ascii_85::decode(data)
|
||||
Self::Ascii85Decode => ascii_85::decode(data, limit)
|
||||
.map(FilterResult::from_data)
|
||||
.ok_or(DecodeFailure::StreamDecode),
|
||||
Self::RunLengthDecode => run_length::decode(data, limit)
|
||||
|
||||
Reference in new issue
Block a user