[PATCH 6.1 529/609] net/atm: fix slab-out-of-bounds read in vcc_setsockopt()
Greg Kroah-Hartman <[email protected]>
| Newsgroups | dev.linux.lists.patches,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
6.1-stable review patch. If anyone has any objections, please let me know. ------------------ From: Eric Dumazet <[email protected]> [ Upstream commit d0c80dbb970439bd2eeb0e5effff8c16a5f4e1e3 ] 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: [email protected] Closes: https://syzkaller.appspot.com/bug?extid=53ecc09fb81df10ef4de Signed-off-by: Eric Dumazet <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Jakub Kicinski <[email protected]> Signed-off-by: Sasha Levin <[email protected]> --- net/atm/common.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/net/atm/common.c b/net/atm/common.c index 96f680a45e306..f3f68c231935a 100644 --- a/net/atm/common.c +++ b/net/atm/common.c @@ -760,7 +760,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)) @@ -772,8 +772,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; @@ -786,8 +788,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 -- 2.53.0