PDF import: an XMP packet nested more than 256 deep is skipped instead of overflowing the stack

When a PDF, PDF-compatible .ai or .ait opens, crates/pdf/src/file_info.rs
reads its File Info from the XMP packet with roxmltree, which parses
nested elements recursively and has no limit on their depth. A packet
with elements nested 10,000 deep (70 KB) overflowed a 1 MB stack, and
one nested 100,000 deep (700 KB) overflowed an 8 MB stack. A stack
overflow aborts the app.

File Info now counts how deep the packet's elements nest before roxmltree
parses it, and leaves out a packet nested more than 256 deep, as it
leaves out a packet that is not XML. The document information
dictionary then gives the File Info.
This commit is contained in:
dotCooCoo
2026-10-18 12:15:31 +07:00
committed by bflatastic
parent bdf6059155
commit 830cefcaf6
4 changed files with 80 additions and 4 deletions
+6 -3
View File
@@ -823,8 +823,8 @@ fn the_file_info_sample_reaches_the_reader() {
proptest! {
#![proptest_config(config())]
/// File Info read on open: mutated XMP packets and junk information dictionaries, opened as a
/// PDF and as a `.ai`.
/// File Info read on open: mutated XMP packets, some with elements nested deeply, and junk
/// information dictionaries, opened as a PDF and as a `.ai`.
#[test]
fn pdf_file_info_never_panics(
cut in 0usize..2_000,
@@ -836,8 +836,11 @@ proptest! {
"/Subject <FEFF00> /Author <FEFFD800> /Keywords <EFBBBF> /Title (\\377\\376)",
"/CreationDate (D:00000101000000Z) /ModDate (D:99991231235959+23'59')",
]),
nest in prop::sample::select(vec![0usize, 0, 0, 251, 100_000]),
) {
let pdf = file_info_pdf(info, &mutate_text(RICH_XMP, cut, &edits));
// `nest` elements nested in one another around the packet.
let xmp = format!("{}{}{}", "<a>".repeat(nest), mutate_text(RICH_XMP, cut, &edits), "</a>".repeat(nest));
let pdf = file_info_pdf(info, &xmp);
let b64 = vectorcraft_format::base64_encode(&pdf);
let r = catch_quiet(|| {
let _ = vectorcraft_pdf::file_info(&pdf, None);