aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorDaniel Borkmann <daniel@iogearbox.net>2017-09-07 18:14:51 -0400
committerDavid S. Miller <davem@davemloft.net>2017-09-08 23:58:09 -0400
commit109980b894e9dae66c37c3d804a415aa68b19c7e (patch)
treea97e0a878b044fc370a1d990ecfcefd36eb469b4 /net
parent9a486c9dc515f7daa05a344621031ee09645c522 (diff)
bpf: don't select potentially stale ri->map from buggy xdp progs
We can potentially run into a couple of issues with the XDP bpf_redirect_map() helper. The ri->map in the per CPU storage can become stale in several ways, mostly due to misuse, where we can then trigger a use after free on the map: i) prog A is calling bpf_redirect_map(), returning XDP_REDIRECT and running on a driver not supporting XDP_REDIRECT yet. The ri->map on that CPU becomes stale when the XDP program is unloaded on the driver, and a prog B loaded on a different driver which supports XDP_REDIRECT return code. prog B would have to omit calling to bpf_redirect_map() and just return XDP_REDIRECT, which would then access the freed map in xdp_do_redirect() since not cleared for that CPU. ii) prog A is calling bpf_redirect_map(), returning a code other than XDP_REDIRECT. prog A is then detached, which triggers release of the map. prog B is attached which, similarly as in i), would just return XDP_REDIRECT without having called bpf_redirect_map() and thus be accessing the freed map in xdp_do_redirect() since not cleared for that CPU. iii) prog A is attached to generic XDP, calling the bpf_redirect_map() helper and returning XDP_REDIRECT. xdp_do_generic_redirect() is currently not handling ri->map (will be fixed by Jesper), so it's not being reset. Later loading a e.g. native prog B which would, say, call bpf_xdp_redirect() and then returns XDP_REDIRECT would find in xdp_do_redirect() that a map was set and uses that causing use after free on map access. Fix thus needs to avoid accessing stale ri->map pointers, naive way would be to call a BPF function from drivers that just resets it to NULL for all XDP return codes but XDP_REDIRECT and including XDP_REDIRECT for drivers not supporting it yet (and let ri->map being handled in xdp_do_generic_redirect()). There is a less intrusive way w/o letting drivers call a reset for each BPF run. The verifier knows we're calling into bpf_xdp_redirect_map() helper, so it can do a small insn rewrite transparent to the prog itself in the sense that it fills R4 with a pointer to the own bpf_prog. We have that pointer at verification time anyway and R4 is allowed to be used as per calling convention we scratch R0 to R5 anyway, so they become inaccessible and program cannot read them prior to a write. Then, the helper would store the prog pointer in the current CPUs struct redirect_info. Later in xdp_do_*_redirect() we check whether the redirect_info's prog pointer is the same as passed xdp_prog pointer, and if that's the case then all good, since the prog holds a ref on the map anyway, so it is always valid at that point in time and must have a reference count of at least 1. If in the unlikely case they are not equal, it means we got a stale pointer, so we clear and bail out right there. Also do reset map and the owning prog in bpf_xdp_redirect(), so that bpf_xdp_redirect_map() and bpf_xdp_redirect() won't get mixed up, only the last call should take precedence. A tc bpf_redirect() doesn't use map anywhere yet, so no need to clear it there since never accessed in that layer. Note that in case the prog is released, and thus the map as well we're still under RCU read critical section at that time and have preemption disabled as well. Once we commit with the __dev_map_insert_ctx() from xdp_do_redirect_map() and set the map to ri->map_to_flush, we still wait for a xdp_do_flush_map() to finish in devmap dismantle time once flush_needed bit is set, so that is fine. Fixes: 97f91a7cf04f ("bpf: add bpf_redirect_map helper routine") Reported-by: Jesper Dangaard Brouer <brouer@redhat.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: John Fastabend <john.fastabend@gmail.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/core/filter.c21
1 files changed, 19 insertions, 2 deletions
diff --git a/net/core/filter.c b/net/core/filter.c
index 5912c738a7b2..0848df2cd9bf 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -1794,6 +1794,7 @@ struct redirect_info {
1794 u32 flags; 1794 u32 flags;
1795 struct bpf_map *map; 1795 struct bpf_map *map;
1796 struct bpf_map *map_to_flush; 1796 struct bpf_map *map_to_flush;
1797 const struct bpf_prog *map_owner;
1797}; 1798};
1798 1799
1799static DEFINE_PER_CPU(struct redirect_info, redirect_info); 1800static DEFINE_PER_CPU(struct redirect_info, redirect_info);
@@ -1807,7 +1808,6 @@ BPF_CALL_2(bpf_redirect, u32, ifindex, u64, flags)
1807 1808
1808 ri->ifindex = ifindex; 1809 ri->ifindex = ifindex;
1809 ri->flags = flags; 1810 ri->flags = flags;
1810 ri->map = NULL;
1811 1811
1812 return TC_ACT_REDIRECT; 1812 return TC_ACT_REDIRECT;
1813} 1813}
@@ -2504,6 +2504,7 @@ static int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp,
2504 struct bpf_prog *xdp_prog) 2504 struct bpf_prog *xdp_prog)
2505{ 2505{
2506 struct redirect_info *ri = this_cpu_ptr(&redirect_info); 2506 struct redirect_info *ri = this_cpu_ptr(&redirect_info);
2507 const struct bpf_prog *map_owner = ri->map_owner;
2507 struct bpf_map *map = ri->map; 2508 struct bpf_map *map = ri->map;
2508 u32 index = ri->ifindex; 2509 u32 index = ri->ifindex;
2509 struct net_device *fwd; 2510 struct net_device *fwd;
@@ -2511,6 +2512,15 @@ static int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp,
2511 2512
2512 ri->ifindex = 0; 2513 ri->ifindex = 0;
2513 ri->map = NULL; 2514 ri->map = NULL;
2515 ri->map_owner = NULL;
2516
2517 /* This is really only caused by a deliberately crappy
2518 * BPF program, normally we would never hit that case,
2519 * so no need to inform someone via tracepoints either,
2520 * just bail out.
2521 */
2522 if (unlikely(map_owner != xdp_prog))
2523 return -EINVAL;
2514 2524
2515 fwd = __dev_map_lookup_elem(map, index); 2525 fwd = __dev_map_lookup_elem(map, index);
2516 if (!fwd) { 2526 if (!fwd) {
@@ -2607,6 +2617,8 @@ BPF_CALL_2(bpf_xdp_redirect, u32, ifindex, u64, flags)
2607 2617
2608 ri->ifindex = ifindex; 2618 ri->ifindex = ifindex;
2609 ri->flags = flags; 2619 ri->flags = flags;
2620 ri->map = NULL;
2621 ri->map_owner = NULL;
2610 2622
2611 return XDP_REDIRECT; 2623 return XDP_REDIRECT;
2612} 2624}
@@ -2619,7 +2631,8 @@ static const struct bpf_func_proto bpf_xdp_redirect_proto = {
2619 .arg2_type = ARG_ANYTHING, 2631 .arg2_type = ARG_ANYTHING,
2620}; 2632};
2621 2633
2622BPF_CALL_3(bpf_xdp_redirect_map, struct bpf_map *, map, u32, ifindex, u64, flags) 2634BPF_CALL_4(bpf_xdp_redirect_map, struct bpf_map *, map, u32, ifindex, u64, flags,
2635 const struct bpf_prog *, map_owner)
2623{ 2636{
2624 struct redirect_info *ri = this_cpu_ptr(&redirect_info); 2637 struct redirect_info *ri = this_cpu_ptr(&redirect_info);
2625 2638
@@ -2629,10 +2642,14 @@ BPF_CALL_3(bpf_xdp_redirect_map, struct bpf_map *, map, u32, ifindex, u64, flags
2629 ri->ifindex = ifindex; 2642 ri->ifindex = ifindex;
2630 ri->flags = flags; 2643 ri->flags = flags;
2631 ri->map = map; 2644 ri->map = map;
2645 ri->map_owner = map_owner;
2632 2646
2633 return XDP_REDIRECT; 2647 return XDP_REDIRECT;
2634} 2648}
2635 2649
2650/* Note, arg4 is hidden from users and populated by the verifier
2651 * with the right pointer.
2652 */
2636static const struct bpf_func_proto bpf_xdp_redirect_map_proto = { 2653static const struct bpf_func_proto bpf_xdp_redirect_map_proto = {
2637 .func = bpf_xdp_redirect_map, 2654 .func = bpf_xdp_redirect_map,
2638 .gpl_only = false, 2655 .gpl_only = false,