From: Jeff Hostetler <redacted>
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.
Signed-off-by: Jeff Hostetler <redacted>
---
unix-socket.c | 19 ++++++++++++++++---
unix-socket.h | 2 ++
2 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/unix-socket.c b/unix-socket.c
index 8bcef18ea55..9726992f276 100644
--- a/unix-socket.c
+++ b/unix-socket.c
@@ -11,8 +11,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)@@ -32,7 +39,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;@@ -67,7 +78,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;@@ -96,7 +107,9 @@ int unix_stream_listen(const char *path,
int bind_successful = 0;
int backlog;
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)
return -1;
diff --git a/unix-socket.h b/unix-socket.h
index c28372ef48e..5b0e8ccef10 100644
--- a/unix-socket.h
+++ b/unix-socket.h
@@ -4,12 +4,14 @@
struct unix_stream_listen_opts {
int listen_backlog_size;
unsigned int force_unlink_before_bind:1;
+ unsigned int disallow_chdir:1;
};
#define UNIX_STREAM_LISTEN_OPTS_INIT \
{ \
.listen_backlog_size = 5, \
.force_unlink_before_bind = 1, \
+ .disallow_chdir = 0, \
}
int unix_stream_connect(const char *path);--
gitgitgadget