Skip to content

v0.46.0 — ceilings the overflow reached first

Choose a tag to compare

@tannevaled tannevaled released this 26 Sep 10:33
· 51 commits to main since this release
4ba7c25

Two size ceilings that the overflow reached first

A page that could not be counted was converted anyway — on every architecture

Page converted the extent to an int and judged it afterwards:

w := int(math.Ceil((box[2] - box[0]) * s))
...
if w*h > opt.maxPixels() {

A MediaBox is a number in somebody's file and the scale is the caller's, so
that product is an arbitrary float. Converting a float too large for an int
is not defined in Go
, so the overflow happened before the ceiling could refuse
anything. A page with a MediaBox of 1e300 panicked in fill() — on arm64,
not only on 32-bit. The extent is now judged as a float, before it becomes an
int.

A codestream ceiling that a 32-bit int walked through

affordDecoded refused an oversized codestream by multiplying its sides in an
int. 65535 × 65535 = 4 294 836 225 wraps to −131 071 in an int32, which
is not greater than anything: the guard passed, the decoder was handed the bytes,
and it asked for four gigabytes on a machine that cannot express the allocation.
A 1 KB file crashed a 32-bit build. Its sibling afford had already been written
in int64 and says why in a comment; this one had not.

decodeBase multiplied the same way and is fixed the same way.

A time bound that carried one machine's speed

TestAPageMayBeGivenOnlySoLong allowed a fixed five seconds. The deadline is
looked at once every timeCheckEvery operations, so the overshoot is a quantity
in operations; five seconds is that quantity converted at the speed of the
machine that wrote the test. riscv64, s390x and 386 took 5.4 s, 8.3 s and 11.3 s
for the same correct code.

It now draws the same page twice — once unbounded — and requires the bounded draw
to return in under a third of that. Measured here it returns in a ninth. Comparing
the page against itself makes every fixed cost cancel.

New: the tests run on six architectures

riscv64, loong64, ppc64le, s390x (big-endian), 386 and arm (32-bit
int). The CI previously cross-compiled for the 64-bit four and ran on none of
them. 386 runs natively rather than under qemu-i386, which loses the guest's
floating-point state across the signal Go delivers for asynchronous preemption:
5 failures in 8 identical lanes emulated, 0 in 8 native, 0 in 8 with
asyncpreemptoff=1.

Unchanged output

All 3 215 corpus documents render byte-identical to v0.45.0.