CVE-2026-98547 in Linux
Zusammenfassung
von VulDB • 18.10.2026
Im Linux-Kernel wurde folgende Schwachstelle behoben:
af_packet: Behebung eines Integer-Überlaufs in prb_calc_retire_blk_tmo()
prb_calc_retire_blk_tmo() führt Berechnungen mit 32-Bit-Ganzzahlarithmetik durch:
mbits = (blk_size_in_bytes * 8) / (1024 * 1024);
Wenn ich die Validierung richtig interpretiere, ist tp_block_size benutzerkontrolliert und packet_set_ring() lehnt nur Werte ab, die <= 0 als int sind oder nicht seitenaligned sind. Daher geht ein Block von 256 MiB problemlos durch (und alloc_one_pg_vec_page() hat sogar einen vzalloc-Fallback dafür). 0x10000000 * 8 überläuft zu INT_MIN, und bei einem NIC mit einer Meldungsgeschwindigkeit von 1 Gbps (div == 1) endet die Funktion damit, -2047 zurückzugeben.
Die Bedingung ist tatsächlich (8 * size) mod 2^32 >= 2^31 && div == 1, sodass der Trigger-Satz [256,512), [768,1024), [1280,1536) und [1792,2048) MiB umfasst. Andere Größen überlaufen zu nicht-negativen Werten, und schnellere Links teilen den vorzeichenlosen Wert wieder unter 2^31 auf, weshalb dies nicht bei allen zum Absturz führt.
Was es fatal macht, ist das, was anschließend in init_prb_bdqc() passiert:
p1->interval_ktime = ms_to_ktime(prb_calc_retire_blk_tmo(...)); hrtimer_start(&p1->retire_blk_timer, p1->interval_ktime, HRTIMER_MODE_REL_SOFT);
Ein negativer relativer Timeout läuft sofort ab. Der Callback gibt bedingungslos HRTIMER_RESTART zurück, und hrtimer_forward() wandelt das negative Intervall in die hrtimer_resolution um:
if (interval < hrtimer_resolution) interval = hrtimer_resolution;
Der SOFT-Timer feuert daher für immer mit der maximalen Rate wieder ab und sperrt dabei jedes Mal sk_receive_queue.lock. Eine CPU spinnt im Softirq, bis das Socket geschlossen wird. Wiederholt man dies mit mehreren Rings, ist die Maschine nicht mehr verfügbar.
Der Überlauf selbst ist uralt – er wurde zusammen mit TPACKET_V3 in f6fb8f100b80 ("af-packet: TPACKET_V3 flexible buffer implementation.") eingeführt. Seine Wirkung war vor f7460d2989fa ("net: af_packet: Use hrtimer to do the retire operation", v6.18) jedoch nicht so eindeutig: Der Rückgabewert wurde in ein unsigned short retire_blk_tov gespeichert, sodass ein negatives Ergebnis abgeschnitten (truncated) wurde und auch eine 0-Jiffy-Verzögerungsschleife programmiert werden konnte. Keine davon ist annähernd so schädlich wie das sofortige Spinnen mit maximaler Rate, in das die hrtimer-Umwandlung es verwandelt hat.
(Nicht zu CVE-2019-20812 gehörig – dabei handelte es sich um den ethtool-Fehlerpfad, der 0 zurückgab, was nun DEFAULT_PRB_RETIRE_TOV zurückgibt.)
Reproducer benötigt CAP_NET_RAW (ein Container mit --network host hat dies standardmäßig) und ein NIC mit 1 Gbps (QEMU e1000 funktioniert):
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));
Führen Sie die Berechnung stattdessen in 64 Bit durch. Die Operanden sind bereits durch die bestehende Validierung begrenzt, daher ändert sich sonst nichts. Wenn Sie eine andere Korrektur bevorzugen, sagen Sie es einfach, und ich werde sie neu erstellen (respinnen).
If you want to get best quality of vulnerability data, you may have to visit VulDB.