net: Fix data-races around sysctl_optmem_max.
[ Upstream commit 7de6d09f51917c829af2b835aba8bb5040f8e86a ]
While reading sysctl_optmem_max, it can be changed concurrently.
Thus, we need to add READ_ONCE() to its readers.
Fixes: 1da177e4c3
("Linux-2.6.12-rc2")
Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
27e8ade792
commit
ed48223f87
@@ -304,11 +304,12 @@ BPF_CALL_2(bpf_sk_storage_delete, struct bpf_map *, map, struct sock *, sk)
|
|||||||
static int sk_storage_charge(struct bpf_local_storage_map *smap,
|
static int sk_storage_charge(struct bpf_local_storage_map *smap,
|
||||||
void *owner, u32 size)
|
void *owner, u32 size)
|
||||||
{
|
{
|
||||||
|
int optmem_max = READ_ONCE(sysctl_optmem_max);
|
||||||
struct sock *sk = (struct sock *)owner;
|
struct sock *sk = (struct sock *)owner;
|
||||||
|
|
||||||
/* same check as in sock_kmalloc() */
|
/* same check as in sock_kmalloc() */
|
||||||
if (size <= sysctl_optmem_max &&
|
if (size <= optmem_max &&
|
||||||
atomic_read(&sk->sk_omem_alloc) + size < sysctl_optmem_max) {
|
atomic_read(&sk->sk_omem_alloc) + size < optmem_max) {
|
||||||
atomic_add(size, &sk->sk_omem_alloc);
|
atomic_add(size, &sk->sk_omem_alloc);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -1212,10 +1212,11 @@ void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp)
|
|||||||
static bool __sk_filter_charge(struct sock *sk, struct sk_filter *fp)
|
static bool __sk_filter_charge(struct sock *sk, struct sk_filter *fp)
|
||||||
{
|
{
|
||||||
u32 filter_size = bpf_prog_size(fp->prog->len);
|
u32 filter_size = bpf_prog_size(fp->prog->len);
|
||||||
|
int optmem_max = READ_ONCE(sysctl_optmem_max);
|
||||||
|
|
||||||
/* same check as in sock_kmalloc() */
|
/* same check as in sock_kmalloc() */
|
||||||
if (filter_size <= sysctl_optmem_max &&
|
if (filter_size <= optmem_max &&
|
||||||
atomic_read(&sk->sk_omem_alloc) + filter_size < sysctl_optmem_max) {
|
atomic_read(&sk->sk_omem_alloc) + filter_size < optmem_max) {
|
||||||
atomic_add(filter_size, &sk->sk_omem_alloc);
|
atomic_add(filter_size, &sk->sk_omem_alloc);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -1547,7 +1548,7 @@ int sk_reuseport_attach_filter(struct sock_fprog *fprog, struct sock *sk)
|
|||||||
if (IS_ERR(prog))
|
if (IS_ERR(prog))
|
||||||
return PTR_ERR(prog);
|
return PTR_ERR(prog);
|
||||||
|
|
||||||
if (bpf_prog_size(prog->len) > sysctl_optmem_max)
|
if (bpf_prog_size(prog->len) > READ_ONCE(sysctl_optmem_max))
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
else
|
else
|
||||||
err = reuseport_attach_prog(sk, prog);
|
err = reuseport_attach_prog(sk, prog);
|
||||||
@@ -1614,7 +1615,7 @@ int sk_reuseport_attach_bpf(u32 ufd, struct sock *sk)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* BPF_PROG_TYPE_SOCKET_FILTER */
|
/* BPF_PROG_TYPE_SOCKET_FILTER */
|
||||||
if (bpf_prog_size(prog->len) > sysctl_optmem_max) {
|
if (bpf_prog_size(prog->len) > READ_ONCE(sysctl_optmem_max)) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
goto err_prog_put;
|
goto err_prog_put;
|
||||||
}
|
}
|
||||||
|
@@ -2219,7 +2219,7 @@ struct sk_buff *sock_omalloc(struct sock *sk, unsigned long size,
|
|||||||
|
|
||||||
/* small safe race: SKB_TRUESIZE may differ from final skb->truesize */
|
/* small safe race: SKB_TRUESIZE may differ from final skb->truesize */
|
||||||
if (atomic_read(&sk->sk_omem_alloc) + SKB_TRUESIZE(size) >
|
if (atomic_read(&sk->sk_omem_alloc) + SKB_TRUESIZE(size) >
|
||||||
sysctl_optmem_max)
|
READ_ONCE(sysctl_optmem_max))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
skb = alloc_skb(size, priority);
|
skb = alloc_skb(size, priority);
|
||||||
@@ -2237,8 +2237,10 @@ struct sk_buff *sock_omalloc(struct sock *sk, unsigned long size,
|
|||||||
*/
|
*/
|
||||||
void *sock_kmalloc(struct sock *sk, int size, gfp_t priority)
|
void *sock_kmalloc(struct sock *sk, int size, gfp_t priority)
|
||||||
{
|
{
|
||||||
if ((unsigned int)size <= sysctl_optmem_max &&
|
int optmem_max = READ_ONCE(sysctl_optmem_max);
|
||||||
atomic_read(&sk->sk_omem_alloc) + size < sysctl_optmem_max) {
|
|
||||||
|
if ((unsigned int)size <= optmem_max &&
|
||||||
|
atomic_read(&sk->sk_omem_alloc) + size < optmem_max) {
|
||||||
void *mem;
|
void *mem;
|
||||||
/* First do the add, to avoid the race if kmalloc
|
/* First do the add, to avoid the race if kmalloc
|
||||||
* might sleep.
|
* might sleep.
|
||||||
|
@@ -773,7 +773,7 @@ static int ip_set_mcast_msfilter(struct sock *sk, sockptr_t optval, int optlen)
|
|||||||
|
|
||||||
if (optlen < GROUP_FILTER_SIZE(0))
|
if (optlen < GROUP_FILTER_SIZE(0))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (optlen > sysctl_optmem_max)
|
if (optlen > READ_ONCE(sysctl_optmem_max))
|
||||||
return -ENOBUFS;
|
return -ENOBUFS;
|
||||||
|
|
||||||
gsf = memdup_sockptr(optval, optlen);
|
gsf = memdup_sockptr(optval, optlen);
|
||||||
@@ -808,7 +808,7 @@ static int compat_ip_set_mcast_msfilter(struct sock *sk, sockptr_t optval,
|
|||||||
|
|
||||||
if (optlen < size0)
|
if (optlen < size0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (optlen > sysctl_optmem_max - 4)
|
if (optlen > READ_ONCE(sysctl_optmem_max) - 4)
|
||||||
return -ENOBUFS;
|
return -ENOBUFS;
|
||||||
|
|
||||||
p = kmalloc(optlen + 4, GFP_KERNEL);
|
p = kmalloc(optlen + 4, GFP_KERNEL);
|
||||||
@@ -1231,7 +1231,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, int optname,
|
|||||||
|
|
||||||
if (optlen < IP_MSFILTER_SIZE(0))
|
if (optlen < IP_MSFILTER_SIZE(0))
|
||||||
goto e_inval;
|
goto e_inval;
|
||||||
if (optlen > sysctl_optmem_max) {
|
if (optlen > READ_ONCE(sysctl_optmem_max)) {
|
||||||
err = -ENOBUFS;
|
err = -ENOBUFS;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -208,7 +208,7 @@ static int ipv6_set_mcast_msfilter(struct sock *sk, sockptr_t optval,
|
|||||||
|
|
||||||
if (optlen < GROUP_FILTER_SIZE(0))
|
if (optlen < GROUP_FILTER_SIZE(0))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (optlen > sysctl_optmem_max)
|
if (optlen > READ_ONCE(sysctl_optmem_max))
|
||||||
return -ENOBUFS;
|
return -ENOBUFS;
|
||||||
|
|
||||||
gsf = memdup_sockptr(optval, optlen);
|
gsf = memdup_sockptr(optval, optlen);
|
||||||
@@ -242,7 +242,7 @@ static int compat_ipv6_set_mcast_msfilter(struct sock *sk, sockptr_t optval,
|
|||||||
|
|
||||||
if (optlen < size0)
|
if (optlen < size0)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (optlen > sysctl_optmem_max - 4)
|
if (optlen > READ_ONCE(sysctl_optmem_max) - 4)
|
||||||
return -ENOBUFS;
|
return -ENOBUFS;
|
||||||
|
|
||||||
p = kmalloc(optlen + 4, GFP_KERNEL);
|
p = kmalloc(optlen + 4, GFP_KERNEL);
|
||||||
|
Reference in New Issue
Block a user