Skip to content

Commit f4eaed2

Browse files
borkmanndavem330
authored andcommitted
act_bpf: fix memory leaks when replacing bpf programs
We currently trigger multiple memory leaks when replacing bpf actions, besides others: comm "tc", pid 1909, jiffies 4294851310 (age 1602.796s) hex dump (first 32 bytes): 01 00 00 00 03 00 00 00 00 00 00 00 00 00 00 00 ................ 18 b0 98 6d 00 88 ff ff 00 00 00 00 00 00 00 00 ...m............ backtrace: [<ffffffff817e623e>] kmemleak_alloc+0x4e/0xb0 [<ffffffff8120a22d>] __vmalloc_node_range+0x1bd/0x2c0 [<ffffffff8120a37a>] __vmalloc+0x4a/0x50 [<ffffffff811a8d0a>] bpf_prog_alloc+0x3a/0xa0 [<ffffffff816c0684>] bpf_prog_create+0x44/0xa0 [<ffffffffa09ba4eb>] tcf_bpf_init+0x28b/0x3c0 [act_bpf] [<ffffffff816d7001>] tcf_action_init_1+0x191/0x1b0 [<ffffffff816d70a2>] tcf_action_init+0x82/0xf0 [<ffffffff816d4d12>] tcf_exts_validate+0xb2/0xc0 [<ffffffffa09b5838>] cls_bpf_modify_existing+0x98/0x340 [cls_bpf] [<ffffffffa09b5cd6>] cls_bpf_change+0x1a6/0x274 [cls_bpf] [<ffffffff816d56e5>] tc_ctl_tfilter+0x335/0x910 [<ffffffff816b9145>] rtnetlink_rcv_msg+0x95/0x240 [<ffffffff816df34f>] netlink_rcv_skb+0xaf/0xc0 [<ffffffff816b909e>] rtnetlink_rcv+0x2e/0x40 [<ffffffff816deaaf>] netlink_unicast+0xef/0x1b0 Issue is that the old content from tcf_bpf is allocated and needs to be released when we replace it. We seem to do that since the beginning of act_bpf on the filter and insns, later on the name as well. Example test case, after patch: # FOO="1,6 0 0 4294967295," # BAR="1,6 0 0 4294967294," # tc actions add action bpf bytecode "$FOO" index 2 # tc actions show action bpf action order 0: bpf bytecode '1,6 0 0 4294967295' default-action pipe index 2 ref 1 bind 0 # tc actions replace action bpf bytecode "$BAR" index 2 # tc actions show action bpf action order 0: bpf bytecode '1,6 0 0 4294967294' default-action pipe index 2 ref 1 bind 0 # tc actions replace action bpf bytecode "$FOO" index 2 # tc actions show action bpf action order 0: bpf bytecode '1,6 0 0 4294967295' default-action pipe index 2 ref 1 bind 0 # tc actions del action bpf index 2 [...] # echo "scan" > /sys/kernel/debug/kmemleak # cat /sys/kernel/debug/kmemleak | grep "comm \"tc\"" | wc -l 0 Fixes: d23b8ad ("tc: add BPF based action") Signed-off-by: Daniel Borkmann <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent f68b123 commit f4eaed2

File tree

1 file changed

+35
-18
lines changed

1 file changed

+35
-18
lines changed

net/sched/act_bpf.c

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@
2727
struct tcf_bpf_cfg {
2828
struct bpf_prog *filter;
2929
struct sock_filter *bpf_ops;
30-
char *bpf_name;
30+
const char *bpf_name;
3131
u32 bpf_fd;
3232
u16 bpf_num_ops;
33+
bool is_ebpf;
3334
};
3435

3536
static int tcf_bpf(struct sk_buff *skb, const struct tc_action *act,
@@ -207,6 +208,7 @@ static int tcf_bpf_init_from_ops(struct nlattr **tb, struct tcf_bpf_cfg *cfg)
207208
cfg->bpf_ops = bpf_ops;
208209
cfg->bpf_num_ops = bpf_num_ops;
209210
cfg->filter = fp;
211+
cfg->is_ebpf = false;
210212

211213
return 0;
212214
}
@@ -241,18 +243,40 @@ static int tcf_bpf_init_from_efd(struct nlattr **tb, struct tcf_bpf_cfg *cfg)
241243
cfg->bpf_fd = bpf_fd;
242244
cfg->bpf_name = name;
243245
cfg->filter = fp;
246+
cfg->is_ebpf = true;
244247

245248
return 0;
246249
}
247250

251+
static void tcf_bpf_cfg_cleanup(const struct tcf_bpf_cfg *cfg)
252+
{
253+
if (cfg->is_ebpf)
254+
bpf_prog_put(cfg->filter);
255+
else
256+
bpf_prog_destroy(cfg->filter);
257+
258+
kfree(cfg->bpf_ops);
259+
kfree(cfg->bpf_name);
260+
}
261+
262+
static void tcf_bpf_prog_fill_cfg(const struct tcf_bpf *prog,
263+
struct tcf_bpf_cfg *cfg)
264+
{
265+
cfg->is_ebpf = tcf_bpf_is_ebpf(prog);
266+
cfg->filter = prog->filter;
267+
268+
cfg->bpf_ops = prog->bpf_ops;
269+
cfg->bpf_name = prog->bpf_name;
270+
}
271+
248272
static int tcf_bpf_init(struct net *net, struct nlattr *nla,
249273
struct nlattr *est, struct tc_action *act,
250274
int replace, int bind)
251275
{
252276
struct nlattr *tb[TCA_ACT_BPF_MAX + 1];
277+
struct tcf_bpf_cfg cfg, old;
253278
struct tc_act_bpf *parm;
254279
struct tcf_bpf *prog;
255-
struct tcf_bpf_cfg cfg;
256280
bool is_bpf, is_ebpf;
257281
int ret;
258282

@@ -301,6 +325,9 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
301325
prog = to_bpf(act);
302326
spin_lock_bh(&prog->tcf_lock);
303327

328+
if (ret != ACT_P_CREATED)
329+
tcf_bpf_prog_fill_cfg(prog, &old);
330+
304331
prog->bpf_ops = cfg.bpf_ops;
305332
prog->bpf_name = cfg.bpf_name;
306333

@@ -316,32 +343,22 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla,
316343

317344
if (ret == ACT_P_CREATED)
318345
tcf_hash_insert(act);
346+
else
347+
tcf_bpf_cfg_cleanup(&old);
319348

320349
return ret;
321350

322351
destroy_fp:
323-
if (is_ebpf)
324-
bpf_prog_put(cfg.filter);
325-
else
326-
bpf_prog_destroy(cfg.filter);
327-
328-
kfree(cfg.bpf_ops);
329-
kfree(cfg.bpf_name);
330-
352+
tcf_bpf_cfg_cleanup(&cfg);
331353
return ret;
332354
}
333355

334356
static void tcf_bpf_cleanup(struct tc_action *act, int bind)
335357
{
336-
const struct tcf_bpf *prog = act->priv;
337-
338-
if (tcf_bpf_is_ebpf(prog))
339-
bpf_prog_put(prog->filter);
340-
else
341-
bpf_prog_destroy(prog->filter);
358+
struct tcf_bpf_cfg tmp;
342359

343-
kfree(prog->bpf_ops);
344-
kfree(prog->bpf_name);
360+
tcf_bpf_prog_fill_cfg(act->priv, &tmp);
361+
tcf_bpf_cfg_cleanup(&tmp);
345362
}
346363

347364
static struct tc_action_ops act_bpf_ops __read_mostly = {

0 commit comments

Comments
 (0)