Skip to content

Walk the objects a file has, not every number up to the largest of them - #12

Merged
tannevaled merged 1 commit into
mainfrom
sparse-xref
Aug 26, 2026
Merged

Walk the objects a file has, not every number up to the largest of them#12
tannevaled merged 1 commit into
mainfrom
sparse-xref

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Repairing a damaged file means listing its objects in order, and objectsOfType did that by counting from nought to the highest object number and asking the cross-reference table about each one.

A file may name any object number it likes. mozilla/pdf.js's bug1980958.pdf is 219 bytes and declares object 2147483647 beside its three real ones. Counting up to that is two thousand million map lookups, done twice, for three objects.

before 219 bytes in, Open took 25.39s
after 219 bytes in, Open took 0s

No allocation either way — it was a loop, not a memory bomb, which is why nothing caught it.

The numbers that exist are collected, sorted and walked, which is what the order was wanted for in the first place.

Two tests: the 219-byte file itself, with a five-second deadline so it fails loudly rather than hanging the suite; and that the listing is still in object-number order when the numbers are scattered rather than consecutive.

Found by battle-testing go-pdfkit/forms against 1 046 files — the 69 public forms plus mozilla/pdf.js's whole test corpus. 1 029 opened, 14 580 fields read and filled, no panics, and this one file took 12.87 of the run's 13.1 seconds.

100% statement coverage, go vet clean.

Repairing a damaged file means listing its objects in order, and that was done
by counting from nought to the highest object number and asking the table
about each. A file may name any object number it likes.

One of 219 bytes in the wild — mozilla/pdf.js's bug1980958.pdf — declares
object 2147483647 beside its three real ones. Counting up to that is two
thousand million map lookups, done twice, for three objects: twenty-five
seconds of somebody's afternoon for a file that fits in a tweet, and a denial
of service anybody could post.

The numbers that exist are sorted and walked instead, which is what the order
was wanted for. Twenty-five seconds becomes none.

Found by battle-testing the forms library against 1 046 files: 1 029 opened,
14 580 fields read and filled, no panics, and one file that took 12.87 of the
run's 13.1 seconds.
@tannevaled
tannevaled merged commit 6e9bbcf into main Aug 26, 2026
1 check passed
@tannevaled
tannevaled deleted the sparse-xref branch August 26, 2026 13:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant