From 92f8467653ae531d79953272a41aa66dab6bedf1 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 7 Apr 2025 09:34:55 +0200 Subject: [PATCH] unix: remove TOCTOU issues from uv_pipe_chmod Use fchmod() on platforms that support it on UNIX sockets. Only fall back to chmod() on platforms that don't (macOS and the BSDs.) Remove the stat + chmod dance from the fallback and just call chmod directly, because that's another source of TOCTOU issues. Fixes: https://github.com/libuv/libuv/issues/2040 --- src/unix/pipe.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/unix/pipe.c b/src/unix/pipe.c index 9512c20ca..e4bab0e9f 100644 --- a/src/unix/pipe.c +++ b/src/unix/pipe.c @@ -447,13 +447,17 @@ uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) { int uv_pipe_chmod(uv_pipe_t* handle, int mode) { - unsigned desired_mode; - struct stat pipe_stat; char name_buffer[1 + UV__PATH_MAX]; + int desired_mode; size_t name_len; + int fd; int r; - if (handle == NULL || uv__stream_fd(handle) == -1) + if (handle == NULL) + return UV_EBADF; + + fd = uv__stream_fd(handle); + if (fd == -1) return UV_EBADF; if (mode != UV_READABLE && @@ -461,31 +465,27 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) { mode != (UV_WRITABLE | UV_READABLE)) return UV_EINVAL; - /* Unfortunately fchmod does not work on all platforms, we will use chmod. */ - name_len = sizeof(name_buffer); - r = uv_pipe_getsockname(handle, name_buffer, &name_len); - if (r != 0) - return r; - - /* stat must be used as fstat has a bug on Darwin */ - if (uv__stat(name_buffer, &pipe_stat) == -1) - return UV__ERR(errno); - desired_mode = 0; if (mode & UV_READABLE) desired_mode |= S_IRUSR | S_IRGRP | S_IROTH; if (mode & UV_WRITABLE) desired_mode |= S_IWUSR | S_IWGRP | S_IWOTH; - /* Exit early if pipe already has desired mode. */ - if ((pipe_stat.st_mode & desired_mode) == desired_mode) - return 0; + /* fchmod on macOS and (Free|Net|Open)BSD does not support UNIX sockets. */ + if (fchmod(fd, desired_mode)) + if (errno != EINVAL) + return UV__ERR(errno); - pipe_stat.st_mode |= desired_mode; + /* Fall back to chmod. */ + name_len = sizeof(name_buffer); + r = uv_pipe_getsockname(handle, name_buffer, &name_len); + if (r != 0) + return r; - r = chmod(name_buffer, pipe_stat.st_mode); + if (chmod(name_buffer, desired_mode)) + return UV__ERR(errno); - return r != -1 ? 0 : UV__ERR(errno); + return 0; }