]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
landlock: Fix unmarked concurrent access to socket family
authorMatthieu Buffet <matthieu@buffet.re>
Tue, 9 Jun 2026 21:15:10 +0000 (23:15 +0200)
committerMickaël Salaün <mic@digikod.net>
Sat, 13 Jun 2026 21:15:03 +0000 (23:15 +0200)
Socket family is read (twice) in a context where the socket is not
locked, so another thread can setsockopt(IPV6_ADDRFORM) to write it
concurrently. Add needed READ_ONCE() annotation.

Use the proper macro to access __sk_common.skc_family like everywhere
else.

Fixes: fff69fb03dde ("landlock: Support network rules with TCP bind and connect")
Signed-off-by: Matthieu Buffet <matthieu@buffet.re>
Link: https://patch.msgid.link/20260609211511.85630-1-matthieu@buffet.re
Link: https://patch.msgid.link/20260609211511.85630-2-matthieu@buffet.re
[mic: Squash two patches, move variable to ease backport, fix comment
formatting]
Signed-off-by: Mickaël Salaün <mic@digikod.net>
security/landlock/net.c

index a38bdfcffc22a31b23b89e07f2f214b4afa3d455..4ee4002a8f56765e0998a5880dc9c0399ae69196 100644 (file)
@@ -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;