>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.

Reply via email to