Repository: libuv/libuv (C)

Issue:
unix: return error if strlen(socket path) > sizeof sun_path?
https://github.com/nodejs/node/issues/48316 made me realize that:

1. libuv currently truncates long paths, but

2. when #4030 lands, it returns `UV_EINVAL` when the path is too long

It's an unintentional change (because I thought it already worked that way) so I'd like to discuss:

1. whether to keep or back out that change, and

2. if and how to harmonize with src/win/pipe.c. MSDN says the "entire pipe name string can be up to 256 characters long" (characters, not bytes) but I don't know if CreateNamedPipeW() returns an error if it's longer than that. Easy to enforce ourselves though, I think:
```diff
diff --git a/src/win/pipe.c b/src/win/pipe.c
index a3daa3b7..f1634cf2 100644
--- a/src/win/pipe.c
+++ b/src/win/pipe.c
@@ -722,6 +722,11 @@ int uv_pipe_bind2(uv_pipe_t* handle,
     return UV_EINVAL;
   }

+  nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR);
+  if (nameSize > 256 * sizeof(WCHAR)) {
+    return UV_EINVAL;
+  }
+
   if (handle->flags & UV_HANDLE_BOUND) {
     return UV_EINVAL;
   }
@@ -749,7 +754,6 @@ int uv_pipe_bind2(uv_pipe_t* handle,
   }

   /* Convert name to UTF16. */
-  nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR);
   handle->name = uv__malloc(nameSize);
   if (!handle->name) {
     uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc");
```


Discussion:
Both uv_pipe_bind() and uv_pipe_connect() document the fact that libuv truncates the path:

> Paths on Unix get truncated to ``sizeof(sockaddr_un.sun_path)`` bytes, typically between

So I suppose that speaks in favor of backing out the change but we should probably address the Windows discrepancy.



Patch:
diff --git a/docs/src/pipe.rst b/docs/src/pipe.rst
index 9d95b206a97..4abdc65e715 100644
--- a/docs/src/pipe.rst
+++ b/docs/src/pipe.rst
@@ -68,15 +68,18 @@ API
 
     Bind the pipe to a file path (Unix) or a name (Windows).
 
-    ``flags`` must be zero. Returns ``UV_EINVAL`` for unsupported flags
-    without performing the bind operation.
+    ``flags`` must be zero or ``UV_PIPE_NO_TRUNCATE``. Returns ``UV_EINVAL``
+    for unsupported flags without performing the bind operation.
 
     Supports Linux abstract namespace sockets. ``namelen`` must include
     the leading nul byte but not the trailing nul byte.
 
+    .. versionadded:: 1.46.0
+
     .. note::
         Paths on Unix get truncated to ``sizeof(sockaddr_un.sun_path)`` bytes,
-        typically between 92 and 108 bytes.
+        typically between 92 and 108 bytes, unless the ``UV_PIPE_NO_TRUNCATE``
+        flag is specified, in which case an ``UV_EINVAL`` error is returned.
 
 .. c:function:: void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb)
 
@@ -95,15 +98,18 @@ API
 
     Connect to the Unix domain socket or the Windows named pipe.
 
-    ``flags`` must be zero. Returns ``UV_EINVAL`` for unsupported flags
-    without performing the connect operation.
+    ``flags`` must be zero or ``UV_PIPE_NO_TRUNCATE``. Returns ``UV_EINVAL``
+    for unsupported flags without performing the connect operation.
 
     Supports Linux abstract namespace sockets. ``namelen`` must include
     the leading nul byte but not the trailing nul byte.
 
+    .. versionadded:: 1.46.0
+
     .. note::
         Paths on Unix get truncated to ``sizeof(sockaddr_un.sun_path)`` bytes,
-        typically between 92 and 108 bytes.
+        typically between 92 and 108 bytes, unless the ``UV_PIPE_NO_TRUNCATE``
+        flag is specified, in which case an ``UV_EINVAL`` error is returned.
 
 .. c:function:: int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size)
 
diff --git a/include/uv.h b/include/uv.h
index 0f624f4d7f7..f68ff083b04 100644
--- a/include/uv.h
+++ b/include/uv.h
@@ -807,6 +807,10 @@ inline int uv_tty_set_mode(uv_tty_t* handle, int mode) {
 
 UV_EXTERN uv_handle_type uv_guess_handle(uv_file file);
 
+enum {
+  UV_PIPE_NO_TRUNCATE = 1u << 0
+};
+
 /*
  * uv_pipe_t is a subclass of uv_stream_t.
  *
diff --git a/src/unix/pipe.c b/src/unix/pipe.c
index 823deb3d975..d332f351830 100644
--- a/src/unix/pipe.c
+++ b/src/unix/pipe.c
@@ -56,7 +56,7 @@ int uv_pipe_bind2(uv_pipe_t* handle,
 
   pipe_fname = NULL;
 
-  if (flags != 0)
+  if (flags & ~UV_PIPE_NO_TRUNCATE)
     return UV_EINVAL;
 
   if (name == NULL)
@@ -71,6 +71,10 @@ int uv_pipe_bind2(uv_pipe_t* handle,
     return UV_EINVAL;
 #endif
 
+  if (flags & UV_PIPE_NO_TRUNCATE)
+    if (namelen > sizeof(saddr.sun_path))
+      return UV_EINVAL;
+
   /* Truncate long paths. Documented behavior. */
   if (namelen > sizeof(saddr.sun_path))
     namelen = sizeof(saddr.sun_path);
@@ -221,7 +225,7 @@ int uv_pipe_connect2(uv_connect_t* req,
   int err;
   int r;
 
-  if (flags != 0)
+  if (flags & ~UV_PIPE_NO_TRUNCATE)
     return UV_EINVAL;
 
   if (name == NULL)
@@ -236,6 +240,10 @@ int uv_pipe_connect2(uv_connect_t* req,
     return UV_EINVAL;
 #endif
 
+  if (flags & UV_PIPE_NO_TRUNCATE)
+    if (namelen > sizeof(saddr.sun_path))
+      return UV_EINVAL;
+
   /* Truncate long paths. Documented behavior. */
   if (namelen > sizeof(saddr.sun_path))
     namelen = sizeof(saddr.sun_path);
diff --git a/src/win/pipe.c b/src/win/pipe.c
index a3daa3b7a18..f0cac382256 100644
--- a/src/win/pipe.c
+++ b/src/win/pipe.c
@@ -706,7 +706,7 @@ int uv_pipe_bind2(uv_pipe_t* handle,
   int i, err, nameSize;
   uv_pipe_accept_t* req;
 
-  if (flags != 0) {
+  if (flags & ~UV_PIPE_NO_TRUNCATE) {
     return UV_EINVAL;
   }
 
@@ -722,6 +722,12 @@ int uv_pipe_bind2(uv_pipe_t* handle,
     return UV_EINVAL;
   }
 
+  if (flags & UV_PIPE_NO_TRUNCATE) {
+    if (namelen > 256) {
+      return UV_EINVAL;
+    }
+  }
+
   if (handle->flags & UV_HANDLE_BOUND) {
     return UV_EINVAL;
   }
@@ -859,7 +865,7 @@ int uv_pipe_connect2(uv_connect_t* req,
   HANDLE pipeHandle = INVALID_HANDLE_VALUE;
   DWORD duplex_flags;
 
-  if (flags != 0) {
+  if (flags & ~UV_PIPE_NO_TRUNCATE) {
     return UV_EINVAL;
   }
 
@@ -875,6 +881,12 @@ int uv_pipe_connect2(uv_connect_t* req,
     return UV_EINVAL;
   }
 
+  if (flags & UV_PIPE_NO_TRUNCATE) {
+    if (namelen > 256) {
+      return UV_EINVAL;
+    }
+  }
+
   UV_REQ_INIT(req, UV_CONNECT);
   req->handle = (uv_stream_t*) handle;
   req->cb = cb;


Test patch:
diff --git a/test/test-list.h b/test/test-list.h
index 6da7812360a..b2c88170051 100644
--- a/test/test-list.h
+++ b/test/test-list.h
@@ -198,6 +198,7 @@ TEST_DECLARE   (pipe_connect_close_multiple)
 TEST_DECLARE   (pipe_connect_multiple)
 TEST_DECLARE   (pipe_listen_without_bind)
 TEST_DECLARE   (pipe_bind_or_listen_error_after_close)
+TEST_DECLARE   (pipe_overlong_path)
 TEST_DECLARE   (pipe_connect_bad_name)
 TEST_DECLARE   (pipe_connect_to_file)
 TEST_DECLARE   (pipe_connect_on_prepare)
@@ -799,6 +800,7 @@ TASK_LIST_START
   TEST_ENTRY  (pipe_connect_multiple)
   TEST_ENTRY  (pipe_listen_without_bind)
   TEST_ENTRY  (pipe_bind_or_listen_error_after_close)
+  TEST_ENTRY  (pipe_overlong_path)
   TEST_ENTRY  (pipe_getsockname)
   TEST_ENTRY  (pipe_getsockname_abstract)
   TEST_ENTRY  (pipe_getsockname_blocking)
diff --git a/test/test-pipe-bind-error.c b/test/test-pipe-bind-error.c
index 88ece6ba5d8..f8626302a1d 100644
--- a/test/test-pipe-bind-error.c
+++ b/test/test-pipe-bind-error.c
@@ -153,3 +153,27 @@ TEST_IMPL(pipe_bind_or_listen_error_after_close) {
   MAKE_VALGRIND_HAPPY(uv_default_loop());
   return 0;
 }
+
+TEST_IMPL(pipe_overlong_path) {
+  char path[512];
+  uv_pipe_t pipe;
+  uv_connect_t req;
+
+  memset(path, '@', sizeof(path));
+  ASSERT_OK(uv_pipe_init(uv_default_loop(), &pipe, 0));
+  ASSERT_EQ(UV_EINVAL,
+            uv_pipe_bind2(&pipe, path, sizeof(path), UV_PIPE_NO_TRUNCATE));
+  ASSERT_EQ(UV_EINVAL,
+            uv_pipe_connect2(&req,
+                             &pipe,
+                             path,
+                             sizeof(path),
+                             UV_PIPE_NO_TRUNCATE,
+                             (uv_connect_cb) abort));
+  uv_close((uv_handle_t*) &pipe, NULL);
+  ASSERT_OK(uv_run(uv_default_loop(), UV_RUN_DEFAULT));
+
+  MAKE_VALGRIND_HAPPY(uv_default_loop());
+  return 0;
+
+}