Skip to content

Commit d1d6c6e

Browse files
committed
listen: ip bind: fix numeric iface parse overrunning caller sockaddr_in
lws_interface_to_sa() may be handed a buffer as small as a struct sockaddr_in: lws_socket_bind()'s AF_INET path passes its 16-byte serv_addr4 (with addrlen = sizeof(serv_addr4)). When ifname is not a real interface name, the fallback casts that buffer to lws_sockaddr46 * and calls lws_sa46_parse_numeric_address(), which begins with memset(sa46, 0, sizeof(*sa46)); sizeof(lws_sockaddr46) is 28 bytes when IPv6 is enabled (it embeds a struct sockaddr_in6), so this writes 12 bytes past a 16-byte caller buffer. ASan reports a stack-buffer-overflow in lws_sa46_parse_numeric_address() for any listen bind to a numeric IPv4 address (eg, lws_socket_bind() -> lws_interface_to_sa() with iface "127.0.0.1"); in a release build the overrun silently clobbers adjacent stack. addrlen is already passed for exactly this reason but was ignored on this path. Parse into a local full-width lws_sockaddr46 and memcpy back only min(sa46_socklen(), addrlen) bytes, so the caller's buffer is never overrun regardless of family. The 28-byte memset was introduced with the recent lws_sa46_parse_numeric_address() rework; the (lws_sockaddr46 *)addr cast predates it and was harmless while the function only wrote the parsed family's fields.
1 parent fd1e670 commit d1d6c6e

1 file changed

Lines changed: 21 additions & 3 deletions

File tree

lib/plat/unix/unix-sockets.c

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -447,9 +447,27 @@ lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr,
447447

448448
freeifaddrs(ifr);
449449

450-
if (rc &&
451-
!lws_sa46_parse_numeric_address(ifname, (lws_sockaddr46 *)addr))
452-
rc = LWS_ITOSA_USABLE;
450+
/*
451+
* ifname may be a numeric address literal rather than an interface
452+
* name. Parse it into a full-width lws_sockaddr46 and copy back only
453+
* as much as fits in the caller's buffer: addr may be as small as a
454+
* struct sockaddr_in (eg, the AF_INET path in lws_socket_bind() passes
455+
* a 16-byte serv_addr4), so casting it to lws_sockaddr46 * and letting
456+
* lws_sa46_parse_numeric_address() memset sizeof(lws_sockaddr46) (28
457+
* bytes when IPv6 is enabled) straight through it overflows that buffer.
458+
*/
459+
if (rc) {
460+
lws_sockaddr46 sa46;
461+
462+
if (!lws_sa46_parse_numeric_address(ifname, &sa46)) {
463+
size_t n = (size_t)sa46_socklen(&sa46);
464+
465+
if (n > addrlen)
466+
n = addrlen;
467+
memcpy(addr, &sa46, n);
468+
rc = LWS_ITOSA_USABLE;
469+
}
470+
}
453471

454472
return rc;
455473
}

0 commit comments

Comments
 (0)