[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#645469: bind() fails for AF_UNIX sockets with EINVAL



Robert Millan wrote:
> 2011/10/16 Jonathan Nieder <jrnieder@gmail.com>:

>>        unsigned char sun_len;
>>        unsigned char sun_family;
>>        char sun_path[108];     /* Path name. */
>
> Is this 108 the actual length?  ISTR it was just a placeholder.

It's the actual size of the sun_path field of struct sockaddr_un,
if that's what you mean.

> <sys/un.h> has a macro to determine actual length:
>
> /* Evaluate to actual length of the `sockaddr_un' structure.  */
> # define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path)        \
>                       + strlen ((ptr)->sun_path))

Ah, thanks for this clarification.  glibc's reference manual says:

	You should compute the "length" parameter for a socket address in
	the local namespace as the sum of the size of the "sun_family"
	component and the string length (/not/ the allocation size!) of
	the file name string.  This can be done using the macro "SUN_LEN":

In other words, you can call bind() with SUN_LEN(ptr) as the addrlen
argument instead of sizeof(struct sockaddr_un) to tell the kernel not to
copy so much, and to avoid depending on the magic number chosen in the
definition of sockaddr_un.

However, the POSIX documentation for bind() gives an example of an AF_UNIX
socket with

	if (bind(sfd, (struct sockaddr *) &my_addr,
		sizeof(struct sockaddr_un)) == -1)

Similarly, the example in the bind(2) manpage from the man-pages project
uses sizeof(struct sockaddr_un) as the addrlen argument.

Regarding the magic number, POSIX provides some more insight:

	The size of "sun_path" has intentionally been left undefined.
	This is because different implementations use different sizes.
	For example, 4.3 BSD uses a size of 108, and 4.4 BSD uses a
	size of 104.  Since most implementations originate from BSD
	versions, the size is typically in the range 92 to 108.

[...]
>> I wonder whether there would be any downside to changing that 104 in
>> the kernel to 108.
>
> Breaking kernel ABI for everyone not using a patched kernel.  Not a good thing.

If it's desirable, the change could be made upstream.

>> Separately from that, it would be helpful to know where the buffer
>> overflowed in #645377 is, since maybe it could be made bigger without
>> changing the layout of struct sockaddr_un.
>
> I don't think this matters, it's an instance of sockaddr_un.

If sockaddr_un is part of the ABI as an argument to some function,
it would matter.

> If you
> make sun_path[] bigger in the kernel, then instead of 160 you can
> overflow it with a larger value
[...]
> The kernel-side of things is now doing the right thing AFAICS.

It's breaking userspace apps that followed documentation to the letter
and worked before.

How about this (untested)?

diff --git i/sys/kern/uipc_syscalls.c w/sys/kern/uipc_syscalls.c
index 3b83e1c..7b4a11e 100644
--- i/sys/kern/uipc_syscalls.c
+++ w/sys/kern/uipc_syscalls.c
@@ -1703,11 +1703,18 @@ getsockaddr(namp, uaddr, len)
 	if (error) {
 		free(sa, M_SONAME);
 	} else {
+		const char *p;
+		size_t datalen;
+
 #if defined(COMPAT_OLDSOCK) && BYTE_ORDER != BIG_ENDIAN
 		if (sa->sa_family == 0 && sa->sa_len < AF_MAX)
 			sa->sa_family = sa->sa_len;
 #endif
 		sa->sa_len = len;
+		datalen = len - offsetof(struct sockaddr, sa_data[0]);
+		p = memchr(sa->sa_data, '\0', datalen);
+		if (p)
+			sa_len = p - (const char *)sa;
 		*namp = sa;
 	}
 	return (error);



Reply to: