[erlang-questions] unix domain sockets with abstract namespace: can't use all 108 bytes

Raimo Niskanen raimo+erlang-questions@REDACTED
Mon Apr 24 12:01:09 CEST 2017


On Wed, Apr 19, 2017 at 12:13:26PM +0000, Bekes, Andras G wrote:
> Did anyone have a chance to take a closer look?
> 
> I still think that this is a bug in Erlang.

So far no-one have had any good suggestion on how to handle this
in a platform independent way, nor any suggestion on how to detect
this misfeature with configure.  And digging into this is not
on our prioritized list.

I am leaning towards special treatment for this Linux abnormality by
observing the leading 0 and in this special case use all bytes of
the given binary.

Then we might pass a length to the kernel that does not match
the string length of the file path.  But if such a platform uses
zero termination there is no problem, and if it uses
the size information there is no problem, it is only if it uses both
that might be a problem, and that should be very unlikely...

Something like this should do it:
------------------- erts/emulator/drivers/common/inet_drv.c -------------------
index 0fe5183..abcc289 100644
@@ -602,14 +602,6 @@ static size_t my_strnlen(const char *s, size_t maxlen)
     return i;
 }
 
-/* Check that some character in the buffer != '\0' */
-static int is_nonzero(const char *s, size_t n)
-{
-    size_t i;
-    for (i = 0;  i < n;  i++) if (s[i] != '\0') return !0;
-    return 0;
-}
-
 #endif
 
 #ifdef VALGRIND
@@ -4018,7 +4010,15 @@ static char* inet_set_address(int family,
inet_address* dst,
         int n;
         if (*len == 0) return str_einval;
 	n = *((unsigned char*)(*src)); /* Length field */
-	if ((*len < 1+n) || (sizeof(dst->sal.sun_path) < n+1)) {
+	if (*len < 1+n) return str_einval;
+	/* Portability fix: Make sure the address gets zero terminated
+         * except when the first byte is \0 because then it is sort
+         * of zero terminated although the zero termination comes
+         * before the address...  This fix handles Linux's
+         * abstract socket address nonportable extension.
+	 */
+	if (n + ((*len) > 1 && (*src)[1] == '\0' ? 0 : 1)
+            > sizeof(dst->sal.sun_path)) {
 	    return str_einval;
 	}
 	sys_memzero((char*)dst, sizeof(struct sockaddr_un));
@@ -4176,8 +4176,7 @@ static int inet_get_address(char* dst, inet_address*
src, unsigned int* len)
 	 * nonportable extension.
 	 */
         m = my_strnlen(src->sal.sun_path, n);
-	if ((m == 0) && is_nonzero(src->sal.sun_path, n))
-	    m = n;
+	if (m == 0)  m = n;
         dst[0] = INET_AF_LOCAL;
         dst[1] = (char) ((unsigned char) m);
         sys_memcpy(dst+2, src->sal.sun_path, m);
@@ -4241,8 +4240,7 @@ inet_address_to_erlang(char *dst, inet_address **src,
SOCKLEN_T sz) {
 	 * nonportable extension.
 	 */
         m = my_strnlen((*src)->sal.sun_path, n);
-	if ((m == 0) && is_nonzero((*src)->sal.sun_path, n))
-	    m = n;
+	if (m == 0)  m = n;
 	if (dst) {
 	    dst[0] = INET_AF_LOCAL;
 	    dst[1] = (char) ((unsigned char) m);



> 
> From: Bekes, Andras G (IST)
> Sent: Tuesday, March 28, 2017 9:55 AM
> To: 'Alex S.'
> Cc: erlang-questions@REDACTED
> Subject: RE: [erlang-questions] unix domain sockets with abstract namespace: can't use all 108 bytes
> 
> I was also thinking about how and why Erlang puts its limit at 107 characters, but regardless of the answers, apparently I need to use 108 bytes, which seems to be a valid thing on Linux.
> Note: I need to see the number 110 passed as the 3rd parameter to the connect syscall, as opposed to 109.
> 
> From: Alex S. [mailto:alex0player@REDACTED]
> Sent: Monday, March 27, 2017 8:00 PM
> To: Bekes, Andras G (IST)
> Cc: erlang-questions@REDACTED
> Subject: Re: [erlang-questions] unix domain sockets with abstract namespace: can't use all 108 bytes
> 
> I have a strong suspicion the binary might be automatically zero-terminated.
> 27 марта 2017 г., в 19:03, Bekes, Andras G <Andras.Bekes@REDACTED<mailto:Andras.Bekes@REDACTED>> написал(а):
> 
> 
> Hi All,
> 
> I need to communicate with unix domain sockets using the abstract namespace and the server (which is not under my control) opens the port with all 108 bytes of the path used.
> If I understand it correctly, the server/client can decide to indicate the number of actual bytes used in the 3rd parameter to the bind/connect syscalls (this what Erlang does), but it is also possible to use all 108 bytes and fill the unused part with 0 bytes (this is what my server does).
> http://man7.org/linux/man-pages/man7/unix.7.html
> 
> Apparently, I can fill the path with 0 bytes up to 107 only. If I try to pass a 108-byte address to gen_tcp:connect, I get a badarg exception:
> 
> > Sun_path_length=108.
> 108
> 
> > UDS_path = <<"whatever">>.
> <<"whatever">>
> 
> > Fill_length = 8*(Sun_path_length-byte_size(UDS_path)-1).
> 792
> 
> > Address = {local,<<0, UDS_path/binary,0:Fill_length>>}.
> {local,<<0,119,104,97,116,101,118,101,114,0,0,0,0,0,0,0,
>          0,0,0,0,0,0,0,0,0,0,0,...>>}
> 
> > Port = 0.
> 0
> 
> > gen_tcp:connect(Address,Port,[local],infinity).
> (<0.236.0>) call gen_tcp:connect({local,<<0,119,104,97,116,101,118,101,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
>          0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
>          0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
>          0,0,0,0,0,0,0,0,0,0,0,0,0>>},0,[local],infinity)
> (<0.236.0>) call gen_tcp:connect1({local,<<0,119,104,97,116,101,118,101,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
>          0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
>          0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
>          0,0,0,0,0,0,0,0,0,0,0,0,0>>},0,[local],false)
> (<0.236.0>) call gen_tcp:try_connect([{local,<<0,119,104,97,116,101,118,101,114,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
>           0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
>           0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
>           0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0>>}],0,[],false,local_tcp,{error,einval})
> (<0.236.0>) returned from gen_tcp:try_connect/6 -> {error,einval}
> ** exception exit: badarg
>      in function  gen_tcp:connect/4 (gen_tcp.erl, line 149)
> (<0.236.0>) returned from gen_tcp:connect1/4 -> {error,einval}
> (<0.236.0>) exception_from {gen_tcp,connect,4} {exit,badarg}
> 
> If I reduce the path to 107 bytes, it works. With strace, I can see the connect syscall:
> connect(19, {sa_family=AF_FILE, path=@"whatever"...}, 109) = -1 ECONNREFUSED (Connection refused)
> When I strace the official client of this server, the 3rd parameter to the connect syscall is 110 (108 + 2), regardless of the actual length of the path.
> 
> Apparently, with Erlang it is not possible to use all 108 bytes. I should only get a badarg error at 109 bytes, not 108.
> Seems to me that this is a bug in the Erlang implementation. What do you think?
> 
> Thank you very much,
> 
> Andras G. Bekes
> 


-- 

/ Raimo Niskanen, Erlang/OTP, Ericsson AB



More information about the erlang-questions mailing list