bpf: Add support for forced LINK_DETACH command
Add LINK_DETACH command to force-detach bpf_link without destroying it. It has the same behavior as auto-detaching of bpf_link due to cgroup dying for bpf_cgroup_link or net_device being destroyed for bpf_xdp_link. In such case, bpf_link is still a valid kernel object, but is defuncts and doesn't hold BPF program attached to corresponding BPF hook. This functionality allows users with enough access rights to manually force-detach attached bpf_link without killing respective owner process. This patch implements LINK_DETACH for cgroup, xdp, and netns links, mostly re-using existing link release handling code. Signed-off-by: Andrii Nakryiko <andriin@fb.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Song Liu <songliubraving@fb.com> Acked-by: John Fastabend <john.fastabend@gmail.com> Link: https://lore.kernel.org/bpf/20200731182830.286260-2-andriin@fb.com
This commit is contained in:
parent
4939b2847d
commit
73b11c2ab0
|
@ -793,6 +793,7 @@ struct bpf_link {
|
||||||
struct bpf_link_ops {
|
struct bpf_link_ops {
|
||||||
void (*release)(struct bpf_link *link);
|
void (*release)(struct bpf_link *link);
|
||||||
void (*dealloc)(struct bpf_link *link);
|
void (*dealloc)(struct bpf_link *link);
|
||||||
|
int (*detach)(struct bpf_link *link);
|
||||||
int (*update_prog)(struct bpf_link *link, struct bpf_prog *new_prog,
|
int (*update_prog)(struct bpf_link *link, struct bpf_prog *new_prog,
|
||||||
struct bpf_prog *old_prog);
|
struct bpf_prog *old_prog);
|
||||||
void (*show_fdinfo)(const struct bpf_link *link, struct seq_file *seq);
|
void (*show_fdinfo)(const struct bpf_link *link, struct seq_file *seq);
|
||||||
|
|
|
@ -117,6 +117,7 @@ enum bpf_cmd {
|
||||||
BPF_LINK_GET_NEXT_ID,
|
BPF_LINK_GET_NEXT_ID,
|
||||||
BPF_ENABLE_STATS,
|
BPF_ENABLE_STATS,
|
||||||
BPF_ITER_CREATE,
|
BPF_ITER_CREATE,
|
||||||
|
BPF_LINK_DETACH,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum bpf_map_type {
|
enum bpf_map_type {
|
||||||
|
@ -634,6 +635,10 @@ union bpf_attr {
|
||||||
__u32 old_prog_fd;
|
__u32 old_prog_fd;
|
||||||
} link_update;
|
} link_update;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
__u32 link_fd;
|
||||||
|
} link_detach;
|
||||||
|
|
||||||
struct { /* struct used by BPF_ENABLE_STATS command */
|
struct { /* struct used by BPF_ENABLE_STATS command */
|
||||||
__u32 type;
|
__u32 type;
|
||||||
} enable_stats;
|
} enable_stats;
|
||||||
|
|
|
@ -814,6 +814,7 @@ static void bpf_cgroup_link_release(struct bpf_link *link)
|
||||||
{
|
{
|
||||||
struct bpf_cgroup_link *cg_link =
|
struct bpf_cgroup_link *cg_link =
|
||||||
container_of(link, struct bpf_cgroup_link, link);
|
container_of(link, struct bpf_cgroup_link, link);
|
||||||
|
struct cgroup *cg;
|
||||||
|
|
||||||
/* link might have been auto-detached by dying cgroup already,
|
/* link might have been auto-detached by dying cgroup already,
|
||||||
* in that case our work is done here
|
* in that case our work is done here
|
||||||
|
@ -832,8 +833,12 @@ static void bpf_cgroup_link_release(struct bpf_link *link)
|
||||||
WARN_ON(__cgroup_bpf_detach(cg_link->cgroup, NULL, cg_link,
|
WARN_ON(__cgroup_bpf_detach(cg_link->cgroup, NULL, cg_link,
|
||||||
cg_link->type));
|
cg_link->type));
|
||||||
|
|
||||||
|
cg = cg_link->cgroup;
|
||||||
|
cg_link->cgroup = NULL;
|
||||||
|
|
||||||
mutex_unlock(&cgroup_mutex);
|
mutex_unlock(&cgroup_mutex);
|
||||||
cgroup_put(cg_link->cgroup);
|
|
||||||
|
cgroup_put(cg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bpf_cgroup_link_dealloc(struct bpf_link *link)
|
static void bpf_cgroup_link_dealloc(struct bpf_link *link)
|
||||||
|
@ -844,6 +849,13 @@ static void bpf_cgroup_link_dealloc(struct bpf_link *link)
|
||||||
kfree(cg_link);
|
kfree(cg_link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bpf_cgroup_link_detach(struct bpf_link *link)
|
||||||
|
{
|
||||||
|
bpf_cgroup_link_release(link);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void bpf_cgroup_link_show_fdinfo(const struct bpf_link *link,
|
static void bpf_cgroup_link_show_fdinfo(const struct bpf_link *link,
|
||||||
struct seq_file *seq)
|
struct seq_file *seq)
|
||||||
{
|
{
|
||||||
|
@ -883,6 +895,7 @@ static int bpf_cgroup_link_fill_link_info(const struct bpf_link *link,
|
||||||
static const struct bpf_link_ops bpf_cgroup_link_lops = {
|
static const struct bpf_link_ops bpf_cgroup_link_lops = {
|
||||||
.release = bpf_cgroup_link_release,
|
.release = bpf_cgroup_link_release,
|
||||||
.dealloc = bpf_cgroup_link_dealloc,
|
.dealloc = bpf_cgroup_link_dealloc,
|
||||||
|
.detach = bpf_cgroup_link_detach,
|
||||||
.update_prog = cgroup_bpf_replace,
|
.update_prog = cgroup_bpf_replace,
|
||||||
.show_fdinfo = bpf_cgroup_link_show_fdinfo,
|
.show_fdinfo = bpf_cgroup_link_show_fdinfo,
|
||||||
.fill_link_info = bpf_cgroup_link_fill_link_info,
|
.fill_link_info = bpf_cgroup_link_fill_link_info,
|
||||||
|
|
|
@ -142,9 +142,16 @@ static void bpf_netns_link_release(struct bpf_link *link)
|
||||||
bpf_prog_array_free(old_array);
|
bpf_prog_array_free(old_array);
|
||||||
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
|
net_link->net = NULL;
|
||||||
mutex_unlock(&netns_bpf_mutex);
|
mutex_unlock(&netns_bpf_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bpf_netns_link_detach(struct bpf_link *link)
|
||||||
|
{
|
||||||
|
bpf_netns_link_release(link);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void bpf_netns_link_dealloc(struct bpf_link *link)
|
static void bpf_netns_link_dealloc(struct bpf_link *link)
|
||||||
{
|
{
|
||||||
struct bpf_netns_link *net_link =
|
struct bpf_netns_link *net_link =
|
||||||
|
@ -228,6 +235,7 @@ static void bpf_netns_link_show_fdinfo(const struct bpf_link *link,
|
||||||
static const struct bpf_link_ops bpf_netns_link_ops = {
|
static const struct bpf_link_ops bpf_netns_link_ops = {
|
||||||
.release = bpf_netns_link_release,
|
.release = bpf_netns_link_release,
|
||||||
.dealloc = bpf_netns_link_dealloc,
|
.dealloc = bpf_netns_link_dealloc,
|
||||||
|
.detach = bpf_netns_link_detach,
|
||||||
.update_prog = bpf_netns_link_update_prog,
|
.update_prog = bpf_netns_link_update_prog,
|
||||||
.fill_link_info = bpf_netns_link_fill_info,
|
.fill_link_info = bpf_netns_link_fill_info,
|
||||||
.show_fdinfo = bpf_netns_link_show_fdinfo,
|
.show_fdinfo = bpf_netns_link_show_fdinfo,
|
||||||
|
|
|
@ -3991,6 +3991,29 @@ out_put_link:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define BPF_LINK_DETACH_LAST_FIELD link_detach.link_fd
|
||||||
|
|
||||||
|
static int link_detach(union bpf_attr *attr)
|
||||||
|
{
|
||||||
|
struct bpf_link *link;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (CHECK_ATTR(BPF_LINK_DETACH))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
link = bpf_link_get_from_fd(attr->link_detach.link_fd);
|
||||||
|
if (IS_ERR(link))
|
||||||
|
return PTR_ERR(link);
|
||||||
|
|
||||||
|
if (link->ops->detach)
|
||||||
|
ret = link->ops->detach(link);
|
||||||
|
else
|
||||||
|
ret = -EOPNOTSUPP;
|
||||||
|
|
||||||
|
bpf_link_put(link);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int bpf_link_inc_not_zero(struct bpf_link *link)
|
static int bpf_link_inc_not_zero(struct bpf_link *link)
|
||||||
{
|
{
|
||||||
return atomic64_fetch_add_unless(&link->refcnt, 1, 0) ? 0 : -ENOENT;
|
return atomic64_fetch_add_unless(&link->refcnt, 1, 0) ? 0 : -ENOENT;
|
||||||
|
@ -4240,6 +4263,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
|
||||||
case BPF_ITER_CREATE:
|
case BPF_ITER_CREATE:
|
||||||
err = bpf_iter_create(&attr);
|
err = bpf_iter_create(&attr);
|
||||||
break;
|
break;
|
||||||
|
case BPF_LINK_DETACH:
|
||||||
|
err = link_detach(&attr);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -8979,12 +8979,20 @@ static void bpf_xdp_link_release(struct bpf_link *link)
|
||||||
/* if racing with net_device's tear down, xdp_link->dev might be
|
/* if racing with net_device's tear down, xdp_link->dev might be
|
||||||
* already NULL, in which case link was already auto-detached
|
* already NULL, in which case link was already auto-detached
|
||||||
*/
|
*/
|
||||||
if (xdp_link->dev)
|
if (xdp_link->dev) {
|
||||||
WARN_ON(dev_xdp_detach_link(xdp_link->dev, NULL, xdp_link));
|
WARN_ON(dev_xdp_detach_link(xdp_link->dev, NULL, xdp_link));
|
||||||
|
xdp_link->dev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
rtnl_unlock();
|
rtnl_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bpf_xdp_link_detach(struct bpf_link *link)
|
||||||
|
{
|
||||||
|
bpf_xdp_link_release(link);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void bpf_xdp_link_dealloc(struct bpf_link *link)
|
static void bpf_xdp_link_dealloc(struct bpf_link *link)
|
||||||
{
|
{
|
||||||
struct bpf_xdp_link *xdp_link = container_of(link, struct bpf_xdp_link, link);
|
struct bpf_xdp_link *xdp_link = container_of(link, struct bpf_xdp_link, link);
|
||||||
|
@ -9066,6 +9074,7 @@ out_unlock:
|
||||||
static const struct bpf_link_ops bpf_xdp_link_lops = {
|
static const struct bpf_link_ops bpf_xdp_link_lops = {
|
||||||
.release = bpf_xdp_link_release,
|
.release = bpf_xdp_link_release,
|
||||||
.dealloc = bpf_xdp_link_dealloc,
|
.dealloc = bpf_xdp_link_dealloc,
|
||||||
|
.detach = bpf_xdp_link_detach,
|
||||||
.show_fdinfo = bpf_xdp_link_show_fdinfo,
|
.show_fdinfo = bpf_xdp_link_show_fdinfo,
|
||||||
.fill_link_info = bpf_xdp_link_fill_link_info,
|
.fill_link_info = bpf_xdp_link_fill_link_info,
|
||||||
.update_prog = bpf_xdp_link_update,
|
.update_prog = bpf_xdp_link_update,
|
||||||
|
|
Loading…
Reference in New Issue