fix(wifi): resolve kernel page fault CR2:0x658 in ml_thread_policy via distinct IOWorkLoop
### Breakthroughs & Rétro-ingénierie - Analyse post-mortem du crash dump de panique noyau réel (/Library/Logs/DiagnosticReports/.contents.panic) : panic(cpu 2 caller 0xffffff80130d528a): Kernel trap at 0xffffff80130cc673, type = 14=page fault, CR2: 0x0000000000000658. Backtrace : _IOLockLock + 0x3 (%rdi == 0x658) <- _ml_thread_policy + 0x27 (_thread_policy_set_internal) <- IONetworkController::start + 0x1d4 <- IO80211Controller::start + 0x41 <- QualcommWifi::start + 0x61. - Désassemblage de IONetworkController::start (0xffffff80026f3650) : Appel de this->getWorkLoop() (slot 209 = 0x688) comparé au workloop du provider IOPCIDevice. En cas de non-concordance, il invoque workloop->getThread() (slot 37 = 0x128) et transmet le thread retourné à ml_thread_policy. - Désassemblage de IO80211WorkQueue::getThread() (0xffffff80022ee1ea) : Contrairement à IOWorkLoop qui maintient un Mach thread_t (offset +0x30), IO80211WorkQueue::getThread() renvoie systématiquement 0 (xorl %eax, %eax). QualcommWifi redirigeait getWorkLoop() vers fWorkQueue, causant un appel à ml_thread_policy(0, ...), qui dereference 0 + 0x658 (thread->mutex), provoquant le Page Fault #PF fatal en tout début de boot. ### Modifications apportées - QualcommWifi/QualcommWifi.hpp : - Déclaration du pointeur dédié IOWorkLoop *fWorkLoop; séparé de IO80211WorkQueue *fWorkQueue;. - QualcommWifi/QualcommWifi.cpp : - init() : Initialisation de fWorkLoop à nullptr. - free() : Libération de fWorkLoop via OSSafeReleaseNULL(fWorkLoop). - start() : Instanciation séquentielle createWorkLoop() puis createWorkQueue() avant d'appeler super::start(provider). - stop() : Libération sûre de fWorkLoop et fWorkQueue après désactivation des interruptions. - createWorkLoop() : Allocation d'un véritable IOWorkLoop via IOWorkLoop::workLoop(), garantissant la présence d'un Mach thread valide. - getWorkLoop() : Renvoie fWorkLoop (ou provider->getWorkLoop() en repli). - docs/ISSUES_AND_POSTMORTEMS.md : - Ajout de la fiche d'incident complète INC-13 détaillant l'analyse désassemblée, la cause racine et les résolutions. ### Validation - Compilation réussie via ./build_wifi.sh. - Vérification binaire : slot 209 (getWorkLoop), slot 306 (createWorkLoop), slot 395 (createWorkQueue), slot 397 (getWorkQueue) parfaitement alignés. - Validation symbolique et dépendances kext kmutil libraries -p réussie à 100% contre BootKernelExtensions.kc.
This commit is contained in:
1 parent
3a02c6ff8e
commit
a922133da9
3 files changed
+86
-2
No files matched your search
@@ -143,6 +143,7 @@ bool QualcommWifi::init(OSDictionary *dictionary) {
|
||||
fBar0Virt = nullptr;
|
||||
fCurrWindow = 0xFFFFFFFF;
|
||||
fInterruptSource = nullptr;
|
||||
fWorkLoop = nullptr;
|
||||
fWorkQueue = nullptr;
|
||||
fNetif = nullptr;
|
||||
// Adresse MAC par défaut (OUI Qualcomm 00:03:7F) en attendant la lecture BHI
|
||||
@@ -174,6 +175,7 @@ void QualcommWifi::free(void) {
|
||||
logNvramStage("free_enter");
|
||||
OSSafeReleaseNULL(fFaultReporter);
|
||||
OSSafeReleaseNULL(fLogger);
|
||||
OSSafeReleaseNULL(fWorkLoop);
|
||||
OSSafeReleaseNULL(fWorkQueue);
|
||||
super::free();
|
||||
}
|
||||
@@ -644,6 +646,12 @@ bool QualcommWifi::start(IOService *provider) {
|
||||
logNvramStage("start_enter");
|
||||
IOLog("QualcommWifi: Demarrage du pilote Wi-Fi natif (IO80211Controller)...\n");
|
||||
|
||||
if (!createWorkLoop()) {
|
||||
logNvramStage("workLoop_failed");
|
||||
IOLog("QualcommWifi: Erreur - Impossible de creer IOWorkLoop !\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!createWorkQueue()) {
|
||||
logNvramStage("workQueue_failed");
|
||||
IOLog("QualcommWifi: Erreur - Impossible de creer IO80211WorkQueue !\n");
|
||||
@@ -779,6 +787,14 @@ void QualcommWifi::stop(IOService *provider) {
|
||||
OSSafeReleaseNULL(fPciDevice);
|
||||
}
|
||||
|
||||
if (fWorkLoop) {
|
||||
OSSafeReleaseNULL(fWorkLoop);
|
||||
}
|
||||
|
||||
if (fWorkQueue) {
|
||||
OSSafeReleaseNULL(fWorkQueue);
|
||||
}
|
||||
|
||||
super::stop(provider);
|
||||
}
|
||||
|
||||
@@ -803,11 +819,23 @@ IO80211WorkQueue* QualcommWifi::getWorkQueue(void) const {
|
||||
}
|
||||
|
||||
bool QualcommWifi::createWorkLoop(void) {
|
||||
return createWorkQueue();
|
||||
logNvramStage("createWorkLoop");
|
||||
if (fWorkLoop) {
|
||||
return true;
|
||||
}
|
||||
fWorkLoop = IOWorkLoop::workLoop();
|
||||
if (!fWorkLoop) {
|
||||
logNvramStage("workLoop_alloc_failed");
|
||||
IOLog("QualcommWifi: Erreur - Impossible d'allouer IOWorkLoop !\n");
|
||||
return false;
|
||||
}
|
||||
logNvramStage("workLoop_ok");
|
||||
IOLog("QualcommWifi: IOWorkLoop allouee avec succes (0x%p) !\n", fWorkLoop);
|
||||
return true;
|
||||
}
|
||||
|
||||
IOWorkLoop* QualcommWifi::getWorkLoop(void) const {
|
||||
return getWorkQueue();
|
||||
return fWorkLoop ? fWorkLoop : (getProvider() ? getProvider()->getWorkLoop() : nullptr);
|
||||
}
|
||||
|
||||
IOReturn QualcommWifi::getHardwareAddress(IOEthernetAddress *addrP) {
|
||||
|
||||
@@ -185,6 +185,7 @@ private:
|
||||
UInt32 fCurrWindow;
|
||||
|
||||
IOInterruptEventSource *fInterruptSource;
|
||||
IOWorkLoop *fWorkLoop;
|
||||
IO80211WorkQueue *fWorkQueue;
|
||||
IONetworkInterface *fNetif;
|
||||
IOEthernetAddress fMacAddress;
|
||||
|
||||
@@ -260,3 +260,58 @@ Ce document répertorie de manière exhaustive l'ensemble des anomalies, blocage
|
||||
- Taille totale de l'instance fixée à `0x98` octets (`static_assert(sizeof(FakeLogStream) == 0x98)`).
|
||||
3. **Mise en Conformité de `FakeFaultReporter`** :
|
||||
- `class FakeFaultReporter : public IOService` avec padding `0x90 - sizeof(IOService)`. Vtable de 269 slots (`0x868` octets) et taille `0x90` octets (`static_assert(sizeof(FakeFaultReporter) == 0x90)`).
|
||||
|
||||
---
|
||||
|
||||
### INC-13 : Page Fault Kernel Trap `CR2: 0x0000000000000658` dans `ml_thread_policy` / `IONetworkController::start`
|
||||
* **Symptômes** :
|
||||
- Bootloop instantané ("coupure de courant" / reboot immédiat en mode verbose, ou blocage sur logo Apple en mode normal).
|
||||
- Trace matérielle extraite du log de panique (`/Library/Logs/DiagnosticReports/.contents.panic`) :
|
||||
```
|
||||
panic(cpu 2 caller 0xffffff80130d528a): Kernel trap at 0xffffff80130cc673, type = 14=page fault, CR2: 0x0000000000000658
|
||||
Backtrace:
|
||||
_IOLockLock + 0x3 (avec %rdi == 0x658)
|
||||
_ml_thread_policy + 0x27 (_thread_policy_set_internal)
|
||||
IONetworkController::start(IOService*) + 0x1d4 (0xffffff80026f36b0)
|
||||
IO80211Controller::start(IOService*) + 0x41 (0xffffff8002291f71)
|
||||
QualcommWifi::start(IOService*) + 0x61
|
||||
```
|
||||
* **Causes Racines (Breakthroughs Majeurs & Rétro-ingénierie Noyau)** :
|
||||
1. **Désassemblage critique de `IONetworkController::start(provider)` (`0xffffff80026f3650`)** :
|
||||
```assembly
|
||||
ffffff80026f365b: callq *0x688(%rax) ; Appelle this->getWorkLoop() (slot 209 = 0x688)
|
||||
ffffff80026f3661: movq %rax, 0x88(%rbx) ; Stocke dans ivars->workLoop
|
||||
ffffff80026f3676: movq 0x88(%rbx), %r15
|
||||
ffffff80026f367d: movq (%r14), %rax ; r14 = provider (IOPCIDevice)
|
||||
ffffff80026f3683: callq *0x688(%rax) ; Appelle provider->getWorkLoop()
|
||||
ffffff80026f3689: cmpq %rax, %r15 ; Compare le workloop provider avec notre workloop
|
||||
ffffff80026f368c: je 0xffffff80026f36b0 ; SI IDENTIQUE, SAUTE ml_thread_policy !
|
||||
ffffff80026f368e: movq 0x88(%rbx), %rdi ; Si différent, utilise notre workloop
|
||||
ffffff80026f3695: movq (%rdi), %rax ; vtable de notre workloop
|
||||
ffffff80026f3698: callq *0x128(%rax) ; APPELLE workloop->getThread() (slot 37 = 0x128)
|
||||
ffffff80026f369e: movq %rax, %rdi ; TRANSMET LE THREAD RETOURNÉ À ml_thread_policy !
|
||||
ffffff80026f36ab: callq ml_thread_policy ; -> thread_policy_set_internal(thread, ...)
|
||||
```
|
||||
2. **Origine du Pointeur Null et Incompatibilité d'ABI `IOWorkLoop` vs `IO80211WorkQueue`** :
|
||||
- Dans `QualcommWifi.cpp`, l'implémentation initiale de `getWorkLoop()` redirigeait directement vers `getWorkQueue()` (un objet `IO80211WorkQueue`).
|
||||
- Le désassemblage de `IO80211WorkQueue::getThread()` (`0xffffff80022ee1ea`) dans `BootKernelExtensions.kc` a révélé son implémentation exacte :
|
||||
```assembly
|
||||
ffffff80022ee1ea <IO80211WorkQueue::getThread()>:
|
||||
pushq %rbp
|
||||
movq %rsp, %rbp
|
||||
xorl %eax, %eax ; RETOURNE CONSTAMMENT NULL (0) !
|
||||
popq %rbp
|
||||
retq
|
||||
```
|
||||
- Contrairement à un véritable `IOWorkLoop` qui maintient un Mach thread dédié (`0x30(%rdi)`), `IO80211WorkQueue` retourne toujours `0`.
|
||||
- `thread_policy_set_internal(thread_t thread, ...)` présume que le thread passé en paramètre est non-nul et exécute `IOLockLock(&thread->mutex)` où le mutex de structure `thread` se situe à l'offset `+0x658`.
|
||||
- Le déréférencement `IOLockLock(0 + 0x658)` a déclenché le Page Fault `#PF` sur `CR2: 0x0000000000000658`.
|
||||
* **Résolutions Techniques Appliquées** :
|
||||
1. **Séparation Stricte des Interfaces `IOWorkLoop` et `IO80211WorkQueue`** :
|
||||
- Ajout d'une instance dédiée `IOWorkLoop *fWorkLoop;` allouée via `IOWorkLoop::workLoop()` dans `QualcommWifi::createWorkLoop()`.
|
||||
- `QualcommWifi::getWorkLoop()` retourne `fWorkLoop ? fWorkLoop : (getProvider() ? getProvider()->getWorkLoop() : nullptr);`.
|
||||
- `QualcommWifi::createWorkQueue()` et `getWorkQueue()` conservent `IO80211WorkQueue *fWorkQueue;` allouée via `IO80211WorkQueue::workQueue()`.
|
||||
2. **Séquence d'Initialisation et Nettoyage** :
|
||||
- Dans `QualcommWifi::start(provider)` : instanciation séquentielle de `createWorkLoop()` puis `createWorkQueue()` avant l'invocation de `super::start(provider)`.
|
||||
- Dans `IONetworkController::start`, `workLoop->getThread()` invoque désormais l'implémentation native de `IOWorkLoop::getThread` (`movq 0x30(%rdi), %rax`), fournissant un `thread_t` valide à `ml_thread_policy`, et permettant l'attachement propre du `IOCommandGate` via `fWorkLoop->addEventSource`.
|
||||
- Dans `QualcommWifi::stop()` et `QualcommWifi::free()` : libération propre des deux objets via `OSSafeReleaseNULL(fWorkLoop)` et `OSSafeReleaseNULL(fWorkQueue)`.
|
||||
Reference in new issue
Block a user