From 656cd5fa4e6f39ed71c3f0b5bcaec51f63806ab2 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 8 Apr 2022 12:49:11 +0200 Subject: [PATCH 1/2] unix: prep work for long unix path support The BSDs let you pass path names longer than sizeof(s.sun_path), they read past the end when socklen > sizeof(struct sockaddr_un) up to a limit of at least 255 characters. This commit refactors src/unix/pipe.c to make it easy to support long paths. The actual support will be done in a follow-up commit. Refs: https://github.com/libuv/libuv/issues/2826 --- src/unix/pipe.c | 57 +++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/src/unix/pipe.c b/src/unix/pipe.c index f373c86e..5d91c987 100644 --- a/src/unix/pipe.c +++ b/src/unix/pipe.c @@ -29,6 +29,23 @@ #include #include +struct uv__sockaddr_un_path { + char pad[offsetof(struct sockaddr_un, sun_path)]; + char path[sizeof(((struct sockaddr_un*)0)->sun_path)]; +}; + +union uv__sockaddr_un { + struct sockaddr sa; + struct uv__sockaddr_un_path up; +}; + + +static void uv__prep_sockaddr_un(union uv__sockaddr_un* s, const char* path) { + memset(s, 0, sizeof(*s)); + s->sa.sa_family = AF_UNIX; + uv__strscpy(s->up.path, path, sizeof(s->up.path)); +} + int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) { uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE); @@ -41,7 +58,7 @@ int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) { int uv_pipe_bind(uv_pipe_t* handle, const char* name) { - struct sockaddr_un saddr; + union uv__sockaddr_un saddr; const char* pipe_fname; int sockfd; int err; @@ -65,11 +82,9 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { goto err_socket; sockfd = err; - memset(&saddr, 0, sizeof saddr); - uv__strscpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path)); - saddr.sun_family = AF_UNIX; + uv__prep_sockaddr_un(&saddr, pipe_fname); - if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr)) { + if (bind(sockfd, &saddr.sa, sizeof(saddr))) { err = UV__ERR(errno); /* Convert ENOENT to EACCES for compatibility with Windows. */ if (err == UV_ENOENT) @@ -174,7 +189,7 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb) { - struct sockaddr_un saddr; + union uv__sockaddr_un saddr; int new_sock; int err; int r; @@ -188,14 +203,10 @@ void uv_pipe_connect(uv_connect_t* req, handle->io_watcher.fd = err; } - memset(&saddr, 0, sizeof saddr); - uv__strscpy(saddr.sun_path, name, sizeof(saddr.sun_path)); - saddr.sun_family = AF_UNIX; + uv__prep_sockaddr_un(&saddr, name); - do { - r = connect(uv__stream_fd(handle), - (struct sockaddr*)&saddr, sizeof saddr); - } + do + r = connect(uv__stream_fd(handle), &saddr.sa, sizeof(saddr)); while (r == -1 && errno == EINTR); if (r == -1 && errno != EINPROGRESS) { @@ -241,28 +252,28 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle, uv__peersockfunc func, char* buffer, size_t* size) { - struct sockaddr_un sa; - socklen_t addrlen; + union uv__sockaddr_un saddr; + int addrlen; int err; - addrlen = sizeof(sa); - memset(&sa, 0, addrlen); + addrlen = sizeof(saddr); + memset(&saddr, 0, sizeof(saddr)); err = uv__getsockpeername((const uv_handle_t*) handle, func, - (struct sockaddr*) &sa, - (int*) &addrlen); + &saddr.sa, + &addrlen); if (err < 0) { *size = 0; return err; } #if defined(__linux__) - if (sa.sun_path[0] == 0) + if (saddr.up.path[0] == 0) /* Linux abstract namespace */ - addrlen -= offsetof(struct sockaddr_un, sun_path); + addrlen -= offsetof(struct uv__sockaddr_un_path, path); else #endif - addrlen = strlen(sa.sun_path); + addrlen = strlen(saddr.up.path); if ((size_t)addrlen >= *size) { @@ -270,7 +281,7 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle, return UV_ENOBUFS; } - memcpy(buffer, sa.sun_path, addrlen); + memcpy(buffer, saddr.up.path, addrlen); *size = addrlen; /* only null-terminate if it's not an abstract socket */ From c59b8e22e7f56034f5556bbccfc676980e3bf66e Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Fri, 8 Apr 2022 12:49:11 +0200 Subject: [PATCH 2/2] unix: support long unix paths on macos + bsds See the previous commit. This commit allows paths up to 253 bytes instead of the current restriction of 104 bytes. Fixes: https://github.com/libuv/libuv/issues/2826 --- src/unix/pipe.c | 28 ++++++++++++++++--- test/test-list.h | 2 ++ test/test-pipe-getsockname.c | 52 ++++++++++++++++++++++++++++++++---- 3 files changed, 74 insertions(+), 8 deletions(-) diff --git a/src/unix/pipe.c b/src/unix/pipe.c index 5d91c987..27c89044 100644 --- a/src/unix/pipe.c +++ b/src/unix/pipe.c @@ -31,7 +31,15 @@ struct uv__sockaddr_un_path { char pad[offsetof(struct sockaddr_un, sun_path)]; +#ifdef SOCK_MAXADDRLEN + /* macOS and the BSDs support paths > sizeof(su.sun_path). + * SOCK_MAXADDRLEN is, to the best of my knowledge, always 255. + * Minus the two byte header, that leaves 253 bytes for the path. + */ + char path[SOCK_MAXADDRLEN - offsetof(struct sockaddr_un, sun_path)]; +#else char path[sizeof(((struct sockaddr_un*)0)->sun_path)]; +#endif }; union uv__sockaddr_un { @@ -41,9 +49,18 @@ union uv__sockaddr_un { static void uv__prep_sockaddr_un(union uv__sockaddr_un* s, const char* path) { + size_t len; + + len = 1 + strlen(path); + if (len > sizeof(s->up.path)) + len = sizeof(s->up.path); + memset(s, 0, sizeof(*s)); +#ifdef SOCK_MAXADDRLEN + s->sa.sa_len = len; +#endif s->sa.sa_family = AF_UNIX; - uv__strscpy(s->up.path, path, sizeof(s->up.path)); + memcpy(s->up.path, path, len); } @@ -255,6 +272,7 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle, union uv__sockaddr_un saddr; int addrlen; int err; + char* nul; addrlen = sizeof(saddr); memset(&saddr, 0, sizeof(saddr)); @@ -273,8 +291,12 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle, addrlen -= offsetof(struct uv__sockaddr_un_path, path); else #endif - addrlen = strlen(saddr.up.path); - + { + nul = memchr(saddr.up.path, '\0', sizeof(saddr.up.path)); + addrlen = (int) sizeof(saddr.up.path); + if (nul != NULL) + addrlen = nul - saddr.up.path; + } if ((size_t)addrlen >= *size) { *size = addrlen + 1; diff --git a/test/test-list.h b/test/test-list.h index 833047e8..0d8f22bc 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -199,6 +199,7 @@ TEST_DECLARE (pipe_connect_on_prepare) TEST_DECLARE (pipe_getsockname) TEST_DECLARE (pipe_getsockname_abstract) TEST_DECLARE (pipe_getsockname_blocking) +TEST_DECLARE (pipe_getsockname_long_path) TEST_DECLARE (pipe_pending_instances) TEST_DECLARE (pipe_sendmsg) TEST_DECLARE (pipe_server_close) @@ -780,6 +781,7 @@ TASK_LIST_START TEST_ENTRY (pipe_getsockname) TEST_ENTRY (pipe_getsockname_abstract) TEST_ENTRY (pipe_getsockname_blocking) + TEST_ENTRY (pipe_getsockname_long_path) TEST_ENTRY (pipe_pending_instances) TEST_ENTRY (pipe_sendmsg) diff --git a/test/test-pipe-getsockname.c b/test/test-pipe-getsockname.c index 79db8eba..5f06d469 100644 --- a/test/test-pipe-getsockname.c +++ b/test/test-pipe-getsockname.c @@ -25,12 +25,9 @@ #include #include -#if defined(__linux__) - #include - #include -#endif - #ifndef _WIN32 +# include /* SOCK_MAXADDRLEN */ +# include # include /* close */ #else # include @@ -268,3 +265,48 @@ TEST_IMPL(pipe_getsockname_blocking) { MAKE_VALGRIND_HAPPY(); return 0; } + + +static void long_path_connect_cb(uv_connect_t* req, int status) { + ASSERT_EQ(status, 0); + uv_close((uv_handle_t*) req->handle, NULL); +} + + +/* macOS and the BSDs support paths > sizeof(su.sun_path). */ +TEST_IMPL(pipe_getsockname_long_path) { +#ifdef SOCK_MAXADDRLEN + char path[SOCK_MAXADDRLEN - offsetof(struct sockaddr_un, sun_path) + 1]; + char name[SOCK_MAXADDRLEN - offsetof(struct sockaddr_un, sun_path) + 1]; + uv_pipe_t server; + uv_pipe_t client; + uv_connect_t req; + size_t len; + + memset(path, 'x', sizeof(path) - 1); + path[sizeof(path) - 1] = '\0'; + unlink(path); + + ASSERT_EQ(0, uv_pipe_init(uv_default_loop(), &server, 0)); + ASSERT_EQ(0, uv_pipe_init(uv_default_loop(), &client, 0)); + ASSERT_EQ(0, uv_pipe_bind(&server, path)); + ASSERT_EQ(0, uv_listen((uv_stream_t*) &server, 0, (uv_connection_cb) abort)); + uv_pipe_connect(&req, &client, path, long_path_connect_cb); + + len = sizeof(name); + ASSERT_EQ(0, uv_pipe_getsockname(&server, name, &len)); + + ASSERT_EQ(len, sizeof(path) - 1); + ASSERT_MEM_EQ(path, name, len); + + uv_close((uv_handle_t*) &server, NULL); + ASSERT_EQ(0, uv_run(uv_default_loop(), UV_RUN_DEFAULT)); + + unlink(path); + MAKE_VALGRIND_HAPPY(); + return 0; +#else + (void) &long_path_connect_cb; + RETURN_SKIP("no long unix path support on this platform"); +#endif +}