
Android GKI 6.12 Kernel-Exploit für CVE-2026-43499, der einen rt_mutex-Rollback-Bug mit pselect-Stack-Overwrite verkettet, um Root auf Samsung- und Pixel-Geräten zu erlangen.
Ein Linux-Kernel-Exploit für die Android-GKI 6.12-Linie (Samsung- und Pixel-
Geräte), der auf CVE-2026-43499 abzielt: einen fehlerhaften remove_waiter()-Rollback in
rt_mutex_start_proxy_lock(), der current statt waiter::task verwendet
und dadurch pi_blocked_on eines Waiters auf seinen (später entfernten) Kernel-Stack
rt_mutex_waiter zeigen lässt. In Kombination mit einem pselect()-fd_set-Stack-Overwrite und einem
konsumierenden, durch sched_setattr angetriebenen Fake-rb_tree-Durchlauf ergibt sich
ein deterministisches Kernel-Schreibprimitiv und vollständiges Root.
Der Exploit läuft als LD_PRELOAD-Shared-Library (preload.so) und installiert
einen Root-su-Daemon sowie ein Wallpaper als Post-Root-Artefakte.
Status: aktive Entwicklung. Das m1q-Ziel (ZF1) ist der Bring-up-Fokus. Der pipei tmp_page-uname-Bootstrap ist die aktuell aktive Route: Der Walk ist auf dem Gerät als sauber nachgewiesen (Build #33: pro-Kind geseedete rt_mutex-Regionen) und der vollständige 168-Kandidaten-Sweep läuft. Die configfs-CFI-Route ist eine Sackgasse auf m1q (Rust-Ashmem — kein injizierbarer fops-Slot) und bleibt die Route für C-Ashmem-Ziele. Die Offsets pro Ziel variieren je nach Gerät — vor dem Vertrauen gegen das tatsächliche Kernel-Binary verifizieren.
Wenn FUTEX_CMP_REQUEUE_PI einen Waiter requeued und der Deadlock-Erkennungs-
Kettendurchlauf des Requeues -EDEADLK zurückgibt, rollt __rt_mutex_start_proxy_lock()
über remove_waiter() (rtmutex.c:1535) zurück. Der Rollback dequeued den Waiter korrekt
aus dem Wait-Tree, löscht aber das pi_blocked_on des Requeue-Aufrufers
statt waiter->task->pi_blocked_on. Das pi_blocked_on des WAITERS
bleibt baumelnd an seinem Stack-rt_mutex_waiter, der entfernt wird,
sobald der Futex timeoutet.
Nach dem Timeout wird die Kernel-Stack-Region des Waiters wiederverwendet: core_sys_select()
kopiert die drei fd_sets in diesen Stack-Puffer (der nfds < 344-Stack-Pfad auf
ZF1). Ein präpariertes fd_set-Wort-Array materialisiert einen Fake-rt_mutex_waiter /
Fake-Task / Fake-rt_mutex (mit einer rb_tree-Wurzel, deren Zeiger kontrolliert werden).
Ein Consumer-Thread ruft dann sched_setattr_tid(waiter) auf →
rt_mutex_adjust_pi() → rb_erase_cached, was einen beliebigen
Adress-Schreibvorgang eines kontrollierten Werts erzeugt.
Das gesamte Primitiv erfordert den PI-Ketten-Zyklus: Der Owner hält f_pi_target
und blockiert zugleich auf f_pi_chain (gehalten vom Waiter), sodass der Kettendurchlauf
auf owner → chain → waiter → target → owner trifft und mit -EDEADLK fehlschlägt. Wird der
Zyklus entfernt, gibt der Requeue Erfolg mit null Kernel-Effekt zurück.
sched_blocked_reason (m1q primär, auf dem Gerät bewiesen): Der
gespeicherte Return-PC eines blockierten kworkers (stack_trace_save_tsk) wird aus
dem Ringpuffer gelesen und gegen den einkompilierten worker_thread-
Offset verglichen. Läuft, bevor irgendwelche boot_id-Routen-Wörter verwendet werden, damit Data-Alias-
Schreibziele (data_addr() = p0-Alias + slide_p0_offset) bei einem von Null verschiedenen Slide
korrekt bleiben.SLIDE_LOGGERS_0_1 über einen Linear-Map-
Alias in die boot_id-sysctl-Daten; der geleakte Wert rekonstruiert stext. Auf m1q geparkt: Sein W1-
Ziel (SLIDE_RANDOM_BOOT_ID_DATA_OFF, niedriger physischer RAM) hat
seitenabhängige Beschreibbarkeit — der Slide verschiebt das Ziel pro Boot über Seitengrenzen,
sodass ~6/7 Läufe fehlschlagen. Nur explizit über
SLIDE_FORCE_BOOTID (Mechanismus-Test) ausgeführt, mit tree_pc/tree_left
auf die gesprayte Seite umgeleitet.mm_struct-große Objekte sprayen und eine Futex-Hash-
Kollision nutzen, um ein mm_struct auf dem Heap zu lokalisieren, wodurch eine Kernel-Heap-Seiten-
Adresse geleakt wird, die als Basis für den Fake-Object-Spray dient. Waiter werden beim
Cleanup abgetrennt (nicht gejoint), um den Kernel-Stack-OOM zu vermeiden, der Builds vor
#26 zum Absturz brachte.write_iter-Slot und das ASHMEM_SET_NAME-Heap-Objekt ist
ein KVec, dessen Layout nicht mit dem private_data von configfs_bin_write_iter
übereinstimmt. m1q wechselt stattdessen zu einem kmalloc-Schreibvorgang: den geleakten mm-
Order-3-Block als pipe_inode_info-Objekte zurückgewinnen und den pselect-W1-Schreibvorgang
(*(tree_left) = tree_pc) nutzen, um den tmp_page-Slot eines Kandidaten
(+0x90) mit der UTS-Namespace-Seite (init_uts_ns) zu überschreiben. Ein Fanout-Schreibvorgang platziert
einen Marker-Namen am sysname-Offset und uname() meldet "CatOS" — ein
verifizierbarer Kernel-Schreibvorgang ohne Abhängigkeit von statischen Objekten. C-Ashmem-Ziele
behalten den configfs-Pfad.preload.c den eingebetteten su-Daemon (tmpfs-gemountet in
/apex/com.android.virt/bin, plus adbd-Namespace- und lokale Varianten) und
tauscht das Wallpaper aus.Die pselect-fd_set-Wörter materialisieren einen Fake-rt_mutex_waiter auf dem
Kernel-Stack des Waiters; der sched_setattr_tid(waiter) eines Consumer-Threads durchläuft
ihn via rt_mutex_adjust_pi(). ZF1-Wortkarte (disasm-verifiziert):
PSELECT_WAITER_WORD_SHIFT = 0, Wort 12 = task (@+0x50), Wort 13 = lock
(@+0x58), Wort 14 = wake_state (@+0x60 = 3). Mit einem geseedeten Fake-rt_mutex
schließt der Walk sauber ab: [7]s rb_erase W1 *(tree_left)=tree_pc / W2
*(tree_pc&~3+8)=tree_left sind die einzigen Kernel-Schreibvorgänge; [11] (setprio /
dequeue_pi) und der [9]-Wake werden beide übersprungen (lokaler Fake-Waiter bei Prio 100
hält den Stack-Knoten aus dem Top-Waiter). Jede walk-entered-Vervollständigung seit
Build #19 überlebt auf dem Gerät; die deterministischen Abstürze davor waren ein
8-Byte-Payload-Platzierungsfehler (SKB_DATA_DELTA), kein Walk-Body-Fehler.
Optionale STAGE3=1-Phase, die ein Bridge-Kind forkt, dessen mm->pgd
auf eine vorbereitete Fake-Page-Table tauscht (3-Ebenen: PGD→L1→L2, RX-Loop-Leaf + RW-Stack-Leaf)
und das Kind einen reinen Register-Assembly-Blob ausführen lässt, der seine eigenen Cred
durch ein 2MB-physisches Scan-Fenster patcht — all-RAM-Phys-R/W ohne die configfs/pipe-
Primitive. Derzeit nur in das m1q-Ziel verdrahtet und nicht auf dem Gerät
ausgeführt.
41 Ziele in src/targets/<codename>-<build>/, jedes benötigt mindestens eine
target.h (Kernel-Symbol-Offsets, KIMAGE_TEXT_BASE, Direct-Map-Basis, Struct-
Offsets). Pixel-Ziele (comet, tokay, tegu, caiman, komodo,
frankel, mustang, rango, stallion, blazer) überschreiben gemeinsame Quellen;
Samsung-Ziele (m1q-*) fügen gerätespezifische Logik hinzu.
make list-projects # full list
Kernel-Struct-Layouts unterscheiden sich je Gerät — Offsets müssen gegen das tatsächliche Kernel-Binary / kallsyms für jedes Ziel verifiziert werden.
Ausgabe: build/<PROJECT>/bin/preload.so (Shared Lib, geladen via LD_PRELOAD).
# Default project
CC=clang make