Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <abwsU8O48wEKqJb2@symphytum.spacehopper.org>
Date: Thu, 19 Mar 2026 17:03:15 +0000
From: Stuart Henderson <stu@...cehopper.org>
To: oss-security@...ts.openwall.com
Subject: Re: Off-by-one heap buffer overflow in libuv

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

Powered by blists - more mailing lists

Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.