diff options
author | 2023-05-13 16:20:16 -0700 | |
---|---|---|
committer | 2023-05-13 17:00:47 -0700 | |
commit | 79b3604d6221220770fb52619e1f55ee04b2c1b7 (patch) | |
tree | a9447b5600d3d980f8445bb223db5cbab058042b /kernel/bpf | |
parent | libbpf: fix offsetof() and container_of() to work with CO-RE (diff) | |
parent | bpf: Document EFAULT changes for sockopt (diff) | |
download | wireguard-linux-79b3604d6221220770fb52619e1f55ee04b2c1b7.tar.xz wireguard-linux-79b3604d6221220770fb52619e1f55ee04b2c1b7.zip |
Merge branch 'bpf: Don't EFAULT for {g,s}setsockopt with wrong optlen'
Stanislav Fomichev says:
====================
optval larger than PAGE_SIZE leads to EFAULT if the BPF program
isn't careful enough. This is often overlooked and might break
completely unrelated socket options. Instead of EFAULT,
let's ignore BPF program buffer changes. See the first patch for
more info.
In addition, clearly document this corner case and reset optlen
in our selftests (in case somebody copy-pastes from them).
v6:
- no changes; resending due to screwing up v5 series with the unrelated
patch
v5:
- goto in the selftest (Martin)
- set IP_TOS to zero to avoid endianness complications (Martin)
v4:
- ignore retval as well when optlen > PAGE_SIZE (Martin)
v3:
- don't hard-code PAGE_SIZE (Martin)
- reset orig_optlen in getsockopt when kernel part succeeds (Martin)
====================
Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
Diffstat (limited to 'kernel/bpf')
-rw-r--r-- | kernel/bpf/cgroup.c | 15 |
1 files changed, 15 insertions, 0 deletions
diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index a06e118a9be5..14c870595428 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -1826,6 +1826,12 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level, ret = 1; } else if (ctx.optlen > max_optlen || ctx.optlen < -1) { /* optlen is out of bounds */ + if (*optlen > PAGE_SIZE && ctx.optlen >= 0) { + pr_info_once("bpf setsockopt: ignoring program buffer with optlen=%d (max_optlen=%d)\n", + ctx.optlen, max_optlen); + ret = 0; + goto out; + } ret = -EFAULT; } else { /* optlen within bounds, run kernel handler */ @@ -1881,8 +1887,10 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, .optname = optname, .current_task = current, }; + int orig_optlen; int ret; + orig_optlen = max_optlen; ctx.optlen = max_optlen; max_optlen = sockopt_alloc_buf(&ctx, max_optlen, &buf); if (max_optlen < 0) @@ -1905,6 +1913,7 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, ret = -EFAULT; goto out; } + orig_optlen = ctx.optlen; if (copy_from_user(ctx.optval, optval, min(ctx.optlen, max_optlen)) != 0) { @@ -1922,6 +1931,12 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, goto out; if (optval && (ctx.optlen > max_optlen || ctx.optlen < 0)) { + if (orig_optlen > PAGE_SIZE && ctx.optlen >= 0) { + pr_info_once("bpf getsockopt: ignoring program buffer with optlen=%d (max_optlen=%d)\n", + ctx.optlen, max_optlen); + ret = retval; + goto out; + } ret = -EFAULT; goto out; } |