From: Jeff Hostetler <jeffhost@xxxxxxxxxxxxx> Calls to `chdir()` are dangerous in a multi-threaded context. If `unix_stream_listen()` is given a socket pathname that is too big to fit in a `sockaddr_un` structure, it will `chdir()` to the parent directory of the requested socket pathname, create the socket using a relative pathname, and then `chdir()` back. This is not thread-safe. Add `disallow_chdir` flag to `struct unix_sockaddr_context` and change all callers to pass an initialized context structure. Teach `unix_sockaddr_init()` to not allow calls to `chdir()` when flag is set. Extend the public interface to `unix_stream_listen_gently()` to also expose this new flag. Signed-off-by: Jeff Hostetler <jeffhost@xxxxxxxxxxxxx> --- unix-socket.c | 21 +++++++++++++++++---- unix-socket.h | 1 + 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/unix-socket.c b/unix-socket.c index 3a9ffc32268..f66987261e6 100644 --- a/unix-socket.c +++ b/unix-socket.c @@ -19,8 +19,15 @@ static int chdir_len(const char *orig, int len) struct unix_sockaddr_context { char *orig_dir; + unsigned int disallow_chdir:1; }; +#define UNIX_SOCKADDR_CONTEXT_INIT \ +{ \ + .orig_dir=NULL, \ + .disallow_chdir=0, \ +} + static void unix_sockaddr_cleanup(struct unix_sockaddr_context *ctx) { if (!ctx->orig_dir) @@ -40,7 +47,11 @@ static int unix_sockaddr_init(struct sockaddr_un *sa, const char *path, { int size = strlen(path) + 1; - ctx->orig_dir = NULL; + if (ctx->disallow_chdir && size > sizeof(sa->sun_path)) { + errno = ENAMETOOLONG; + return -1; + } + if (size > sizeof(sa->sun_path)) { const char *slash = find_last_dir_sep(path); const char *dir; @@ -75,7 +86,7 @@ int unix_stream_connect(const char *path) { int fd, saved_errno; struct sockaddr_un sa; - struct unix_sockaddr_context ctx; + struct unix_sockaddr_context ctx = UNIX_SOCKADDR_CONTEXT_INIT; if (unix_sockaddr_init(&sa, path, &ctx) < 0) return -1; @@ -97,7 +108,7 @@ int unix_stream_listen(const char *path) { int fd, saved_errno; struct sockaddr_un sa; - struct unix_sockaddr_context ctx; + struct unix_sockaddr_context ctx = UNIX_SOCKADDR_CONTEXT_INIT; unlink(path); @@ -129,7 +140,9 @@ int unix_stream_listen_gently(const char *path, int bind_successful = 0; int saved_errno; struct sockaddr_un sa; - struct unix_sockaddr_context ctx; + struct unix_sockaddr_context ctx = UNIX_SOCKADDR_CONTEXT_INIT; + + ctx.disallow_chdir = opts->disallow_chdir; if (unix_sockaddr_init(&sa, path, &ctx) < 0) goto fail; diff --git a/unix-socket.h b/unix-socket.h index 253f579f087..08d3d822111 100644 --- a/unix-socket.h +++ b/unix-socket.h @@ -7,6 +7,7 @@ int unix_stream_listen(const char *path); struct unix_stream_listen_opts { int listen_backlog_size; unsigned int force_unlink_before_bind:1; + unsigned int disallow_chdir:1; }; int unix_stream_listen_gently(const char *path, -- gitgitgadget