mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	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: af6eea5743 ("bpf: Implement bpf_link-based cgroup BPF program attachment")
Reported-by: <syzbot+f264bffdfbd5614f3bb2@syzkaller.appspotmail.com>
Signed-off-by: Tadeusz Struk <tadeusz.struk@linaro.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Cc: <stable@vger.kernel.org>
Link: https://syzkaller.appspot.com/bug?id=8ebf179a95c2a2670f7cf1ba62429ec044369db4
Link: https://lore.kernel.org/bpf/20220517180420.87954-1-tadeusz.struk@linaro.org
			
			
This commit is contained in:
		
							parent
							
								
									de4b4b94fa
								
							
						
					
					
						commit
						4c46091ee9
					
				
					 1 changed files with 60 additions and 10 deletions
				
			
		| 
						 | 
					@ -720,6 +720,60 @@ static struct bpf_prog_list *find_detach_entry(struct list_head *progs,
 | 
				
			||||||
	return ERR_PTR(-ENOENT);
 | 
						return ERR_PTR(-ENOENT);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * purge_effective_progs() - After compute_effective_progs fails to alloc new
 | 
				
			||||||
 | 
					 *                           cgrp->bpf.inactive table we can recover by
 | 
				
			||||||
 | 
					 *                           recomputing the array in place.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @cgrp: The cgroup which descendants to travers
 | 
				
			||||||
 | 
					 * @prog: A program to detach or NULL
 | 
				
			||||||
 | 
					 * @link: A link to detach or NULL
 | 
				
			||||||
 | 
					 * @atype: Type of detach operation
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog *prog,
 | 
				
			||||||
 | 
									  struct bpf_cgroup_link *link,
 | 
				
			||||||
 | 
									  enum cgroup_bpf_attach_type atype)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct cgroup_subsys_state *css;
 | 
				
			||||||
 | 
						struct bpf_prog_array *progs;
 | 
				
			||||||
 | 
						struct bpf_prog_list *pl;
 | 
				
			||||||
 | 
						struct list_head *head;
 | 
				
			||||||
 | 
						struct cgroup *cg;
 | 
				
			||||||
 | 
						int pos;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* recompute effective prog array in place */
 | 
				
			||||||
 | 
						css_for_each_descendant_pre(css, &cgrp->self) {
 | 
				
			||||||
 | 
							struct cgroup *desc = container_of(css, struct cgroup, self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (percpu_ref_is_zero(&desc->bpf.refcnt))
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* find position of link or prog in effective progs array */
 | 
				
			||||||
 | 
							for (pos = 0, cg = desc; cg; cg = cgroup_parent(cg)) {
 | 
				
			||||||
 | 
								if (pos && !(cg->bpf.flags[atype] & BPF_F_ALLOW_MULTI))
 | 
				
			||||||
 | 
									continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								head = &cg->bpf.progs[atype];
 | 
				
			||||||
 | 
								list_for_each_entry(pl, head, node) {
 | 
				
			||||||
 | 
									if (!prog_list_prog(pl))
 | 
				
			||||||
 | 
										continue;
 | 
				
			||||||
 | 
									if (pl->prog == prog && pl->link == link)
 | 
				
			||||||
 | 
										goto found;
 | 
				
			||||||
 | 
									pos++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					found:
 | 
				
			||||||
 | 
							BUG_ON(!cg);
 | 
				
			||||||
 | 
							progs = rcu_dereference_protected(
 | 
				
			||||||
 | 
									desc->bpf.effective[atype],
 | 
				
			||||||
 | 
									lockdep_is_held(&cgroup_mutex));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Remove the program from the array */
 | 
				
			||||||
 | 
							WARN_ONCE(bpf_prog_array_delete_safe_at(progs, pos),
 | 
				
			||||||
 | 
								  "Failed to purge a prog from array at index %d", pos);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * __cgroup_bpf_detach() - Detach the program or link from a cgroup, and
 | 
					 * __cgroup_bpf_detach() - Detach the program or link from a cgroup, and
 | 
				
			||||||
 *                         propagate the change to descendants
 | 
					 *                         propagate the change to descendants
 | 
				
			||||||
| 
						 | 
					@ -739,7 +793,6 @@ static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
 | 
				
			||||||
	struct bpf_prog_list *pl;
 | 
						struct bpf_prog_list *pl;
 | 
				
			||||||
	struct list_head *progs;
 | 
						struct list_head *progs;
 | 
				
			||||||
	u32 flags;
 | 
						u32 flags;
 | 
				
			||||||
	int err;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	atype = to_cgroup_bpf_attach_type(type);
 | 
						atype = to_cgroup_bpf_attach_type(type);
 | 
				
			||||||
	if (atype < 0)
 | 
						if (atype < 0)
 | 
				
			||||||
| 
						 | 
					@ -761,9 +814,12 @@ static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
 | 
				
			||||||
	pl->prog = NULL;
 | 
						pl->prog = NULL;
 | 
				
			||||||
	pl->link = NULL;
 | 
						pl->link = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	err = update_effective_progs(cgrp, atype);
 | 
						if (update_effective_progs(cgrp, atype)) {
 | 
				
			||||||
	if (err)
 | 
							/* if update effective array failed replace the prog with a dummy prog*/
 | 
				
			||||||
		goto cleanup;
 | 
							pl->prog = old_prog;
 | 
				
			||||||
 | 
							pl->link = link;
 | 
				
			||||||
 | 
							purge_effective_progs(cgrp, old_prog, link, atype);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* now can actually delete it from this cgroup list */
 | 
						/* now can actually delete it from this cgroup list */
 | 
				
			||||||
	list_del(&pl->node);
 | 
						list_del(&pl->node);
 | 
				
			||||||
| 
						 | 
					@ -775,12 +831,6 @@ static int __cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
 | 
				
			||||||
		bpf_prog_put(old_prog);
 | 
							bpf_prog_put(old_prog);
 | 
				
			||||||
	static_branch_dec(&cgroup_bpf_enabled_key[atype]);
 | 
						static_branch_dec(&cgroup_bpf_enabled_key[atype]);
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					 | 
				
			||||||
cleanup:
 | 
					 | 
				
			||||||
	/* restore back prog or link */
 | 
					 | 
				
			||||||
	pl->prog = old_prog;
 | 
					 | 
				
			||||||
	pl->link = link;
 | 
					 | 
				
			||||||
	return err;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
 | 
					static int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue