Attach gets the cgroup from an fd before it takes cgroup_mutex. If the cgroup is removed in between and cgroup_bpf_release() runs first, attach adds a program that never runs: - With BPF_PROG_ATTACH the program leaks, since nothing can detach it. - With a link that uses cgroup storage, the storage stays on the freed cgroup's list, and freeing the map later writes to the freed cgroup. - A cgroup struct_ops link is attached but never called. The window is small today because the release work also waits for cgroup_mutex. The next patch stops that, which makes the race easy to hit. Check cgroup_is_dead() under the lock and return -ENOENT, the error the fd lookup gives for a removed cgroup. rmdir marks the cgroup dead under cgroup_mutex before it starts the release, so the check is exact. Reproduced in a VM with debug-only delays. With the next patch applied, a link attach queued behind a slow mkdir lands on the released cgroup, and freeing the map then hits a KASAN use-after-free in cgroup_storage_map_free(). BPF_PROG_ATTACH leaks the program. With this patch, attach returns -ENOENT. Fixes: 4bfc0bb2c60e ("bpf: decouple the lifetime of cgroup_bpf from cgroup itself") Fixes: 369d9dcd8fb8 ("bpf: Add infrastructure to support attaching struct_ops to cgroups") Signed-off-by: Shakeel Butt --- kernel/bpf/cgroup.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/kernel/bpf/cgroup.c b/kernel/bpf/cgroup.c index 9258c2669fa8..334237096759 100644 --- a/kernel/bpf/cgroup.c +++ b/kernel/bpf/cgroup.c @@ -988,6 +988,13 @@ static int __cgroup_bpf_attach(struct cgroup *cgrp, /* replace_prog implies BPF_F_REPLACE, and vice versa */ return -EINVAL; + /* + * The cgroup may have been removed since the caller looked it up. + * Then its bpf state may be freed, and nothing would detach this. + */ + if (cgroup_is_dead(cgrp)) + return -ENOENT; + atype = bpf_cgroup_atype_find(type, new_prog->aux->attach_btf_id); if (atype < 0) return -EINVAL; @@ -3170,6 +3177,12 @@ int cgroup_bpf_struct_ops_attach(struct bpf_map *map, const union bpf_attr *attr cgroup_lock(); + /* See the cgroup_is_dead() check in __cgroup_bpf_attach() */ + if (cgroup_is_dead(cgrp)) { + err = -ENOENT; + goto unlock; + } + if (attr->link_create.cgroup.expected_revision && attr->link_create.cgroup.expected_revision != cgrp->bpf.revisions[atype]) { err = -ESTALE; -- 2.53.0-Meta