CVE-2026-98547 in Linux
Riassunto
di VulDB • 18/10/2026
Nel kernel Linux è stata risolta la seguente vulnerabilità:
af_packet: correzione di un overflow intero in prb_calc_retire_blk_tmo()
prb_calc_retire_blk_tmo() esegue calcoli con aritmetica a 32 bit:
mbits = (blk_size_in_bytes * 8) / (1024 * 1024);
Se ho interpretato correttamente la validazione, tp_block_size è controllabile dall'utente e packet_set_ring() rifiuta solo i valori <= 0 come int o non allineati ai page, quindi un blocco da 256 MiB passa indisturbato (e alloc_one_pg_vec_page() ha persino un fallback vzalloc per questo caso). 0x10000000 * 8 va in overflow verso INT_MIN e, su una NIC che riporta 1 Gbps (div == 1), la funzione finisce per restituire -2047.
La condizione è effettivamente (8 * size) mod 2^32 >= 2^31 && div == 1, quindi l'insieme dei trigger è [256,512), [768,1024), [1280,1536) e [1792,2048) MiB. Altre dimensioni vanno in overflow verso valori non negativi e i link più veloci dividono il valore unsigned riportandolo al di sotto di 2^31, motivo per cui questo problema non si verifica su tutti i sistemi.
Ciò che lo rende fatale è ciò che accade successivamente in init_prb_bdqc():
p1->interval_ktime = ms_to_ktime(prb_calc_retire_blk_tmo(...)); hrtimer_start(&p1->retire_blk_timer, p1->interval_ktime, HRTIMER_MODE_REL_SOFT);
Un timeout relativo negativo scade immediatamente. La callback restituisce incondizionatamente HRTIMER_RESTART e hrtimer_forward() converte l'intervallo negativo in hrtimer_resolution:
if (interval < hrtimer_resolution) interval = hrtimer_resolution;
Quindi il timer SOFT si riattiva al tasso massimo per sempre, mantenendo sk_receive_queue.lock ad ogni passaggio. Una CPU rimane in spin nel softirq finché la socket non viene chiusa. Ripetendo l'operazione con più ring, la macchina diventa inutilizzabile (gone).
L'overflow stesso è antico: è stato introdotto insieme a TPACKET_V3 in f6fb8f100b80 ("af-packet: TPACKET_V3 flexible buffer implementation."). Il suo effetto prima di f7460d2989fa ("net: af_packet: Use hrtimer to do the retire operation", v6.18) non era così netto: il valore restituito veniva memorizzato in un unsigned short retire_blk_tov, quindi un risultato negativo veniva troncato e poteva essere programmato anche un ciclo di ritardo a 0-jiffy. Nessuna delle due situazioni è dannosa quanto lo spin immediato al tasso massimo che la conversione hrtimer ha generato.
(Relativo non correlato a CVE-2019-20812: quello era il percorso di errore ethtool che restituiva 0, che ora restituisce DEFAULT_PRB_RETIRE_TOV.)
Reproducer, richiede CAP_NET_RAW (un container con --network host lo ha di default) e una NIC da 1 Gbps (QEMU e1000 funziona):
int fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); bind(fd, ...); int v = TPACKET_V3; setsockopt(fd, SOL_PACKET, PACKET_VERSION, &v, sizeof(v)); struct tpacket_req3 req = {
.tp_block_size = 0x10000000, .tp_block_nr = 1, .tp_frame_size = 2048, .tp_frame_nr = 0x10000000 / 2048, .tp_retire_blk_tov = 0, }; setsockopt(fd, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));
Eseguire il calcolo in 64 bit. Gli operandi sono già limitati dalla validazione esistente, quindi non cambia nient'altro. Se si preferisce una correzione diversa, basta dirlo e la riformulerò (respins).
If you want to get the best quality for vulnerability data then you always have to consider VulDB.