• Frederic Weisbecker's avatar
    perf: Fix event leak upon exit · 2fd5ad3f
    Frederic Weisbecker authored
    When a task is scheduled out, pending sigtrap deliveries are deferred
    to the target task upon resume to userspace via task_work.
    
    However failures while adding an event's callback to the task_work
    engine are ignored. And since the last call for events exit happen
    after task work is eventually closed, there is a small window during
    which pending sigtrap can be queued though ignored, leaking the event
    refcount addition such as in the following scenario:
    
        TASK A
        -----
    
        do_exit()
           exit_task_work(tsk);
    
           <IRQ>
           perf_event_overflow()
              event->pending_sigtrap = pending_id;
              irq_work_queue(&event->pending_irq);
           </IRQ>
        =========> PREEMPTION: TASK A -> TASK B
           event_sched_out()
              event->pending_sigtrap = 0;
              atomic_long_inc_not_zero(&event->refcount)
              // FAILS: task work has exited
              task_work_add(&event->pending_task)
           [...]
           <IRQ WORK>
           perf_pending_irq()
              // early return: event->oncpu = -1
           </IRQ WORK>
           [...]
        =========> TASK B -> TASK A
           perf_event_exit_task(tsk)
              perf_event_exit_event()
                 free_event()
                    WARN(atomic_long_cmpxchg(&event->refcount, 1, 0) != 1)
                    // leak event due to unexpected refcount == 2
    
    As a result the event is never released while the task exits.
    
    Fix this with appropriate task_work_add()'s error handling.
    
    Fixes: 517e6a30 ("perf: Fix perf_pending_task() UaF")
    Signed-off-by: default avatarFrederic Weisbecker <frederic@kernel.org>
    Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
    Cc: stable@vger.kernel.org
    Link: https://lore.kernel.org/r/20240621091601.18227-4-frederic@kernel.org
    2fd5ad3f
core.c 334 KB