unix: support long path names in pipe.c

This commit is contained in:
Edy Silva 2026-03-08 22:31:04 -03:00
parent a19ceeb13a
commit fa697fc675
3 changed files with 82 additions and 19 deletions

View File

@ -29,6 +29,18 @@
#include <unistd.h>
#include <stdlib.h>
union uv__sockaddr_un {
struct sockaddr sa;
struct uv__sockaddr_un_path {
char pad[offsetof(struct sockaddr_un, sun_path)];
#ifdef SOCK_MAXADDRLEN
char path[SOCK_MAXADDRLEN - offsetof(struct sockaddr_un, sun_path)];
#else
char path[sizeof(((struct sockaddr_un*)0)->sun_path)];
#endif
} up;
};
/* Does the file path contain embedded nul bytes? */
static int includes_invalid_nul(const char *s, size_t n) {
@ -64,7 +76,7 @@ int uv_pipe_bind2(uv_pipe_t* handle,
const char* name,
size_t namelen,
unsigned int flags) {
struct sockaddr_un saddr;
union uv__sockaddr_un saddr;
char* pipe_fname;
int sockfd;
int err;
@ -90,12 +102,12 @@ int uv_pipe_bind2(uv_pipe_t* handle,
return UV_EINVAL;
if (flags & UV_PIPE_NO_TRUNCATE)
if (namelen > sizeof(saddr.sun_path))
if (namelen > sizeof(saddr.up.path))
return UV_EINVAL;
/* Truncate long paths. Documented behavior. */
if (namelen > sizeof(saddr.sun_path))
namelen = sizeof(saddr.sun_path);
if (namelen > sizeof(saddr.up.path))
namelen = sizeof(saddr.up.path);
/* Already bound? */
if (uv__stream_fd(handle) >= 0)
@ -125,10 +137,10 @@ int uv_pipe_bind2(uv_pipe_t* handle,
sockfd = err;
memset(&saddr, 0, sizeof saddr);
memcpy(&saddr.sun_path, name, namelen);
saddr.sun_family = AF_UNIX;
memcpy(saddr.up.path, name, namelen);
saddr.sa.sa_family = AF_UNIX;
if (bind(sockfd, (struct sockaddr*)&saddr, addrlen)) {
if (bind(sockfd, &saddr.sa, addrlen)) {
err = UV__ERR(errno);
/* Convert ENOENT to EACCES for compatibility with Windows. */
if (err == UV_ENOENT)
@ -257,7 +269,7 @@ int uv_pipe_connect2(uv_connect_t* req,
size_t namelen,
unsigned int flags,
uv_connect_cb cb) {
struct sockaddr_un saddr;
union uv__sockaddr_un saddr;
int new_sock;
int err;
int r;
@ -276,12 +288,12 @@ int uv_pipe_connect2(uv_connect_t* req,
return UV_EINVAL;
if (flags & UV_PIPE_NO_TRUNCATE)
if (namelen > sizeof(saddr.sun_path))
if (namelen > sizeof(saddr.up.path))
return UV_EINVAL;
/* Truncate long paths. Documented behavior. */
if (namelen > sizeof(saddr.sun_path))
namelen = sizeof(saddr.sun_path);
if (namelen > sizeof(saddr.up.path))
namelen = sizeof(saddr.up.path);
new_sock = (uv__stream_fd(handle) == -1);
@ -293,8 +305,8 @@ int uv_pipe_connect2(uv_connect_t* req,
}
memset(&saddr, 0, sizeof saddr);
memcpy(&saddr.sun_path, name, namelen);
saddr.sun_family = AF_UNIX;
memcpy(saddr.up.path, name, namelen);
saddr.sa.sa_family = AF_UNIX;
if (*name == '\0')
addrlen = offsetof(struct sockaddr_un, sun_path) + namelen;
@ -355,7 +367,7 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
#else
static const int is_linux = 0;
#endif
struct sockaddr_un sa;
union uv__sockaddr_un sa;
socklen_t addrlen;
size_t slop;
char* p;
@ -376,15 +388,15 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
}
slop = 1;
if (is_linux && sa.sun_path[0] == '\0') {
if (is_linux && sa.up.path[0] == '\0') {
/* Linux abstract namespace. Not zero-terminated. */
slop = 0;
addrlen -= offsetof(struct sockaddr_un, sun_path);
} else {
p = memchr(sa.sun_path, '\0', sizeof(sa.sun_path));
p = memchr(sa.up.path, '\0', sizeof(sa.up.path));
if (p == NULL)
p = ARRAY_END(sa.sun_path);
addrlen = p - sa.sun_path;
p = ARRAY_END(sa.up.path);
addrlen = p - sa.up.path;
}
if ((size_t)addrlen + slop > *size) {
@ -392,7 +404,7 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle,
return UV_ENOBUFS;
}
memcpy(buffer, sa.sun_path, addrlen);
memcpy(buffer, sa.up.path, addrlen);
*size = addrlen;
/* only null-terminate if it's not an abstract socket */

View File

@ -218,6 +218,7 @@ TEST_DECLARE (pipe_getsockname)
TEST_DECLARE (pipe_getsockname_abstract)
TEST_DECLARE (pipe_getsockname_autobind)
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)
@ -853,6 +854,7 @@ TASK_LIST_START
TEST_ENTRY (pipe_getsockname_abstract)
TEST_ENTRY (pipe_getsockname_autobind)
TEST_ENTRY (pipe_getsockname_blocking)
TEST_ENTRY (pipe_getsockname_long_path)
TEST_ENTRY (pipe_pending_instances)
TEST_ENTRY (pipe_sendmsg)

View File

@ -368,3 +368,52 @@ TEST_IMPL(pipe_getsockname_blocking) {
MAKE_VALGRIND_HAPPY(uv_default_loop());
return 0;
}
static void long_path_connect_cb(uv_connect_t* req, int status) {
ASSERT_OK(status);
uv_close((uv_handle_t*) req->handle, NULL);
}
TEST_IMPL(pipe_getsockname_long_path) {
#ifndef SOCK_MAXADDRLEN
RETURN_SKIP("long unix paths not supported on this platform");
#else
uv_loop_t* loop;
size_t len;
int r;
char path[254];
char name[254];
loop = uv_default_loop();
ASSERT_NOT_NULL(loop);
r = uv_pipe_init(loop, &pipe_server, 0);
ASSERT_OK(r);
r = uv_pipe_init(loop, &pipe_client, 0);
ASSERT_OK(r);
memset(path, 'x', sizeof(path) - 1);
path[sizeof(path) - 1] = '\0';
r = uv_pipe_bind(&pipe_server, path);
ASSERT_OK(r);
len = sizeof(name);
r = uv_pipe_getsockname(&pipe_server, name, &len);
ASSERT_OK(r);
ASSERT_EQ(len, strlen(path));
ASSERT_MEM_EQ(path, name, len);
r = uv_listen((uv_stream_t*) &pipe_server, 0, pipe_server_connection_cb);
ASSERT_OK(r);
uv_pipe_connect(&connect_req, &pipe_client, path, long_path_connect_cb);
uv_close((uv_handle_t*) &pipe_server, NULL);
ASSERT_OK(uv_run(loop, UV_RUN_DEFAULT));
MAKE_VALGRIND_HAPPY(loop);
return 0;
#endif
}