#1153069 wv: heap buffer overflow WRITE in wvMergeCHPXFromBucket

Package:
wv
Source:
wv
Description:
Programs for accessing Microsoft Word documents
Submitter:
zwf zwf
Date:
2026-11-05 07:17:02 UTC
Severity:
normal
Tags:
#1153069#5
Date:
2026-11-05 02:33:06 UTC
From:
To:
Hello,

I am reporting a reproducible heap out-of-bounds write in Debian wv 1.2.9-9 and upstream wvWare.

`wvMergeCHPXFromBucket()` in `chp.c:523/572/593` accumulates the merged grpprl length in `U8 len`, allocates with that wrapped value, then recomputes each sprm length with a wider `i` and copies `i+2` bytes. A crafted Word document makes the copy continue past the allocated heap buffer.

Reproduction:

    wvRTF merge-chpx-write-207.doc

Normal build: `double free or corruption (out)`. ASan: `heap-buffer-overflow WRITE of size 1` at `wvMergeCHPXFromBucket chp.c:593`, through `wvGenerateStyle`, `wvGetSTSH`, and `wvDecodeSimple`. The first illegal write is immediately after a 207-byte allocation; a second sample reaches the same loop after a 147-byte allocation. Since the allocation is total modulo 256, the 207-byte sample proves at least 463 bytes were written, or at least 256 bytes past the allocation. The copied bytes come from document sprm nodes.

The current Debian 1.2.9-9 source still has `U8 len` at chp.c:523 and allocates with it at chp.c:572. The attached report, two PoCs, and source snapshot provide the complete trace.

This is distinct from CVE-2026-112056, which is a heap out-of-bounds read in `wvGetSTSH` itself; this report concerns a write in `wvMergeCHPXFromBucket`. It is also distinct from Debian Bug #1148725. Confirmed impact is heap memory corruption and process termination; code execution is not claimed.

Please assign a CVE if Debian considers this a vulnerability in the Debian package. I am happy to retest a patched build.

Thanks,
netwind
zwfnetwind@hotmail.com

#1153069#10
Date:
2026-11-05 04:29:33 UTC
From:
To:
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.

#1153069#15
Date:
2026-11-05 07:14:46 UTC
From:
To:
Reported upstream as well, with the same sample(s) and patch attached: https://github.com/opendocument-app/wvWare/issues/2