]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net/atm: fix slab-out-of-bounds read in vcc_setsockopt()
authorEric Dumazet <edumazet@google.com>
Wed, 5 Aug 2026 13:15:08 +0000 (13:15 +0000)
committerJakub Kicinski <kuba@kernel.org>
Thu, 6 Aug 2026 16:29:14 +0000 (09:29 -0700)
vcc_setsockopt() contained an ineffective optlen check:
  if (__SO_LEVEL_MATCH(optname, level) && optlen != __SO_SIZE(optname))
      return -EINVAL;

If __SO_LEVEL_MATCH(optname, level) evaluated to false (e.g. if the caller
passed a mismatched level), the length check optlen != __SO_SIZE(optname)
was short-circuited and bypassed. Execution then fell through to switch(optname),
calling copy_from_sockptr() assuming optval contained sufficient space.

Furthermore, even if level matched, a cgroup BPF setsockopt filter could shrink
optlen after entry. Because copy_from_sockptr() on kernel pointers uses memcpy(),
this leads to a KASAN slab-out-of-bounds read when optlen is smaller than the
expected structure size.

Fix this by using copy_safe_from_sockptr(), which unconditionally validates
that optlen is at least the expected size before copying. Also change the local
'value' variable type from 'unsigned long' to 'int' so that SO_SETCLP matches
its sizeof(int) ABI encoding on 64-bit systems.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Reported-by: syzbot+53ecc09fb81df10ef4de@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=53ecc09fb81df10ef4de
Signed-off-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20260805131508.3227331-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/atm/common.c

index c7f92405daf050ecd2a507e41efdd622ab8dc6a8..81195727fa18ba4e6146c0c02cd3898d17fc436f 100644 (file)
@@ -710,7 +710,7 @@ int vcc_setsockopt(struct socket *sock, int level, int optname,
                   sockptr_t optval, unsigned int optlen)
 {
        struct atm_vcc *vcc;
-       unsigned long value;
+       int value;
        int error;
 
        if (__SO_LEVEL_MATCH(optname, level) && optlen != __SO_SIZE(optname))
@@ -722,8 +722,10 @@ int vcc_setsockopt(struct socket *sock, int level, int optname,
        {
                struct atm_qos qos;
 
-               if (copy_from_sockptr(&qos, optval, sizeof(qos)))
-                       return -EFAULT;
+               error = copy_safe_from_sockptr(&qos, sizeof(qos), optval,
+                                              optlen);
+               if (error)
+                       return error;
                error = check_qos(&qos);
                if (error)
                        return error;
@@ -737,8 +739,10 @@ int vcc_setsockopt(struct socket *sock, int level, int optname,
                return 0;
        }
        case SO_SETCLP:
-               if (copy_from_sockptr(&value, optval, sizeof(value)))
-                       return -EFAULT;
+               error = copy_safe_from_sockptr(&value, sizeof(value), optval,
+                                              optlen);
+               if (error)
+                       return error;
                if (value)
                        vcc->atm_options |= ATM_ATMOPT_CLP;
                else