CVE-2021-38383: Use After Free (CWE-416)
value: OwnTone (aka owntone-server) through 28.1 has a use-after-free in net_bind() in misc.c.
CWE description: The product reuses or references memory after it has been freed. At some point afterward, the memory may be allocated again and saved in another pointer, while the original pointer references a location somewhere within the new allocation. Any operations using the original pointer are no longer valid because the memory "belongs" to the code that operates on the new pointer.
CVSS v3 base score: 9.8
Published: 2021-08-10T18:15Z
Repository: https://github.com/owntone/owntone-server
Language: C
Files changed: src/misc.c
Commit date: 2021-07-31 00:55:48 +0200
Commit message: [misc] Fix use-after-free in net_bind()

Thanks to Ba Jinsheng for reporting this bug

Diff: diff --git a/src/misc.c b/src/misc.c
index 40625257..73266c91 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -249,10 +249,12 @@ int
 net_bind(short unsigned *port, int type, const char *log_service_name)
 {
   struct addrinfo hints = { 0 };
   struct addrinfo *servinfo;
   struct addrinfo *ptr;
+  union net_sockaddr naddr = { 0 };
+  socklen_t naddr_len = sizeof(naddr);
   const char *cfgaddr;
   char addr[INET6_ADDRSTRLEN];
   char strport[8];
   int yes = 1;
   int no = 0;
@@ -312,20 +314,26 @@ net_bind(short unsigned *port, int type, const char *log_service_name)
     {
       DPRINTF(E_LOG, L_MISC, "Could not create service '%s' with address %s, port %hu: %s\n", log_service_name, cfgaddr ? cfgaddr : "(ANY)", *port, strerror(errno));
       goto error;
     }
 
-  // Get the port that was assigned
-  ret = getsockname(fd, ptr->ai_addr, &ptr->ai_addrlen);
+  // Get our address (as string) and the port that was assigned (necessary when
+  // caller didn't specify a port)
+  ret = getsockname(fd, &naddr.sa, &naddr_len);
   if (ret < 0)
     {
-      DPRINTF(E_LOG, L_MISC, "Could not find address of service '%s': %s\n", log_service_name, strerror(errno));
+      DPRINTF(E_LOG, L_MISC, "Error finding address of service '%s': %s\n", log_service_name, strerror(errno));
+      goto error;
+    }
+  else if (naddr_len > sizeof(naddr))
+    {
+      DPRINTF(E_LOG, L_MISC, "Unexpected address length of service '%s'\n", log_service_name);
       goto error;
     }
 
-  net_port_get(port, (union net_sockaddr *)ptr->ai_addr);
-  net_address_get(addr, sizeof(addr), (union net_sockaddr *)ptr->ai_addr);
+  net_port_get(port, &naddr);
+  net_address_get(addr, sizeof(addr), &naddr);
 
   DPRINTF(E_DBG, L_MISC, "Service '%s' bound to %s, port %hu, socket %d\n", log_service_name, addr, *port, fd);
 
   return fd;
 

Vulnerable code:   // Get the port that was assigned
  ret = getsockname(fd, ptr->ai_addr, &ptr->ai_addrlen);
      DPRINTF(E_LOG, L_MISC, "Could not find address of service '%s': %s\n", log_service_name, strerror(errno));
  net_port_get(port, (union net_sockaddr *)ptr->ai_addr);
  net_address_get(addr, sizeof(addr), (union net_sockaddr *)ptr->ai_addr);
Fixed code:   union net_sockaddr naddr = { 0 };
  socklen_t naddr_len = sizeof(naddr);
  // Get our address (as string) and the port that was assigned (necessary when
  // caller didn't specify a port)
  ret = getsockname(fd, &naddr.sa, &naddr_len);
      DPRINTF(E_LOG, L_MISC, "Error finding address of service '%s': %s\n", log_service_name, strerror(errno));
      goto error;
    }
  else if (naddr_len > sizeof(naddr))
    {
      DPRINTF(E_LOG, L_MISC, "Unexpected address length of service '%s'\n", log_service_name);
  net_port_get(port, &naddr);
  net_address_get(addr, sizeof(addr), &naddr);