Patch for the heap out-of-bounds write described in #1153069. `wvMergeCHPXFromBucket()` accumulates the merged grpprl length in a `U8`, so it wraps at 256, while the copy loop recomputes each sprm length into a wider `U16 i` and keeps copying. The patch keeps the accumulator in `size_t`, allocates exactly that total, and bounds the copy with a remaining-bytes counter. `dest->cbGrpprl` is still a `U8` in the struct ABI, so the stored length is clamped to 0xff.
Tested on a plain/optimised build of 1.2.9 with the patch applied: - merge-chpx-oob.doc: SIGSEGV before, rc=0 after - merge-chpx-write-207.doc: "double free or corruption (out)" before, rc=0 after - pchtabs-inbounds.doc and patched-pchtabs.doc: SEGV before, rc=0 after - simple-bkd-len4.doc from #1153067: also rc=0, since this tree carries that fix too - wv-corpus/backslash.doc: RTF output byte-identical before and after (1325 bytes) Patch attached. It does not touch the Word 6 variant `wvMergeCHPXFromBucket6()`, which still keeps a `U16` accumulator and did not overflow in these samples; keeping it out makes the change minimal. Happy to retest or rework the approach. Patch for the heap out-of-bounds write described in #1153069. `wvMergeCHPXFromBucket()` accumulates the merged grpprl length in a `U8`, so it wraps at 256, while the copy loop recomputes each sprm length into a wider `U16 i` and keeps copying. The patch keeps the accumulator in `size_t`, allocates exactly that total, and bounds the copy with a remaining-bytes counter. `dest->cbGrpprl` is still a `U8` in the struct ABI, so the stored length is clamped to 0xff. Tested on a plain/optimised build of 1.2.9 with the patch applied: - merge-chpx-oob.doc: SIGSEGV before, rc=0 after - merge-chpx-write-207.doc: "double free or corruption (out)" before, rc=0 after - pchtabs-inbounds.doc and patched-pchtabs.doc: SEGV before, rc=0 after - simple-bkd-len4.doc from #1153067: also rc=0, since this tree carries that fix too - wv-corpus/backslash.doc: RTF output byte-identical before and after (1325 bytes) Patch attached. It does not touch the Word 6 variant `wvMergeCHPXFromBucket6()`, which still keeps a `U16` accumulator and did not overflow in these samples; keeping it out makes the change minimal. Happy to retest or rework the approach. Patch for the heap out-of-bounds write described in #1153069. `wvMergeCHPXFromBucket()` accumulates the merged grpprl length in a `U8`, so it wraps at 256, while the copy loop recomputes each sprm length into a wider `U16 i` and keeps copying. The patch keeps the accumulator in `size_t`, allocates exactly that total, and bounds the copy with a remaining-bytes counter. `dest->cbGrpprl` is still a `U8` in the struct ABI, so the stored length is clamped to 0xff. Tested on a plain/optimised build of 1.2.9 with the patch applied: Patch for the heap out-of-bounds write described in #1153069. `wvMergeCHPXFromBucket()` accumulates the merged grpprl length in a `U8`, so it wraps at 256, while the copy loop recomputes each sprm length into a wider `U16 i` and keeps copying. The patch keeps the accumulator in `size_t`, allocates exactly that total, and bounds the copy with a remaining-bytes counter. `dest->cbGrpprl` is still a `U8` in the struct ABI, so the stored length is clamped to 0xff. Tested on a plain/optimised build of 1.2.9 with the patch applied: - merge-chpx-oob.doc: SIGSEGV before, rc=0 after - merge-chpx-write-207.doc: "double free or corruption (out)" before, rc=0 after - pchtabs-inbounds.doc and patched-pchtabs.doc: SEGV before, rc=0 after - simple-bkd-len4.doc from #1153067: also rc=0, since this tree carries that fix too - wv-corpus/backslash.doc: RTF output byte-identical before and after (1325 bytes) Patch attached. It does not touch the Word 6 variant `wvMergeCHPXFromBucket6()`, which still keeps a `U16` accumulator and did not overflow in these samples; keeping it out makes the change minimal. Happy to retest or rework the approach. - merge-chpx-oob.doc: SIGSEGV before, rc=0 after - merge-chpx-write-207.doc: "double free or corruption (out)" before, rc=0 after - pchtabs-inbounds.doc and patched-pchtabs.doc: SEGV before, rc=0 after - simple-bkd-len4.doc from #1153067: also rc=0, since this tree carries that fix too - wv-corpus/backslash.doc: RTF output byte-identical before and after (1325 bytes) Patch attached. It does not touch the Word 6 variant `wvMergeCHPXFromBucket6()`, which still keeps a `U16` accumulator and did not overflow in these samples; keeping it out makes the change minimal. Happy to retest or rework the approach.
fix-chp-1153069.patch
Description: fix-chp-1153069.patch
# Fix verification: BTS #1153069 and #1153070 Tree: `~/fuzz/wv-fix2` (copy of `wv-fixed`, which already carries the #1153067 fix) Patch: `wv-oob-fixes.patch` (chp.c + sprm.c, 95 lines) SHA-256 of patch: `f3463237bf925da799cc00c3c6e49186fd0b3150b7737101955ad27dbc83abfa` ## Change summary ### chp.c (`wvMergeCHPXFromBucket`) â #1153069 heap OOB write - accumulator `U8 len` â `size_t total` so the merged length no longer wraps at 256 - allocation uses `total`, copy loop bounded by a `left` counter that starts at `total` - stored `dest->cbGrpprl` (still `U8` by ABI) clamped to `0xff` ### sprm.c (`wvApplysprmPChgTabsPapx`, `wvApplysprmPChgTabs`) â #1153070 stack OOB write - both merge loops get `&& (k < itbdMax)` so writes stop at the 64-entry arrays ## Verification (patched build, plain/optimised) | input | before (wv-clean) | after (wv-fix2) | |---|---|---| | `merge-chpx-oob.doc` | SEGV 139 | rc=0 | | `merge-chpx-write-207.doc` | `double free or corruption`, abort | rc=0 | | `patched-pchtabs.doc` | `*** stack smashing detected ***` / SEGV | rc=0 | | `pchtabs-inbounds.doc` | SEGV 139 | rc=0 | | `simple-bkd-len4.doc` (#1153067) | `free(): invalid pointer`, 134 | rc=0 | Functional check: `wv-corpus/backslash.doc` produces byte-identical RTF before and after (1325 bytes, `cmp` clean), so the bound changes do not alter normal output. ## Note `wvMergeCHPXFromBucket6()` (the Word 6 variant in the same file) still keeps a `U16 len`; it did not overflow in any of the samples here and is left untouched to keep the patch minimal.

