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.

Attachment: 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.

Reply via email to