On 2026/03/19 21:45, Ali Raza wrote:
> Last few days ago I found an off-by-one heap buffer overflow in libuv.
> Off-by-one NUL write past a heap buffer in `uv_utf16_to_wtf8()` when called
> from the Windows TTY line-read path. When a user types or pastes CJK
> characters into a Windows console application backed by libuv, a 1-byte
> out-of-bounds NUL write occurs if the read buffer size is divisible by 3.
> 
> I found this while reading through the TTY code. `uv_utf16_to_wtf8()` in
> src/idna.c unconditionally writes a NUL terminator at:
> ```c
> *target++ = '\0';   // idna.c:550 -- writes at target[target_len] when
> buffer is full
> ```
> 
> The function's own comment says `*target_len_ptr` should be the length
> _excluding_ space for NUL. Two callers in util.c handle this correctly:
> ```c
> utf8_len = *size_ptr - 1; /* Reserve space for NUL */    // util.c:126
> *size -= 1; /* Reserve space for NUL. */                   // util.c:1121
> ```
> 
> But the TTY line-read path passes the full buffer size without the
> subtraction:
> ```c
> read_bytes = bytes;    // tty.c:558 — should be bytes - 1
> uv_utf16_to_wtf8(utf16, read_chars,
>                  &handle->tty.rd.read_line_buffer.base,
>                  &read_bytes);
> ```
> 
> The overflow happens when all the input characters encode to exactly 3
> UTF-8 bytes each (BMP characters in U+0800–U+FFFF range, like CJK
> ideographs). The TTY code computes `chars = bytes / 3` (tty.c:540), so when
> `bytes % 3 == 0`, the worst-case output `chars * 3` equals `bytes` exactly,
> and the NUL terminator writes one byte past the buffer.
> 
> The buffer size comes from the application's `alloc_cb`. libuv suggests
> 8192 (not divisible by 3), but any application returning a size that's
> divisible by 3 hits this.
> 
> Introduced in v1.47.0 (commit f3889085, PR #4021), still present on v1.x
> HEAD.

Seems the fix for this was merged last week?

https://github.com/libuv/libuv/commit/ec0ab5d77d32d836a60b024fa43d54ed3ce3ce87

Reply via email to