Skip to content

Commit c127449

Browse files
Peter ZijlstraIngo Molnar
authored andcommitted
perf: Fix race in perf_event_exec()
I managed to tickle this warning: [ 2338.884942] ------------[ cut here ]------------ [ 2338.890112] WARNING: CPU: 13 PID: 35162 at ../kernel/events/core.c:2702 task_ctx_sched_out+0x6b/0x80() [ 2338.900504] Modules linked in: [ 2338.903933] CPU: 13 PID: 35162 Comm: bash Not tainted 4.4.0-rc4-dirty torvalds#244 [ 2338.911610] Hardware name: Intel Corporation S2600GZ/S2600GZ, BIOS SE5C600.86B.02.02.0002.122320131210 12/23/2013 [ 2338.923071] ffffffff81f1468e ffff8807c6457cb8 ffffffff815c680c 0000000000000000 [ 2338.931382] ffff8807c6457cf0 ffffffff810c8a56 ffffe8ffff8c1bd0 ffff8808132ed400 [ 2338.939678] 0000000000000286 ffff880813170380 ffff8808132ed400 ffff8807c6457d00 [ 2338.947987] Call Trace: [ 2338.950726] [<ffffffff815c680c>] dump_stack+0x4e/0x82 [ 2338.956474] [<ffffffff810c8a56>] warn_slowpath_common+0x86/0xc0 [ 2338.963195] [<ffffffff810c8b4a>] warn_slowpath_null+0x1a/0x20 [ 2338.969720] [<ffffffff811a49cb>] task_ctx_sched_out+0x6b/0x80 [ 2338.976244] [<ffffffff811a62d2>] perf_event_exec+0xe2/0x180 [ 2338.982575] [<ffffffff8121fb6f>] setup_new_exec+0x6f/0x1b0 [ 2338.988810] [<ffffffff8126de83>] load_elf_binary+0x393/0x1660 [ 2338.995339] [<ffffffff811dc772>] ? get_user_pages+0x52/0x60 [ 2339.001669] [<ffffffff8121e297>] search_binary_handler+0x97/0x200 [ 2339.008581] [<ffffffff8121f8b3>] do_execveat_common.isra.33+0x543/0x6e0 [ 2339.016072] [<ffffffff8121fcea>] SyS_execve+0x3a/0x50 [ 2339.021819] [<ffffffff819fc165>] stub_execve+0x5/0x5 [ 2339.027469] [<ffffffff819fbeb2>] ? entry_SYSCALL_64_fastpath+0x12/0x71 [ 2339.034860] ---[ end trace ee1337c59a0ddeac ]--- Which is a WARN_ON_ONCE() indicating that cpuctx->task_ctx is not what we expected it to be. This is because context switches can swap the task_struct::perf_event_ctxp[] pointer around. Therefore you have to either disable preemption when looking at current, or hold ctx->lock. Fix perf_event_enable_on_exec(), it loads current->perf_event_ctxp[] before disabling interrupts, therefore a preemption in the right place can swap contexts around and we're using the wrong one. Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Cc: Alexander Potapenko <[email protected]> Cc: Arnaldo Carvalho de Melo <[email protected]> Cc: Arnaldo Carvalho de Melo <[email protected]> Cc: Dmitry Vyukov <[email protected]> Cc: Eric Dumazet <[email protected]> Cc: Jiri Olsa <[email protected]> Cc: Kostya Serebryany <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Sasha Levin <[email protected]> Cc: Stephane Eranian <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Vince Weaver <[email protected]> Cc: syzkaller <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 2d2e7ac commit c127449

File tree

1 file changed

+5
-10
lines changed

1 file changed

+5
-10
lines changed

kernel/events/core.c

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3154,15 +3154,16 @@ static int event_enable_on_exec(struct perf_event *event,
31543154
* Enable all of a task's events that have been marked enable-on-exec.
31553155
* This expects task == current.
31563156
*/
3157-
static void perf_event_enable_on_exec(struct perf_event_context *ctx)
3157+
static void perf_event_enable_on_exec(int ctxn)
31583158
{
3159-
struct perf_event_context *clone_ctx = NULL;
3159+
struct perf_event_context *ctx, *clone_ctx = NULL;
31603160
struct perf_event *event;
31613161
unsigned long flags;
31623162
int enabled = 0;
31633163
int ret;
31643164

31653165
local_irq_save(flags);
3166+
ctx = current->perf_event_ctxp[ctxn];
31663167
if (!ctx || !ctx->nr_events)
31673168
goto out;
31683169

@@ -3205,17 +3206,11 @@ static void perf_event_enable_on_exec(struct perf_event_context *ctx)
32053206

32063207
void perf_event_exec(void)
32073208
{
3208-
struct perf_event_context *ctx;
32093209
int ctxn;
32103210

32113211
rcu_read_lock();
3212-
for_each_task_context_nr(ctxn) {
3213-
ctx = current->perf_event_ctxp[ctxn];
3214-
if (!ctx)
3215-
continue;
3216-
3217-
perf_event_enable_on_exec(ctx);
3218-
}
3212+
for_each_task_context_nr(ctxn)
3213+
perf_event_enable_on_exec(ctxn);
32193214
rcu_read_unlock();
32203215
}
32213216

0 commit comments

Comments
 (0)