]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
bpf: one perf event close won't free bpf program attached by another perf event
authorYonghong Song <yhs@fb.com>
Mon, 18 Sep 2017 23:38:36 +0000 (16:38 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 12 Oct 2017 09:56:04 +0000 (11:56 +0200)
[ Upstream commit ec9dd352d591f0c90402ec67a317c1ed4fb2e638 ]

This patch fixes a bug exhibited by the following scenario:
  1. fd1 = perf_event_open with attr.config = ID1
  2. attach bpf program prog1 to fd1
  3. fd2 = perf_event_open with attr.config = ID1
     <this will be successful>
  4. user program closes fd2 and prog1 is detached from the tracepoint.
  5. user program with fd1 does not work properly as tracepoint
     no output any more.

The issue happens at step 4. Multiple perf_event_open can be called
successfully, but only one bpf prog pointer in the tp_event. In the
current logic, any fd release for the same tp_event will free
the tp_event->prog.

The fix is to free tp_event->prog only when the closing fd
corresponds to the one which registered the program.

Signed-off-by: Yonghong Song <yhs@fb.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/linux/trace_events.h
kernel/events/core.c

index 5012b524283d2d59da5b0f954c242a605a31ee4d..60248d644b6f9e8081a0b0ae497113e7b96938b8 100644 (file)
@@ -277,6 +277,7 @@ struct trace_event_call {
        int                             perf_refcount;
        struct hlist_head __percpu      *perf_events;
        struct bpf_prog                 *prog;
+       struct perf_event               *bpf_prog_owner;
 
        int     (*perf_perm)(struct trace_event_call *,
                             struct perf_event *);
index 03ac9c8b02fb81a89662a16c6871755933cf9721..7242a6e1ec76d5e6ddc4d6e6ff15c04cf2f212a7 100644 (file)
@@ -8121,6 +8121,7 @@ static int perf_event_set_bpf_prog(struct perf_event *event, u32 prog_fd)
                }
        }
        event->tp_event->prog = prog;
+       event->tp_event->bpf_prog_owner = event;
 
        return 0;
 }
@@ -8135,7 +8136,7 @@ static void perf_event_free_bpf_prog(struct perf_event *event)
                return;
 
        prog = event->tp_event->prog;
-       if (prog) {
+       if (prog && event->tp_event->bpf_prog_owner == event) {
                event->tp_event->prog = NULL;
                bpf_prog_put(prog);
        }