[bpf] bpf: fix leak in LINK_UPDATE and enforce empty old_prog_fd

Message ID 20200424035039.3534080-1-andriin@fb.com
State New
Headers show
Series
  • [bpf] bpf: fix leak in LINK_UPDATE and enforce empty old_prog_fd
Related show

Commit Message

Andrii Nakryiko April 24, 2020, 3:50 a.m.
Fix bug of not putting bpf_link in LINK_UPDATE command.
Also enforce zeroed old_prog_fd if no BPF_F_REPLACE flag is specified.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
---
 kernel/bpf/syscall.c | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

Patch

diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index d85f37239540..087cf27218c9 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -3608,7 +3608,7 @@  static int link_create(union bpf_attr *attr)
 
 static int link_update(union bpf_attr *attr)
 {
-	struct bpf_prog *old_prog = NULL, *new_prog;
+	struct bpf_prog *old_prog = NULL, *new_prog = NULL;
 	struct bpf_link *link;
 	u32 flags;
 	int ret;
@@ -3628,31 +3628,38 @@  static int link_update(union bpf_attr *attr)
 		return PTR_ERR(link);
 
 	new_prog = bpf_prog_get(attr->link_update.new_prog_fd);
-	if (IS_ERR(new_prog))
-		return PTR_ERR(new_prog);
+	if (IS_ERR(new_prog)) {
+		ret = PTR_ERR(new_prog);
+		new_prog = NULL;
+		goto out_put;
+	}
 
 	if (flags & BPF_F_REPLACE) {
 		old_prog = bpf_prog_get(attr->link_update.old_prog_fd);
 		if (IS_ERR(old_prog)) {
 			ret = PTR_ERR(old_prog);
 			old_prog = NULL;
-			goto out_put_progs;
+			goto out_put;
 		}
+	} else if (attr->link_update.old_prog_fd) {
+		ret = -EINVAL;
+		goto out_put;
 	}
 
 #ifdef CONFIG_CGROUP_BPF
 	if (link->ops == &bpf_cgroup_link_lops) {
 		ret = cgroup_bpf_replace(link, old_prog, new_prog);
-		goto out_put_progs;
+		goto out_put;
 	}
 #endif
 	ret = -EINVAL;
 
-out_put_progs:
+out_put:
 	if (old_prog)
 		bpf_prog_put(old_prog);
-	if (ret)
+	if (ret && new_prog)
 		bpf_prog_put(new_prog);
+	bpf_link_put(link);
 	return ret;
 }