diff --git a/security/landlock/net.c b/security/landlock/net.c index a38bdfcffc22..4ee4002a8f56 100644 --- a/security/landlock/net.c +++ b/security/landlock/net.c @@ -46,6 +46,7 @@ static int current_check_access_socket(struct socket *const sock, const int addrlen, access_mask_t access_request) { + unsigned short sock_family; __be16 port; struct layer_access_masks layer_masks = {}; const struct landlock_rule *rule; @@ -66,6 +67,12 @@ static int current_check_access_socket(struct socket *const sock, if (addrlen < offsetofend(typeof(*address), sa_family)) return -EINVAL; + /* + * The socket is not locked, so sk_family can change concurrently due to + * e.g. setsockopt(IPV6_ADDRFORM). + */ + sock_family = READ_ONCE(sock->sk->sk_family); + switch (address->sa_family) { case AF_UNSPEC: if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) { @@ -102,7 +109,7 @@ static int current_check_access_socket(struct socket *const sock, * these checks, but it is safer to return a proper * error and test consistency thanks to kselftest. */ - if (sock->sk->__sk_common.skc_family == AF_INET) { + if (sock_family == AF_INET) { const struct sockaddr_in *const sockaddr = (struct sockaddr_in *)address; @@ -180,7 +187,7 @@ static int current_check_access_socket(struct socket *const sock, * check, but it is safer to return a proper error and test * consistency thanks to kselftest. */ - if (address->sa_family != sock->sk->__sk_common.skc_family && + if (address->sa_family != sock_family && address->sa_family != AF_UNSPEC) return -EINVAL;