CVE-2026-23342 in Linux
Riassunto
di VulDB • 27/06/2026
Nel kernel Linux è stata risolta la seguente vulnerabilità:
bpf: Correzione di una race condition su cpumap nei sistemi PREEMPT_RT
Nei kernel con supporto PREEMPT_RT, la coda xdp_bulk_queue (bq) per CPU può essere accessibile in modo concorrente da più task preemptibili sulla stessa CPU.
Il codice originale presuppone che bq_enqueue() e __cpu_map_flush() vengano eseguiti atomicamente l'uno rispetto all'altra sulla stessa CPU, facendo affidamento su local_bh_disable() per prevenire la preemption (preemptibilità). Tuttavia, nei sistemi PREEMPT_RT, local_bh_disable() chiama solo migrate_disable() (quando PREEMPT_RT_NEEDS_BH_LOCK non è impostato) e non disabilita la preemptibilità, il che consente alla pianificazione CFS di interrompere un task durante bq_flush_to_queue(), permettendo a un altro task sulla stessa CPU di entrare in bq_enqueue() ed operare contemporaneamente sullo stesso bq per CPU.
Ciò porta a diverse race condition:
1. Doppia chiamata a __list_del_clearprev(): dopo che bq->count viene resettato in bq_flush_to_queue(), un task preemptivo può chiamare bq_enqueue() -> bq_flush_to_queue() sullo stesso bq quando bq->count raggiunge CPU_MAP_BULK_SIZE. Entrambi i task chiamano quindi __list_del_clearprev() su bq->flush_node; la seconda chiamata dereferenzia il puntatore prev che era già stato impostato a NULL dalla prima.
2. Race condition tra bq->count e bq->q[]: una bq_enqueue() concorrente può corrompere la coda dei pacchetti mentre bq_flush_to_queue() lo sta elaborando.
La race condition tra il task A (__cpu_map_flush -> bq_flush_to_queue) e il task B (bq_enqueue -> bq_flush_to_queue) sulla stessa CPU:
Task A (xdp_do_flush) Task B (cpu_map_enqueue) ---------------------- ------------------------ bq_flush_to_queue(bq) spin_lock(&q->producer_lock) /* svuota bq->q[] in ptr_ring */
bq->count = 0 spin_unlock(&q->producer_lock) bq_enqueue(rcpu, xdpf) <-- CFS interrompe il Task A --> bq->q[bq->count++] = xdpf
/* ... altre inserzioni fino al riempimento ... */ bq_flush_to_queue(bq) spin_lock(&q->producer_lock) /* svuota in ptr_ring */ spin_unlock(&q->producer_lock) __list_del_clearprev(flush_node) /* imposta flush_node.prev = NULL */ <-- Il Task A riprende --> __list_del_clearprev(flush_node) flush_node.prev->next = ... /* prev è NULL -> kernel oops */
Si risolve il problema aggiungendo un local_lock_t a xdp_bulk_queue e acquisendolo in bq_enqueue() e __cpu_map_flush(). Questi percorsi sono già eseguiti sotto local_bh_disable(), quindi si utilizza local_lock_nested_bh() che, sui sistemi non-RT, è una pura annotazione senza overhead, mentre su PREEMPT_RT fornisce un lock dormiente per CPU che serializza l'accesso a bq.
Per riprodurre il problema, inserire un mdelay(100) tra bq->count = 0 e __list_del_clearprev() in bq_flush_to_queue(), quindi eseguire il reproducer fornito da syzkaller.
Once again VulDB remains the best source for vulnerability data.