posix-cpu-timers: fix race between handle_posix_cpu_timers() and posix_cpu_timer_del()
Priorize a correção. Ela está sob exploração confirmada pelo CISA e tem prova de conceito pública.
Apply mitigations per vendor instructions, follow applicable BOD 22-01 guidance for cloud services, or discontinue use of the product if mitigations are unavailable.
Resumo
Race condition (CWE-362) no subsistema de temporizadores POSIX CPU do kernel Linux que pode degenerar em use-after-free (CWE-416) quando um processo em finalização, não autorreaping (ex.: sob ptrace ou aguardado por wait() controlado), é reaped pelo pai/debugger exatamente enquanto handle_posix_cpu_timers() ainda processa seus timers a partir de contexto IRQ. Importa porque há PoC pública funcional, exploração ativa registrada no catálogo KEV da CISA e o relato original veio do Google (Benoît Sevens), em contexto consistente com kernels Android da linha 5.10.x — mas a exploração exige ganhar uma corrida de timing fina (AC:H), não é trivial de disparar por acaso.
Detalhamento técnico
O código vive em kernel/time/posix-cpu-timers.c, nas funções run_posix_cpu_timers()/handle_posix_cpu_timers() e posix_cpu_timer_del(). O bug foi introduzido pelo commit 0bdd2ed4138e ('sched: run_posix_cpu_timers: Don't check ->exit_state, use lock_task_sighand()'), que removeu uma checagem de exit_state e passou a confiar apenas em lock_task_sighand() para serializar acesso à task.
A janela de corrida existe porque uma task exiting não-autorreaping pode já ter passado por exit_notify() e ainda assim ser chamada via handle_posix_cpu_timers() a partir de uma interrupção de timer. Depois que essa função executa unlock_task_sighand(), existe um intervalo em que o pai ou um debugger pode reap a task (release_task()) antes que o processamento do timer termine. Se, nesse intervalo, uma chamada concorrente a posix_cpu_timer_del() tentar remover o mesmo cpu timer, as funções auxiliares cpu_timer_task_rcu() e/ou lock_task_sighand() falham em localizar a task já liberada — e por isso não detectam que timer->it.cpu.firing != 0, ou seja, que o timer ainda está em uso por handle_posix_cpu_timers(). O delete prossegue como se fosse seguro, e a estrutura associada ao timer/sighand pode ser acessada depois de liberada.
A correção adiciona uma checagem explícita de tsk->exit_state no início de run_posix_cpu_timers(): se a task já está em processo de saída, a função retorna sem prosseguir, eliminando a janela em que release_task() poderia correr concorrentemente com o processamento do timer. O próprio patch observa que essa correção não é necessária quando CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y, porque nesse modo exit_task_work() é executado antes de exit_notify(), o que já impede a ordem de eventos que gera a corrida — mas o autor manteve a checagem como defesa em profundidade, já que task_work_add() falharia de qualquer forma nesse caso.