
Backports von drei veröffentlichten f_hid-Fixes (inkl. CVE-2026-31721, CVE-2026-31606) auf einen EOL-Linux-4.14.190-Android-Vendor-Kernel, mit Verifizierungsaufzeichnungen auf dem Gerät.
Drei Korrekturen an drivers/usb/gadget/function/f_hid.c, von Hand auf einen Android-Vendor-Kernel 4.14.190 übertragen und auf dem Gerät verifiziert, auf dem sie laufen.
4.14 ist End-of-Life. Für keine dieser Korrekturen existiert ein stabiler Backport, und es wird auch keiner kommen. Genau deshalb gibt es dieses Repository: Das Gerät gehört mir, und einer dieser Defekte hat es zurückgesetzt.
Um präzise zu sein, wer was pflegt: Der NetHunter-Baum, auf dem dies aufbaut (AzeAstro/samsung_kernel_sm7125, Branch OneUI-6), wird aktiv gepflegt, und diese Arbeit steht hier nicht infrage. Aber es handelt sich um Geräte- und NetHunter-Enablement — die Basis bleibt auf Samsungs 4.14.190-Vendor-Drop festgenagelt. Niemand trägt stabile Sicherheitskorrekturen aus 4.14.y dorthin zurück, und Samsung liefert sie auch nicht aus — 4.14.y selbst endete am 10. Januar 2024 bei 4.14.336, und die Serie wird nicht mehr unter den von kernel.org unterstützten Releases geführt. In dieser Lücke liegen diese drei Patches.
Ich habe diese Schwachstellen nicht entdeckt. Alle drei sind veröffentlicht, mit Upstream-Patches von anderen Personen, die unten genannt werden. Was ich getan habe:
Punkt 3 ist der Teil, den ich den Leser bitten möchte zu gewichten. Zwei davon sind durch Messung gegen eine Baseline bestätigt. Das dritte ist es nicht und kann es auf diesem Gerät nicht sein. Diese Unterscheidung wird durchgehend beibehalten statt geglättet.
| # | Patch | Upstream-Autor | Status auf der Hardware |
|---|---|---|---|
| 1 | f_hidg-Lebensdauer vs. cdev (Use-after-free) | John Keeping, 2022-11-22 | ✅ Wirksamkeit gemessen |
| 2 | CVE-2026-31721 — Objekt-Lebensdauer-Zustand in hidg_bind re-initialisiert | Michael Zimmermann, 2026-03-31 | ✅ Wirksamkeit gemessen |
| 3 | CVE-2026-31606 — cdev_init auf einem in Benutzung befindlichen cdev | Michael Zimmermann (81ebd43cc0d6d), 2026-03-27; -ENOMEM-Folgekorrektur von Ethan Tidmore, 2026-04-02 | ⛔ auf diesem Gerät nicht beobachtbar |
f_hidg-Lebensdauer vs. cdevf_hidg_open speicherte den f_hidg * und nahm keine Referenz. f_hidg_poll registrierte Poll-Waiter an Wait-Queues, die in das per kzalloc allokierte f_hidg eingebettet sind. hidg_unbind rief cdev_del() auf, was offene fds nicht ungültig macht, und hidg_free gab das Objekt anschließend per kfree frei. Nichts hielt es per Refcount gegen offene Dateien — ein grep über die ganze Datei nach refcount|kref|atomic_|open_count fand nur opts->lock.
Eine select()/poll()-Schleife auf /dev/hidg0, die Androids Neukomposition des Gadgets überlebte, würde also remove_wait_queue() auf freigegebenem Slab aufrufen, spin_lock_irqsave auf einem Müll-Ticket-Wort nehmen und mit maskierten IRQs für immer spinnen. Der lokale Timer stoppt, der Watchdog-Pet-Thread wird nie geweckt, und der Non-Secure-Watchdog schlägt ~11 s später an. In der Praxis: Das Handset setzt hart zurück. Androids UsbDeviceManager komponiert von selbst neu — zweimal mit ~550 ms gemessen — ohne Nutzeraktion, sodass dies keinen exotischen Auslöser brauchte.
Upstream-Fix: „usb: gadget: f_hid: fix f_hidg lifetime vs cdev", John Keeping, 2022-11-22, Fixes: 71adf1189469. Der Backport ist sauber — jedes Primitiv existiert bei 4.14.190 — außer dem kfree(hidg->set_report_buf) des Patches, ein Feld, das diese Version nicht hat, welches daher entfällt.
CVSS 3.1 5.5, Vektor AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H. hidg_bind re-initialisierte Zustand, der zur Lebensdauer des Objekts gehört, nicht zu der des Bind: zwei Spinlocks, zwei Wait-Queue-Header und ein Listen-Header, von denen keiner in hidg_alloc auftaucht. init_waitqueue_head setzt den Listen-Header auf Selbstzeiger zurück, sodass ein vor einem Rebind registrierter Poll/Select-Waiter stillschweigend verwaist, und dessen poll_freewait → remove_wait_queue findet dann prev->next falsch vor.
Upstream verlagert neun Initialisierungen aus hidg_bind heraus; nur fünf davon existieren bei 4.14.190.
⚠ Mehrere Vendor-Advisories betiteln dies als „privilege escalation". Der Vektor ist C:N/I:N/A:H —
nur Verfügbarkeit, d. h. ein lokaler Denial of Service. Ich weise darauf hin, weil die
erschreckendere Lesart eine Priorität gerechtfertigt hätte, die der Vektor nicht stützt, und niemand
verlangt von einer Vendor-Seite, sich selbst zu beweisen.
Dieser Patch geht zudem einen Schritt über Upstream hinaus. Das Herausverlagern der Initialisierungen
aus hidg_bind hinterlässt zwei ungeschützte Stores auf lock-geschützte Felder, die heute nur
deshalb maskiert sind, weil der Lock unmittelbar darüber neu initialisiert wurde. Sobald der Lock
persistent ist, ist die Race real: Ein Writer, der in der Warteschleife von f_hidg_write blockiert
ist, auf einem fd, das das Unbind überlebt hat, kann den Lock erwerben, die Prüfung bestehen und
hidg->req lesen, während bind außerhalb davon NULL speichert — die erneute Prüfung erfolgt nach der
Dereferenzierung. Der Patch nimmt daher write_spinlock um beide Stores.
Die allgemeine Form ist es wert, festgehalten zu werden: Das Herausverlagern einer Initialisierung aus bind kann eine Race erzeugen, die die Re-Initialisierung verdeckt hat. Man frage, wovor die entfernte Init versehentlich geschützt hat.
CVSS 5.5, derselbe Vektor. struct cdev cdev ist in struct f_hidg eingebettet, und hidg_bind
rief darauf cdev_init() auf — was memset(cdev, 0, sizeof *cdev) plus kobject_init ist —
unmittelbar vor cdev_device_add. Ein offenes fd hält eine Referenz auf dasselbe kobject über
chrdev_open → cdev_get → kobject_get_unless_zero. Bei Unbind→Rebind mit einem offenen fd
löscht bind den Live-Referenzzustand auf einem referenzierten Objekt.
Der Fix ist eine Struct-Änderung statt einer Guard: Das eingebettete struct cdev wird zu einem
struct cdev * mit einem cdev_alloc() pro Bind. cdev_init erscheint dann null Mal in der Datei —
es bleibt kein in Benutzung befindliches Objekt übrig, das re-initialisiert werden könnte.
⛔ Dieses Gerät kann diesen Defekt nicht erkennen, weder vor noch nach dem Fix. Siehe
verification/03-cve-2026-31606.md. Das ist eine positive Aussage über die Instrumentierung des
Geräts, keine Absicherung.
Methode und Aufzeichnungen pro Defekt in verification/. Die Kurzfassung: Fixes 1
und 2 wurden durch Single-Variable-Regression gegen den unmittelbar vorherigen Build mit
Positivkontrollen geschlossen, und Fix 2 wurde ein zweites Mal aus einer anderen Log-Quelle bestätigt.
Fix 3 ist nur durch Quellcode-Lektüre belegt.
Die Patches sind git format-patch-Ausgaben gegen einen 4.14.190-Samsung-Vendor-Tree, in dieser
Reihenfolge:
git am patches/0001-*.patch patches/0002-*.patch patches/0003-*.patch
Sie sind kumulativ und sollen in Reihenfolge angewendet werden — 3 setzt die Struktur voraus, wie 1 sie hinterlassen hat.
GPL-2.0, als abgeleitete Werke des Linux-Kernels. Siehe LICENSE. Die Urheberschaft der ursprünglichen Fixes wird oben genannt; das Backporting, die in Patch 2 hinzugefügte Sperrung und die Verifizierungsarbeit stammen von mir.