From 6cc77734346660954c7f798bc8bf126da8cc9389 Mon Sep 17 00:00:00 2001 From: tannevaled Date: Thu, 27 Aug 2026 14:43:33 +0200 Subject: [PATCH] deps: stop shipping two denial-of-service defects to our callers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This module required reader v0.4.0 and pdffont v0.2.0. Both had already been fixed and released; nothing had brought the fixes here, so a caller who asked only for this package got the defects — minimum version selection gives them the version this go.mod names, and a package's own tests never see its callers' module graph. Merging is not shipping. reader v0.4.1 a 219-byte file with no trailer and no startxref, whose last object is numbered 2 147 483 647, took 21.2 s to open and allocated not one byte — so no memory limit caught it. Now under a millisecond. pdffont v0.3.0 a /ToUnicode map's size had nothing to do with its input: 10 655 bytes produced 13 107 200 entries, 1 054 MB and 10 s, per font per page. Now 52 ms and 31 MB. Also the glyph names of eight more languages, which had been read as nothing at all — "Příliš" came back "Píliš" and still looked like a word. deps_test.go guards the first of those with the file itself rather than with a version string, so a later go.mod edit cannot quietly reintroduce it. It is built in the test, not committed, so nobody else's PDF enters the repository. Against reader v0.4.0 it fails after five seconds. WHAT IT CHANGES ON THE CORPUS Two binaries, identical but for pdffont, verified with `go version -m` because this measurement has silently compared a version against itself twice on this project. 8 935 real files — 2 268 forms and 6 667 arXiv papers — first page each, 20-second budget: identical 8 929 differing 6 Of the six, three are pages sitting at the budget: with a MaxDuration set, a page near the limit stops at a different operation each run, so its output is timing-dependent and nothing can be concluded from it. One is safedocs' Inline_Image_Abbreviations fixture, which disagrees with itself between runs of the same binary for an unrelated reason (go-pdfkit/reader#14). That leaves two genuine changes, both in the vendor half of the forms corpus: gh-pdfbox/source.pdf +142 inked pixels gh-pdfbox/sample_fonts_solidconvertor -58 inked pixels, all inside one 152x212 region, 1 003 pixels changed The second is the interesting one and it is not fully explained: the file has no /Differences and no uniXXXX names anywhere, decompressed streams included, so the difference cannot come from a document-supplied glyph name. It arrives through the base-encoding table, where the added letters include the ones WinAnsiEncoding already names — scaron, zcaron, oe. Fewer inked pixels is consistent with a real narrow glyph replacing a wider wrong one, but that is inference and not measurement, so it is written down as unexplained rather than as an improvement. No file got worse in a way that can be shown. Wall clock is not compared: four other jobs had the machine while this ran, and a number taken under that load would be fiction. Co-Authored-By: Claude Opus 5 --- deps_test.go | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 4 ++-- go.sum | 10 ++++------ 3 files changed, 55 insertions(+), 8 deletions(-) create mode 100644 deps_test.go diff --git a/deps_test.go b/deps_test.go new file mode 100644 index 0000000..493d1d2 --- /dev/null +++ b/deps_test.go @@ -0,0 +1,49 @@ +package render + +import ( + "testing" + "time" + + "github.com/go-pdfkit/reader" +) + +// TestAFileWithAnAbsurdObjectNumberOpensAtOnce guards the dependency rather +// than this package's own code. +// +// reader v0.4.1 stopped answering "which objects call themselves a catalogue?" +// by counting from zero to the largest object number a file names. A file of +// 219 bytes with no trailer and no startxref can only be read by repairing it, +// and if the last object it declares is numbered 2 147 483 647 that is two +// thousand million map lookups for four objects: 21.2 seconds, and not one +// byte allocated, which is why no memory limit anywhere caught it. +// +// Requiring v0.4.0 here meant that a caller who asked only for this package +// got the defect: minimum version selection would give them the version this +// go.mod names. Nothing in the test suite noticed, because a package's own +// tests never see its callers' module graph. +// +// The file is built rather than committed, so this brings nobody else's PDF +// into the repository. +func TestAFileWithAnAbsurdObjectNumberOpensAtOnce(t *testing.T) { + file := []byte("%PDF-1.4\n" + + "1 0 obj<>endobj\n" + + "2 0 obj<>endobj\n" + + "3 0 obj<>endobj\n" + + "2147483647 0 obj<>endobj\n") + done := make(chan error, 1) + go func() { + _, err := reader.Open(file) + done <- err + }() + select { + case err := <-done: + if err != nil { + t.Fatalf("the file did not open: %v", err) + } + case <-time.After(5 * time.Second): + // Against reader v0.4.0 this takes twenty-one seconds. Five is far + // more than the fixed version needs and far less than the defect. + t.Fatal("219 bytes took more than five seconds to open: the reader " + + "this module requires still walks to the largest object number") + } +} diff --git a/go.mod b/go.mod index b89fbc8..bfc683a 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/go-gfx/gfx v0.10.0 github.com/go-opentype/fonts v0.9.0 github.com/go-opentype/opentype v0.10.0 - github.com/go-pdfkit/reader v0.4.0 + github.com/go-pdfkit/reader v0.4.1 ) -require github.com/go-pdfkit/pdffont v0.2.0 +require github.com/go-pdfkit/pdffont v0.3.0 diff --git a/go.sum b/go.sum index 8235c92..c500cb1 100644 --- a/go.sum +++ b/go.sum @@ -2,11 +2,9 @@ github.com/go-gfx/gfx v0.10.0 h1:3AqOO8TZph6/U8+ejJxYkCZ+wzddxAbZ7fxi6TvGop4= github.com/go-gfx/gfx v0.10.0/go.mod h1:bFt/MWyYWRU3Ic9IaB8XOC9KLMMHRRmahMk4FaIGK7g= github.com/go-opentype/fonts v0.9.0 h1:slB6OB3riLyUPrOxqXe0s6/AzdenF1TDvCN8N87hhQk= github.com/go-opentype/fonts v0.9.0/go.mod h1:C6yQL2apHItfEZ5hztpsHF0S5mlX/hklLlq/Z5fRG/g= -github.com/go-opentype/opentype v0.9.0 h1:GFgcJ3nwTDp4NJr5O+Paw7lhZx5Jv/R+noZwvhYDlkM= -github.com/go-opentype/opentype v0.9.0/go.mod h1:AOixevJf7XQaH7+WG+OMIOZEbYPXfMqklVk26Y6YTUU= github.com/go-opentype/opentype v0.10.0 h1:cZVMZ3RVkcijXmxlmpqyVkjlNc33aSB2ugKVWYvoLUg= github.com/go-opentype/opentype v0.10.0/go.mod h1:AOixevJf7XQaH7+WG+OMIOZEbYPXfMqklVk26Y6YTUU= -github.com/go-pdfkit/pdffont v0.2.0 h1:yAp/oR5Z2kkqs4r0GWMalZMC7rc7XSCZXgwIypbpMWM= -github.com/go-pdfkit/pdffont v0.2.0/go.mod h1:y4vo5DgT95e57C3XxIWfA/xss+x6RwZwyj6KWdJc86s= -github.com/go-pdfkit/reader v0.4.0 h1:qPbNZSO+Xl+4NBvQoV1PYt7HlqHaEAQ1tUc6/IL8JAU= -github.com/go-pdfkit/reader v0.4.0/go.mod h1:fQFOVfCMUui1AdvD4qhimdyvvNr9KvvJ1S7IuKZjyV8= +github.com/go-pdfkit/pdffont v0.3.0 h1:G5DKcAmsZJ0e17QhSrcUaL7PKEXKjUx4P/iGMl7bAbI= +github.com/go-pdfkit/pdffont v0.3.0/go.mod h1:bfmNLna1l1CljNX/Utg55YzFylovfmI7sJnvgA3bzKI= +github.com/go-pdfkit/reader v0.4.1 h1:pRxFqRjsn7H/VsGfWb9nYWyFuDgTU2Pjmoq/f5mgVq4= +github.com/go-pdfkit/reader v0.4.1/go.mod h1:fQFOVfCMUui1AdvD4qhimdyvvNr9KvvJ1S7IuKZjyV8=