Skip to content

Commit 4c46091

Browse files
tstrukanakryiko
authored andcommitted
bpf: Fix KASAN use-after-free Read in compute_effective_progs
Syzbot found a Use After Free bug in compute_effective_progs(). The reproducer creates a number of BPF links, and causes a fault injected alloc to fail, while calling bpf_link_detach on them. Link detach triggers the link to be freed by bpf_link_free(), which calls __cgroup_bpf_detach() and update_effective_progs(). If the memory allocation in this function fails, the function restores the pointer to the bpf_cgroup_link on the cgroup list, but the memory gets freed just after it returns. After this, every subsequent call to update_effective_progs() causes this already deallocated pointer to be dereferenced in prog_list_length(), and triggers KASAN UAF error. To fix this issue don't preserve the pointer to the prog or link in the list, but remove it and replace it with a dummy prog without shrinking the table. The subsequent call to __cgroup_bpf_detach() or __cgroup_bpf_detach() will correct it. Fixes: af6eea5 ("bpf: Implement bpf_link-based cgroup BPF program attachment") Reported-by: <[email protected]> Signed-off-by: Tadeusz Struk <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Cc: <[email protected]> Link: https://syzkaller.appspot.com/bug?id=8ebf179a95c2a2670f7cf1ba62429ec044369db4 Link: https://lore.kernel.org/bpf/[email protected]
1 parent de4b4b9 commit 4c46091

File tree

1 file changed

+60
-10
lines changed

1 file changed

+60
-10
lines changed

kernel/bpf/cgroup.c

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,60 @@ static struct bpf_prog_list *find_detach_entry(struct list_head *progs,
720720
return ERR_PTR(-ENOENT);
721721
}
722722

723+
/**
724+
* purge_effective_progs() - After compute_effective_progs fails to alloc new
725+
* cgrp->bpf.inactive table we can recover by
726+
* recomputing the array in place.
727+
*
728+
* @cgrp: The cgroup which descendants to travers
729+
* @prog: A program to detach or NULL
730+
* @link: A link to detach or NULL
731+
* @atype: Type of detach operation
732+
*/
733+
static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog *prog,
734+
struct bpf_cgroup_link *link,
735+
enum cgroup_bpf_attach_type atype)
736+
{
737+
struct cgroup_subsys_state *css;
738+
struct bpf_prog_array *progs;
739+
struct bpf_prog_list *pl;
740+
struct list_head *head;
741+
struct cgroup *cg;
742+
int pos;
743+
744+
/* recompute effective prog array in place */
745+
css_for_each_descendant_pre(css, &cgrp->self) {
746+
struct cgroup *desc = container_of(css, struct cgroup, self);
747+
748+
if (percpu_ref_is_zero(&desc->bpf.refcnt))
749+
continue;
750+
751+
/* find position of link or prog in effective progs array */
752+
for (pos = 0, cg = desc; cg; cg = cgroup_parent(cg)) {
753+
if (pos && !(cg->bpf.flags[atype] & BPF_F_ALLOW_MULTI))
754+
continue;
755+
756+
head = &cg->bpf.progs[atype];
757+
list_for_each_entry(pl, head, node) {
758+
if (!prog_list_prog(pl))
759+
continue;
760+
if (pl->prog == prog && pl->link == link)
761+
goto found;
762+
pos++;
763+
}
764+
}
765+
found:
766+
BUG_ON(!cg);
767+
progs = rcu_dereference_protected(
768+
desc->bpf.effective[atype],
769+
lockdep_is_held(&cgroup_mutex));
770+
771+
/* Remove the program from the array */
772+
WARN_ONCE(bpf_prog_array_delete_safe_at(progs, pos),
773+
"Failed to purge a prog from array at index %d", pos);
774+
}
775+
}
776+
723777
/**
724778
* __cgroup_bpf_detach() - Detach the program or link from a cgroup, and
725779
* propagate the change to descendants
@@ -739,7 +793,6 @@ static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
739793
struct bpf_prog_list *pl;
740794
struct list_head *progs;
741795
u32 flags;
742-
int err;
743796

744797
atype = to_cgroup_bpf_attach_type(type);
745798
if (atype < 0)
@@ -761,9 +814,12 @@ static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
761814
pl->prog = NULL;
762815
pl->link = NULL;
763816

764-
err = update_effective_progs(cgrp, atype);
765-
if (err)
766-
goto cleanup;
817+
if (update_effective_progs(cgrp, atype)) {
818+
/* if update effective array failed replace the prog with a dummy prog*/
819+
pl->prog = old_prog;
820+
pl->link = link;
821+
purge_effective_progs(cgrp, old_prog, link, atype);
822+
}
767823

768824
/* now can actually delete it from this cgroup list */
769825
list_del(&pl->node);
@@ -775,12 +831,6 @@ static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
775831
bpf_prog_put(old_prog);
776832
static_branch_dec(&cgroup_bpf_enabled_key[atype]);
777833
return 0;
778-
779-
cleanup:
780-
/* restore back prog or link */
781-
pl->prog = old_prog;
782-
pl->link = link;
783-
return err;
784834
}
785835

786836
static int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,

0 commit comments

Comments
 (0)