Skip to content

Commit 1f8ca9c

Browse files
tstrukgregkh
authored andcommitted
bpf: Fix KASAN use-after-free Read in compute_effective_progs
commit 4c46091 upstream. 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] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 27f8f52 commit 1f8ca9c

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
@@ -667,6 +667,60 @@ static struct bpf_prog_list *find_detach_entry(struct list_head *progs,
667667
return ERR_PTR(-ENOENT);
668668
}
669669

670+
/**
671+
* purge_effective_progs() - After compute_effective_progs fails to alloc new
672+
* cgrp->bpf.inactive table we can recover by
673+
* recomputing the array in place.
674+
*
675+
* @cgrp: The cgroup which descendants to travers
676+
* @prog: A program to detach or NULL
677+
* @link: A link to detach or NULL
678+
* @atype: Type of detach operation
679+
*/
680+
static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog *prog,
681+
struct bpf_cgroup_link *link,
682+
enum cgroup_bpf_attach_type atype)
683+
{
684+
struct cgroup_subsys_state *css;
685+
struct bpf_prog_array *progs;
686+
struct bpf_prog_list *pl;
687+
struct list_head *head;
688+
struct cgroup *cg;
689+
int pos;
690+
691+
/* recompute effective prog array in place */
692+
css_for_each_descendant_pre(css, &cgrp->self) {
693+
struct cgroup *desc = container_of(css, struct cgroup, self);
694+
695+
if (percpu_ref_is_zero(&desc->bpf.refcnt))
696+
continue;
697+
698+
/* find position of link or prog in effective progs array */
699+
for (pos = 0, cg = desc; cg; cg = cgroup_parent(cg)) {
700+
if (pos && !(cg->bpf.flags[atype] & BPF_F_ALLOW_MULTI))
701+
continue;
702+
703+
head = &cg->bpf.progs[atype];
704+
list_for_each_entry(pl, head, node) {
705+
if (!prog_list_prog(pl))
706+
continue;
707+
if (pl->prog == prog && pl->link == link)
708+
goto found;
709+
pos++;
710+
}
711+
}
712+
found:
713+
BUG_ON(!cg);
714+
progs = rcu_dereference_protected(
715+
desc->bpf.effective[atype],
716+
lockdep_is_held(&cgroup_mutex));
717+
718+
/* Remove the program from the array */
719+
WARN_ONCE(bpf_prog_array_delete_safe_at(progs, pos),
720+
"Failed to purge a prog from array at index %d", pos);
721+
}
722+
}
723+
670724
/**
671725
* __cgroup_bpf_detach() - Detach the program or link from a cgroup, and
672726
* propagate the change to descendants
@@ -686,7 +740,6 @@ int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
686740
struct bpf_prog_list *pl;
687741
struct list_head *progs;
688742
u32 flags;
689-
int err;
690743

691744
atype = to_cgroup_bpf_attach_type(type);
692745
if (atype < 0)
@@ -708,9 +761,12 @@ int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
708761
pl->prog = NULL;
709762
pl->link = NULL;
710763

711-
err = update_effective_progs(cgrp, atype);
712-
if (err)
713-
goto cleanup;
764+
if (update_effective_progs(cgrp, atype)) {
765+
/* if update effective array failed replace the prog with a dummy prog*/
766+
pl->prog = old_prog;
767+
pl->link = link;
768+
purge_effective_progs(cgrp, old_prog, link, atype);
769+
}
714770

715771
/* now can actually delete it from this cgroup list */
716772
list_del(&pl->node);
@@ -722,12 +778,6 @@ int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
722778
bpf_prog_put(old_prog);
723779
static_branch_dec(&cgroup_bpf_enabled_key[atype]);
724780
return 0;
725-
726-
cleanup:
727-
/* restore back prog or link */
728-
pl->prog = old_prog;
729-
pl->link = link;
730-
return err;
731781
}
732782

733783
/* Must be called with cgroup_mutex held to avoid races. */

0 commit comments

Comments
 (0)