texttotext: guard page_size against PageWidth/PageHeight overflow - #248
tillkamppeter merged 2 commits into
Conversation
cfFilterTextToText() takes "PageWidth" and "PageHeight" straight from the
job options with only a check that they are positive. Nothing caps how
large they can be, so a job that asks for an absurdly wide or tall page
(no special privilege needed, this is a plain job option) sails through
into the output-page-buffer size calculation:
page_size = ((num_columns + 2) * num_lines + 2) * 4;
which is computed in a plain int. For a page reported as roughly a
billion columns by one line, that multiplication wraps around instead of
producing something calloc() would reject, so the allocation ends up
only a handful of bytes. The formatting loop right after it still writes
according to the real, unwrapped column count, so it walks straight past
the end of the buffer.
Reject page dimensions that would overflow that computation and fall
back to the existing default page size (80x66) the same way the function
already falls back when PageWidth/PageHeight are non-positive, using
64-bit arithmetic to check the product before it is ever narrowed to int.
Added cupsfilters/test-texttotext-page-overflow.c/.sh, following the same
shape as the existing test-pdftoraster-copy-height regression test: the
harness #includes the real texttotext.c so it drives the actual
cfFilterTextToText() entry point under AddressSanitizer. Without the fix
it aborts with a heap-buffer-overflow on the very first character
written to the page; with it, the function falls back cleanly and
returns success.
|
Generally, your fix looks OK for me, but:
|
….txt
Replace the standalone ASan-only regression test with a case in the
project's own test-filter-cases.txt harness. It was failing CI on
x86_64/arm64 because its link line hardcoded -liconv, which doesn't
exist as a separate library on glibc (iconv is part of libc there);
armv7/riscv64 only "passed" because they skip before reaching that
link step, since ASan can't run under their QEMU emulation.
Wires "texttotext" into testfilters.c's filter_mappings so a
FilterChain entry can drive cfFilterTextToText() directly, then adds
a case with PageWidth=1073741824/PageHeight=1 to trigger the same
overflow described in the bug report through that existing mechanism.
Verified locally (both x86_64 and arm64, via the real autotools build
and testfilters harness): reverting the texttotext.c fix makes
cfFilterChain report the texttotext step crashing on signal 6 for
this case ("free(): invalid next size"); with the fix, it falls back
to the default page size and the case passes.
|
Switched to the test-filter-cases.txt approach and dropped the standalone test program along with its Makefile.am wiring. Wired On what was actually failing: the x86_64/arm64 legs were dying because the old standalone test's link line hardcoded Verified locally on both x86_64 and arm64 with the real autotools build: with the source fix reverted, cfFilterChain reports the texttotext step crashing on signal 6 for this case (glibc catches the corruption, "free(): invalid next size"), and with the fix in place it falls back to the default page size and the case passes cleanly. |
What's going on
cfFilterTextToText()readsPageWidthandPageHeightstraight from thejob options and only checks that they're positive:
There's no upper bound, so a job can ask for a page that's a billion
columns wide. That value feeds directly into the output page buffer size a
few lines later:
page_sizeis a plainint. For a page reported asPageWidth=1073741824 PageHeight=1, the multiplication wraps around instead of producingsomething
calloc()would balk at, soout_pageends up 16 bytes insteadof the ~4 GB the arithmetic actually meant. The formatting loop right after
still writes according to the real, unwrapped
num_columns, so it walksstraight off the end of that 16-byte buffer while filling in the first
line of text.
I ran this through the real
cfFilterTextToText()entry point (not are-implementation) built with AddressSanitizer + UBSan and confirmed it:
This looks like the same underlying issue reported in #198, just triggered
through the more direct
PageWidth/PageHeightoptions path rather thanthrough the media-size/chars-per-inch calculation.
Fix
Reject page dimensions whose product would overflow the
page_sizecomputation, and fall back to the existing default (80 columns x 66
lines), the same way the function already falls back when
PageWidthorPageHeightcome back non-positive. The check is done in 64-bit beforenum_columns/num_linesever feed theintmultiplication, so it can'titself be fooled by the same kind of wraparound.
Testing
Added
cupsfilters/test-texttotext-page-overflow.cand its.shdriver,modeled directly on the existing
test-pdftoraster-copy-heightregressiontest: the harness
#includes the realtexttotext.cso it's driving theactual
cfFilterTextToText()code, built under AddressSanitizer.heap-buffer-overflow above and aborts.
cfFilterTextToText()logs the fallback andreturns success with no ASan report.
Wired the new script into
check_SCRIPTS/TESTSinMakefile.amnext tothe other ASan-based regression tests, and added the
.charness toEXTRA_DISTso it ships inmake distlike the pdftoraster one does. Thescript skips (exit 77) when AddressSanitizer isn't usable in the build
environment, matching the existing tests' convention.