From f8343313b13cfd0f087b7b9878340d7cd156765f Mon Sep 17 00:00:00 2001 From: Ali Raza Date: Sat, 7 Mar 2026 03:08:15 +0500 Subject: [PATCH 1/6] unix: add overflow check for stdio_count in uv_spawn() Add a bounds check for stdio_count before the allocation in uv_spawn() on Unix. Reject negative values and values large enough to overflow the stdio_count * sizeof(*pipes) multiplication. Uses SIZE_MAX to compute the upper limit portably across 32-bit and 64-bit platforms. --- src/unix/process.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/unix/process.c b/src/unix/process.c index 539e7d941..51103ca22 100644 --- a/src/unix/process.c +++ b/src/unix/process.c @@ -993,6 +993,10 @@ int uv_spawn(uv_loop_t* loop, process->status = 0; stdio_count = options->stdio_count; + if (stdio_count < 0 || (size_t) stdio_count > SIZE_MAX / sizeof(*pipes)) { + err = UV_EINVAL; + goto error; + } if (stdio_count < 3) stdio_count = 3; From ac460747972e5c943217f6f8d4a18459d5e49372 Mon Sep 17 00:00:00 2001 From: Ali Raza Date: Sat, 7 Mar 2026 03:08:34 +0500 Subject: [PATCH 2/6] win: add overflow check in make_program_args() The command-line buffer size computation at dst_len * 2 + arg_count * 2 can overflow size_t on 32-bit systems when the total argument length is large. Add a check before the arithmetic to reject inputs that would cause the multiplication or addition to wrap. --- src/win/process.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/win/process.c b/src/win/process.c index 45e85ed96..696a54ce3 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -546,6 +546,10 @@ int make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr) { /* Adjust for potential quotes. Also assume the worst-case scenario that * every character needs escaping, so we need twice as much space. */ + if (dst_len > (SIZE_MAX / sizeof(WCHAR) - arg_count * 2) / 2) { + err = UV_EINVAL; + goto error; + } dst_len = dst_len * 2 + arg_count * 2; /* Allocate buffer for the final command line. */ From 64fbbe388827d729187954f3a0e4a0760ac851ba Mon Sep 17 00:00:00 2001 From: Ali Raza Date: Sat, 7 Mar 2026 03:08:57 +0500 Subject: [PATCH 3/6] win: add overflow checks in make_program_env() The env_len accumulator in make_program_env() has no overflow protection. On 32-bit systems, a large environment block can cause env_len * sizeof(WCHAR) to wrap size_t, resulting in undersized allocations followed by heap buffer overflows during the copy passes. Add overflow checks before both allocation sites (second pass and final pass) to reject environment blocks that would cause arithmetic overflow. --- src/win/process.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/win/process.c b/src/win/process.c index 696a54ce3..369652c34 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -675,6 +675,8 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { /* second pass: copy to UTF-16 environment block */ len = env_block_count * sizeof(WCHAR*); + if (env_len > (SIZE_MAX - len) / sizeof(WCHAR)) + return UV_EINVAL; p = uv__malloc(len + env_len * sizeof(WCHAR)); if (p == NULL) { return UV_ENOMEM; @@ -728,6 +730,10 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { } /* final pass: copy, in sort order, and inserting required variables */ + if (env_len > SIZE_MAX / sizeof(WCHAR) - 1) { + uv__free(p); + return UV_EINVAL; + } dst = uv__malloc((1+env_len) * sizeof(WCHAR)); if (!dst) { uv__free(p); From 36364c3d748077fe055e19c85aca055c4bcf2905 Mon Sep 17 00:00:00 2001 From: Ali Raza Date: Sat, 7 Mar 2026 03:09:20 +0500 Subject: [PATCH 4/6] win: fix TOCTOU race and int truncation in make_program_env() GetEnvironmentVariableW is called twice: first to measure the value size, then to fetch it. If the variable grows between these two calls, the second call writes past the buffer before the size mismatch check at the TODO comment can fire, causing heap corruption. Fix by: - Capping the remaining buffer size to INT_MAX before the cast to int, preventing negative values from implicit signed-to-unsigned conversion - Checking the return value of GetEnvironmentVariableW against the actual remaining buffer size instead of the stale measurement - Gracefully skipping the variable if it was deleted or grew, instead of calling uv_fatal_error This resolves the TODO at the existing 'handle race condition?' comment. --- src/win/process.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/win/process.c b/src/win/process.c index 369652c34..f762f26e9 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -757,13 +757,21 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { /* missing required var */ len = required_vars_value_len[i]; if (len) { + size_t remaining; wcscpy(ptr, required_vars[i].wide_eq); ptr += required_vars[i].len; + remaining = env_len - (ptr - dst); + if (remaining > (size_t) INT_MAX) + remaining = (size_t) INT_MAX; var_size = GetEnvironmentVariableW(required_vars[i].wide, ptr, - (int) (env_len - (ptr - dst))); - if (var_size != (DWORD) (len - 1)) { /* TODO: handle race condition? */ - uv_fatal_error(GetLastError(), "GetEnvironmentVariableW"); + (int) remaining); + if (var_size == 0 || var_size >= remaining) { + /* Env var was deleted or grew since we measured it; skip it. */ + ptr -= required_vars[i].len; + len = 0; + } else { + len = var_size + 1; } } i++; From 2ad3e071c33718e84a3a5549dd074c54d842d9fc Mon Sep 17 00:00:00 2001 From: Ali Raza Date: Sat, 7 Mar 2026 03:09:38 +0500 Subject: [PATCH 5/6] win: add overflow check in search_path_join_test() The allocation size for the joined path is computed as a sum of multiple lengths multiplied by sizeof(WCHAR). On 32-bit systems, if path components are long enough, the sum or the multiplication can wrap size_t, producing a tiny allocation. The subsequent wcsncpy calls then write past the buffer end. Add overflow checks for both the addition and the multiplication before allocating. --- src/win/process.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/win/process.c b/src/win/process.c index f762f26e9..571bbc583 100644 --- a/src/win/process.c +++ b/src/win/process.c @@ -186,8 +186,15 @@ static WCHAR* search_path_join_test(const WCHAR* dir, } /* Allocate buffer for output */ - result = result_pos = (WCHAR*)uv__malloc(sizeof(WCHAR) * - (cwd_len + 1 + dir_len + 1 + name_len + 1 + ext_len + 1)); + { + size_t alloc_len = cwd_len + 1 + dir_len + 1 + name_len + 1 + ext_len + 1; + if (alloc_len > SIZE_MAX / sizeof(WCHAR) || + alloc_len < cwd_len /* overflow in the addition */) { + SetLastError(ERROR_OUTOFMEMORY); + return NULL; + } + result = result_pos = (WCHAR*)uv__malloc(sizeof(WCHAR) * alloc_len); + } /* Copy cwd */ wcsncpy(result_pos, cwd, cwd_len); From 0be2a8f9c8ac80ce9eeac9ccf831d5ad1d4b6209 Mon Sep 17 00:00:00 2001 From: Ali Raza Date: Sat, 7 Mar 2026 03:43:59 +0500 Subject: [PATCH 6/6] win: replace hardcoded stdio count cap with overflow check The existing check rejected stdio_count > 255 with an arbitrary limit. Replace it with a SIZE_MAX-based overflow guard matching the approach used on Unix, so the allocation in CHILD_STDIO_SIZE(count) cannot wrap. --- src/win/process-stdio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/win/process-stdio.c b/src/win/process-stdio.c index 181db92ea..ea635c170 100644 --- a/src/win/process-stdio.c +++ b/src/win/process-stdio.c @@ -175,8 +175,9 @@ int uv__stdio_create(uv_loop_t* loop, count = options->stdio_count; - if (count < 0 || count > 255) { - /* Only support FDs 0-255 */ + if (count < 0 || + (size_t) count > (SIZE_MAX - sizeof(int)) / + (sizeof(unsigned char) + sizeof(uintptr_t))) { return ERROR_NOT_SUPPORTED; } else if (count < 3) { /* There should always be at least 3 stdio handles. */