>Sypnosis: getsockname() reports more information in the socket address
buffer than what actually exists
>Category: library
>Environment:
System : OpenBSD 7.4
Details : OpenBSD 7.4 (GENERIC.MP) #1397: Tue Oct 10
09:02:37 MDT 2023
dera...@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/GENERIC.MP
Architecture: OpenBSD.amd64
Machine : amd64
>Description:
When calling the "getsockname()" standard C library call, it returns
more bytes than the actual length in the
resulting socket path. This results in sockets named, for example,
"socket123", being represented instead as
"socket123\0\0\0\0\0\0\0...". The problem appears to stem from the C
library implementation setting the socket
name to the maximum length allowed by the socket address rather than the
actual name. This is not a problem
if you use traditional null-terminated C strings, but becomes a problem
in other languages which use the returned
length to determine how to use the string.
This was found during the course of investigating this issue:
https://github.com/rust-lang/rust/issues/116523
>How-To-Repeat:
I was able to repeat the bug using this C program:
---
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int server_sock, rc, len, i;
struct sockaddr_un address_in, address_out;
char address_name[32] = { 0 };
char c;
server_sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (server_sock == -1) {
puts("failed to open socket");
exit(1);
}
address_in.sun_family = AF_UNIX;
strcpy(address_in.sun_path, "/tmp/socket123");
len = strlen("/tmp/socket123") + sizeof(address_in) -
sizeof(address_in.sun_path);
rc = bind(server_sock, &address_in, len);
if (rc == -1) {
puts("failed to bind to socket");
exit(1);
}
len = 11111;
rc = getsockname(server_sock, &address_out, &len);
if (rc == -1) {
puts("failed to read socket name");
exit(1);
}
len -= (sizeof(address_out) - sizeof(address_out.sun_path));
printf("socket address length is %d\n", len);
for (i = 0; i < len; i++) {
c = address_out.sun_path[i];
if (c >= 33 && c <= 126) {
printf("%c", c);
} else {
printf("\\%d", c);
}
}
printf("\n");
close(server_sock);
return 0;
}
---
When run on a Linux machine, it produces this output:
---
$ cc bug.c -o bug
$ ./bug
socket address length is 15
/tmp/socket123\0
---
I receive the same output on FreeBSD and NetBSD.
When run on OpenSBD 7.4, it produces this output:
---
$ cc bug.c -o bug
$ ./bug
socket address length is 104
/tmp/socket123\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
---
Ideally, OpenBSD would return the same output.
>Fix:
This could be fixed by returning the actual length of the socket address
in the "len" variable
in the "getsockname" function.
I don't believe that dmesg is relevant for this bug; I can provide it if
requested.