[LibOS] Rework inet_copy_addr to avoid stack smashing when using IPv6

Rework inet_copy_addr by using sockaddr_storage (ported from glibc's
bits/socket.h) for writing the data into, which is large enough to hold
sockaddr_in6. Then copy back into the user's buffer.

Previously some calls to inet_copy_addr were passing in a
'struct sockaddr' that is too small to hold sockaddr_in6 data, thus
causing stack corruption when the socket was an IPv6 socket.
This commit is contained in:
Stefan Berger
2020-04-06 21:09:10 +02:00
committed by Michał Kowalczyk
parent 5ba67f27b7
commit dc0cbd0b68
2 changed files with 38 additions and 37 deletions
+8
View File
@@ -379,6 +379,14 @@ struct sockaddr {
char sa_data[14]; /* Address data. */
};
/* From bits/socket.h */
/* Structure large enough to hold any socket address (with the historical
exception of AF_UNIX). */
struct sockaddr_storage {
__SOCKADDR_COMMON(ss_); /* Address family, etc. */
char __ss_padding[128 - sizeof(sa_family_t)];
};
/* linux/mqueue.h */
struct __kernel_mq_attr {
long mq_flags; /* message queue flags */
+30 -37
View File
@@ -329,24 +329,39 @@ static int inet_check_addr(int domain, struct sockaddr* addr, socklen_t addrlen)
return -EINVAL;
}
static int inet_copy_addr(int domain, struct sockaddr* saddr, const struct addr_inet* addr) {
if (domain == AF_INET) {
struct sockaddr_in* in = (struct sockaddr_in*)saddr;
static socklen_t inet_copy_addr(int domain, struct sockaddr* saddr, socklen_t saddr_len,
const struct addr_inet* addr) {
struct sockaddr_storage ss;
struct sockaddr_in* in;
struct sockaddr_in6* in6;
socklen_t len = 0;
switch (domain) {
case AF_INET:
in = (struct sockaddr_in*)&ss;
in->sin_family = AF_INET;
in->sin_port = __htons(addr->port);
in->sin_addr = addr->addr.v4;
return sizeof(struct sockaddr_in);
}
if (domain == AF_INET6) {
struct sockaddr_in6* in6 = (struct sockaddr_in6*)saddr;
len = MIN(saddr_len, sizeof(struct sockaddr_in));
break;
case AF_INET6:
in6 = (struct sockaddr_in6*)&ss;
in6->sin6_family = AF_INET6;
in6->sin6_port = __htons(addr->port);
in6->sin6_addr = addr->addr.v6;
return sizeof(struct sockaddr_in6);
len = MIN(saddr_len, sizeof(struct sockaddr_in6));
break;
default:
__abort(); /* this function must accept only AF_INET/AF_INET6 */
}
return sizeof(struct sockaddr);
memcpy(saddr, &ss, len);
return len;
}
static void inet_save_addr(int domain, struct addr_inet* addr, const struct sockaddr* saddr) {
@@ -943,14 +958,8 @@ int __do_accept(struct shim_handle* hdl, int flags, struct sockaddr* addr, sockl
inet_rebase_port(true, cli_sock->domain, &cli_sock->addr.in.bind, true);
inet_rebase_port(true, cli_sock->domain, &cli_sock->addr.in.conn, false);
if (addr) {
inet_copy_addr(sock->domain, addr, &sock->addr.in.conn);
if (addrlen) {
assert(sock->domain == AF_INET || sock->domain == AF_INET6);
*addrlen = minimal_addrlen(sock->domain);
}
}
if (addr)
*addrlen = inet_copy_addr(sock->domain, addr, *addrlen, &sock->addr.in.conn);
}
ret = set_new_fd_handle(cli, flags & O_CLOEXEC ? FD_CLOEXEC : 0, NULL);
@@ -1305,13 +1314,10 @@ static ssize_t do_recvmsg(int fd, struct iovec* bufs, int nbufs, int flags, stru
debug("last packet received from %s\n", uri);
inet_rebase_port(true, sock->domain, &conn, false);
inet_copy_addr(sock->domain, addr, &conn);
*addrlen = inet_copy_addr(sock->domain, addr, *addrlen, &conn);
} else {
inet_copy_addr(sock->domain, addr, &sock->addr.in.conn);
*addrlen = inet_copy_addr(sock->domain, addr, *addrlen, &sock->addr.in.conn);
}
*addrlen = (sock->domain == AF_INET) ? sizeof(struct sockaddr_in)
: sizeof(struct sockaddr_in6);
}
address_received = true;
@@ -1486,14 +1492,8 @@ int shim_do_getsockname(int sockfd, struct sockaddr* addr, int* addrlen) {
struct shim_sock_handle* sock = &hdl->info.sock;
lock(&hdl->lock);
struct sockaddr saddr;
int len = inet_copy_addr(sock->domain, &saddr, &sock->addr.in.bind);
*addrlen = inet_copy_addr(sock->domain, addr, *addrlen, &sock->addr.in.bind);
if (len < *addrlen)
len = *addrlen;
memcpy(addr, &saddr, len);
*addrlen = len;
ret = 0;
unlock(&hdl->lock);
out:
@@ -1538,14 +1538,7 @@ int shim_do_getpeername(int sockfd, struct sockaddr* addr, int* addrlen) {
goto out_locked;
}
struct sockaddr saddr;
int len = inet_copy_addr(sock->domain, &saddr, &sock->addr.in.conn);
if (len < *addrlen)
len = *addrlen;
memcpy(addr, &saddr, len);
*addrlen = len;
*addrlen = inet_copy_addr(sock->domain, addr, *addrlen, &sock->addr.in.conn);
ret = 0;
out_locked:
unlock(&hdl->lock);