From: Pu Lehui In bpf_mprog_link, the code does not check the link->type first before dereferencing link->prog->type. This missing validation allows a user to pass an abnormal non-netkit or non-tcx link via relative_fd. If doing BPF_LINK_UPDATE on the abnormal link, it can trigger a UAF issue. CPU0 CPU1 netkit_link_prog_attach bpf_mprog_attach bpf_mprog_tuple_relative bpf_mprog_link link = bpf_link_get_from_fd(id_or_fd); BPF_LINK_UPDATE ... old_prog = xchg(&link->link.prog, new_prog); bpf_prog_put(old_prog); if (type && link->prog->type != type) <-- trigger UAF The reason for the UAF is that each subsystem provides its own protection for link->prog. Since there is no cross subsystem protection (if not considering the RCU of prog tear down), dereferencing the prog of an anchor link that does not belong to the current subsystem is not safe: it may have been freed. Therefore, we need to validate link->type to reject foreign anchors. Fix this by strictly validating link->type in bpf_mprog_link against the expected link type. bpf_mprog_tuple_relative is also adjusted to accept and pass down the expected link type. Fixes: 053c8e1f235d ("bpf: Add generic attach/detach/query API for multi-progs") Reported-by: Sashiko Reviewed-by: Amery Hung Signed-off-by: Pu Lehui --- kernel/bpf/mprog.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/kernel/bpf/mprog.c b/kernel/bpf/mprog.c index 1394168062e8..37ce91e0bcd2 100644 --- a/kernel/bpf/mprog.c +++ b/kernel/bpf/mprog.c @@ -6,7 +6,7 @@ static int bpf_mprog_link(struct bpf_tuple *tuple, u32 id_or_fd, u32 flags, - enum bpf_prog_type type) + enum bpf_link_type type) { struct bpf_link *link = ERR_PTR(-EINVAL); bool id = flags & BPF_F_ID; @@ -17,7 +17,7 @@ static int bpf_mprog_link(struct bpf_tuple *tuple, link = bpf_link_get_from_fd(id_or_fd); if (IS_ERR(link)) return PTR_ERR(link); - if (type && link->prog->type != type) { + if (type && link->type != type) { bpf_link_put(link); return -EINVAL; } @@ -52,21 +52,22 @@ static int bpf_mprog_prog(struct bpf_tuple *tuple, static int bpf_mprog_tuple_relative(struct bpf_tuple *tuple, u32 id_or_fd, u32 flags, - enum bpf_prog_type type) + enum bpf_link_type ltype, + enum bpf_prog_type ptype) { bool link = flags & BPF_F_LINK; bool id = flags & BPF_F_ID; memset(tuple, 0, sizeof(*tuple)); if (link) - return bpf_mprog_link(tuple, id_or_fd, flags, type); + return bpf_mprog_link(tuple, id_or_fd, flags, ltype); /* If no relevant flag is set and no id_or_fd was passed, then * tuple link/prog is just NULLed. This is the case when before/ * after selects first/last position without passing fd. */ if (!id && !id_or_fd) return 0; - return bpf_mprog_prog(tuple, id_or_fd, flags, type); + return bpf_mprog_prog(tuple, id_or_fd, flags, ptype); } static void bpf_mprog_tuple_put(struct bpf_tuple *tuple) @@ -243,6 +244,7 @@ int bpf_mprog_attach(struct bpf_mprog_entry *entry, return -EEXIST; ret = bpf_mprog_tuple_relative(&rtuple, id_or_fd, flags & ~BPF_F_REPLACE, + link ? link->type : BPF_LINK_TYPE_UNSPEC, prog_new->type); if (ret) return ret; @@ -343,6 +345,7 @@ int bpf_mprog_detach(struct bpf_mprog_entry *entry, if (!bpf_mprog_total(entry)) return -ENOENT; ret = bpf_mprog_tuple_relative(&rtuple, id_or_fd, flags, + link ? link->type : BPF_LINK_TYPE_UNSPEC, prog ? prog->type : BPF_PROG_TYPE_UNSPEC); if (ret) -- 2.34.1