mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-07 12:13:20 +00:00
fix(hayro-syntax): read full /W[0] xref entry type width (#241)
* fix(hayro-syntax): read full /W[0] width for xref stream entry type (follow-up to #239) * hayro-syntax: loader-path test for oversized /W[0]; document patch; drop committed build output (CodeRabbit)
This commit is contained in:
@@ -5,3 +5,4 @@ plans/
|
||||
.DS_Store
|
||||
/.claude/
|
||||
.idea/
|
||||
/vendor/*/target
|
||||
|
||||
Vendored
+2
@@ -54,6 +54,8 @@ Hostile-input bounds (CodeRabbit findings):
|
||||
ignored, so globals cannot recurse through further globals.
|
||||
- At most 4096 graphics states, holding at most 64 MiB together, are saved; a `q` past
|
||||
either limit is counted but not copied, and its `Q` still removes the clips added since.
|
||||
- An xref-stream `/W` type field wider than one byte is read at its full width (big-endian)
|
||||
instead of only its first byte, and `/W[0]` over 8 bytes is refused like `/W[1]`.
|
||||
A dash array over 256 entries strokes solid.
|
||||
|
||||
This remains the patched 0.7 backend. It retains the existing Bradford Lab conversion,
|
||||
|
||||
+39
@@ -168,6 +168,7 @@ dependencies = [
|
||||
"rustc-hash",
|
||||
"sha2",
|
||||
"smallvec",
|
||||
"stringprep",
|
||||
"zune-jpeg",
|
||||
]
|
||||
|
||||
@@ -244,12 +245,50 @@ version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "stringprep"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
"unicode-properties",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75cd3fb03d5288b3ba9a1ee103ac7e9cbb59f0b03d64f8ddfcc04dd42758a85d"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-properties"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
|
||||
Vendored
+1
-1
@@ -338,7 +338,7 @@
|
||||
"path": "src/xref.rs",
|
||||
"source_sha256": "51ffeeb7d4ac035bae2cd5ba9288c180088ae06cdcefe2b29dfeda560a793aa0",
|
||||
"source_git_blob": "20e33a410489d42e438a3f2b87d70c7bb8d66eae",
|
||||
"sha256": "d5463a6d629e31a33c1d5b9a735a6e87353752dc0b4156dd84247dcf9b485804"
|
||||
"sha256": "411ce7e3c0d385b1735f945b92dba164825456ef3b70ef464279ba0bac0879d4"
|
||||
},
|
||||
{
|
||||
"path": "src/decode_limits_tests.rs",
|
||||
|
||||
Vendored
+44
-9
@@ -887,17 +887,13 @@ fn populate_from_xref_stream<'a>(
|
||||
|
||||
let [f1_len, f2_len, f3_len] = stream.dict().get::<[u8; 3]>(W)?;
|
||||
|
||||
if f2_len > size_of::<u64>() as u8 {
|
||||
error!("xref offset length is larger than the allowed limit");
|
||||
// PdfCraft patch: the type field may be wider than one byte (upstream behaviour); bound it.
|
||||
if f1_len > size_of::<u64>() as u8 || f2_len > size_of::<u64>() as u8 {
|
||||
error!("xref field length is larger than the allowed limit");
|
||||
|
||||
return None;
|
||||
}
|
||||
|
||||
// Do such files exist?
|
||||
if f1_len != 1 {
|
||||
warn!("first field in xref stream was longer than 1");
|
||||
}
|
||||
|
||||
let xref_data = stream.decoded().ok()?;
|
||||
let mut xref_reader = Reader::new(xref_data.as_ref());
|
||||
|
||||
@@ -980,11 +976,12 @@ fn xref_stream_subsection<'a>(
|
||||
}
|
||||
|
||||
for i in 0..num_elements {
|
||||
// A zero-width type field defaults to 1 (PDF 32000-1, 7.5.8.2).
|
||||
let f_type = if f1_len == 0 {
|
||||
1
|
||||
} else {
|
||||
// We assume a length of 1.
|
||||
xref_reader.read_bytes(1)?[0]
|
||||
let bytes = xref_reader.read_bytes(f1_len as usize)?;
|
||||
bytes.iter().fold(0u64, |acc, b| (acc << 8) | *b as u64)
|
||||
};
|
||||
|
||||
let obj_number = start + i;
|
||||
@@ -1276,6 +1273,44 @@ mod tests {
|
||||
out
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn xref_stream_type_field_uses_the_full_w0_width() {
|
||||
// /W [2 1 1]: two-byte type 0x0001 (normal), offset 7, gen 0.
|
||||
let mut map = XrefMap::default();
|
||||
let mut reader = Reader::new(&[0u8, 1, 7, 0]);
|
||||
assert!(xref_stream_subsection(&mut reader, 5, 1, 2, 1, 1, &mut map).is_some());
|
||||
assert!(matches!(
|
||||
map.get(&ObjectIdentifier::new(5, 0)),
|
||||
Some(EntryType::Normal(7))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn xref_stream_loader_rejects_oversized_w0() {
|
||||
// Same one-entry stream; only /W[0] differs, so the 9 must be what is refused.
|
||||
let load = |w: &str| {
|
||||
let data = format!(
|
||||
"1 0 obj\n<< /Type /XRef /Size 6 /Index [5 1] /W [{w}] /Length 4 >>\nstream\n"
|
||||
)
|
||||
.into_bytes()
|
||||
.into_iter()
|
||||
.chain([0u8, 1, 7, 0])
|
||||
.chain(*b"\nendstream\nendobj\n")
|
||||
.collect::<Vec<u8>>();
|
||||
let mut map = XrefMap::default();
|
||||
let got = populate_from_xref_stream(
|
||||
&data,
|
||||
&mut Reader::new(&data),
|
||||
&mut map,
|
||||
&mut BTreeSet::new(),
|
||||
DecodeLimits::default(),
|
||||
);
|
||||
got.is_some()
|
||||
};
|
||||
assert!(load("2 1 1"));
|
||||
assert!(!load("9 1 1"));
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[test]
|
||||
fn concurrent_cold_object_stream_reads_all_succeed() {
|
||||
|
||||
Reference in New Issue
Block a user