fix(wifi): step down default skywalk_step to 1, enforce initIvars allocation, and guard teardown lifecycle

- Breakthroughs & Rétro-ingénierie :
  * Découverte critique dans IO80211SkywalkInterface::free() (0xffffff80022f6184) :
    La première instruction déréférence ivars + 0xb8 (movq 0x120(%rdi), %rax ; movq 0xb8(%rax), %rdi).
    Si l'interface est allouée (operator new + C1) sans que initIvars ne soit exécuté, 0x120 est NULL,
    provoquant un Page Fault instantané sur CR2: 0x00000000000000b8 lors de tout release() / stop().
  * Dans le commit 506df09, skywalk_step était à 6 par défaut (exécutant d'un coup CCPipe, PM,
    IO80211PeerManager, WCLGlue et IO80211MacAddressAgent), ce qui causait le bootloop sans isolation.
  * Découverte dans IO80211SkywalkInterface::stop (0xffffff80022f885c) :
    Déréférence ivars + 0x80 (movq 0x80(%rax), %rdi). Appeler stop() sans que start() n'ait réussi
    déclenche un crash en cascade.

- Modifications apportées :
  * QualcommWifi/QualcommWifi.hpp : Ajout des drapeaux d'état de cycle de vie fSkywalkCtrlAttached,
    fSkywalkAttached et fSkywalkStarted.
  * QualcommWifi/QualcommWifi.cpp :
    - Déclaration et appel de _ZN23IO80211SkywalkInterface9initIvarsEv dès createSkywalkInterface pour
      garantir la présence immédiate de la structure ivars et immuniser l'objet contre le crash de free().
    - Réduction sécurisée du palier par défaut skywalk_step à 1 pour isoler la création de l'objet.
    - Protection conditionnelle de l'arrêt dans QualcommWifi::stop() via les drapeaux d'état.
  * docs/ISSUES_AND_POSTMORTEMS.md : Documentation détaillée de INC-16.

- Validation :
  * Compilation build_wifi.sh réussie à 100%.
  * Validation des bibliothèques et symboles avec kmutil libraries réussie sans avertissement.
This commit is contained in:
Erwan committed 2026-10-18 15:32:53 +02:00
1 parent 506df09b68
commit b7e32db90a
3 files changed
+69 -11

No files matched your search

+36 -11
View File
@@ -132,6 +132,7 @@ extern "C" {
void* _ZN23IO80211SkywalkInterfacenwEm(unsigned long size);
void _ZN23IO80211SkywalkInterfaceC1EPK11OSMetaClass(void *thisPtr, const void *meta);
void _ZN23IO80211SkywalkInterfacedlEPvm(void *ptr, unsigned long size);
bool _ZN23IO80211SkywalkInterface9initIvarsEv(void *thisPtr);
extern const void * const _ZN23IO80211SkywalkInterface10gMetaClassE;
}
@@ -143,6 +144,7 @@ static IO80211SkywalkInterface* createSkywalkInterface(void) {
}
bzero(mem, 0x128);
_ZN23IO80211SkywalkInterfaceC1EPK11OSMetaClass(mem, &_ZN23IO80211SkywalkInterface10gMetaClassE);
_ZN23IO80211SkywalkInterface9initIvarsEv(mem);
return (IO80211SkywalkInterface *)mem;
}
@@ -166,6 +168,9 @@ bool QualcommWifi::init(OSDictionary *dictionary) {
fWorkQueue = nullptr;
fNetif = nullptr;
fSkywalkInterface = nullptr;
fSkywalkCtrlAttached = false;
fSkywalkAttached = false;
fSkywalkStarted = false;
// Adresse MAC par défaut (OUI Qualcomm 00:03:7F) en attendant la lecture BHI
fMacAddress.bytes[0] = 0x00;
fMacAddress.bytes[1] = 0x03;
@@ -778,15 +783,15 @@ bool QualcommWifi::start(IOService *provider) {
int allow_skywalk = 1;
PE_parse_boot_argn("qcom_skywalk", &allow_skywalk, sizeof(allow_skywalk));
// Étape maximale Skywalk contrôlée par boot-arg "qcom_step" (par défaut: 6 = jusqu'à start(this))
// 1: createSkywalkInterface
// Étape maximale Skywalk contrôlée par boot-arg "qcom_step" (par défaut: 1 = allocation + initIvars sécurisée)
// 1: createSkywalkInterface (allocation + initIvars)
// 2: init(this, &mac)
// 3: setInterfaceRole(1)
// 4: attachInterface(fSkywalkInterface, this) [slot 441 sur controller]
// 5: fSkywalkInterface->attach(this) [attachement IORegistry]
// 6: fSkywalkInterface->start(this) [démarrage Skywalk, joinPMtree, workQueue, etc.]
// 7: fSkywalkInterface->registerService() [matching IOKit / airportd]
int skywalk_step = 6;
int skywalk_step = 1;
PE_parse_boot_argn("qcom_step", &skywalk_step, sizeof(skywalk_step));
if (allow_skywalk) {
@@ -805,16 +810,18 @@ bool QualcommWifi::start(IOService *provider) {
}
if (skywalk_step >= 4) {
logNvramStage("skywalk_attach_ctrl");
bool ctrlAttachOk = attachInterface(fSkywalkInterface, this); // slot 441 in IO80211Controller
IOLog("QualcommWifi: this->attachInterface(fSkywalkInterface, this) = %d\n", ctrlAttachOk);
fSkywalkCtrlAttached = attachInterface(fSkywalkInterface, this); // slot 441 in IO80211Controller
IOLog("QualcommWifi: this->attachInterface(fSkywalkInterface, this) = %d\n", fSkywalkCtrlAttached);
}
if (skywalk_step >= 5) {
logNvramStage("skywalk_attach");
if (fSkywalkInterface->attach(this)) {
fSkywalkAttached = true;
IOLog("QualcommWifi: fSkywalkInterface->attach(this) réussi !\n");
if (skywalk_step >= 6) {
logNvramStage("skywalk_start");
if (fSkywalkInterface->start(this)) {
fSkywalkStarted = true;
IOLog("QualcommWifi: fSkywalkInterface->start(this) démarré avec succès !\n");
if (skywalk_step >= 7) {
logNvramStage("skywalk_register");
@@ -824,15 +831,24 @@ bool QualcommWifi::start(IOService *provider) {
} else {
logNvramStage("skywalk_start_err");
IOLog("QualcommWifi: Erreur - fSkywalkInterface->start(this) a échoué !\n");
fSkywalkInterface->detach(this);
detachInterface(fSkywalkInterface, false);
if (fSkywalkAttached) {
fSkywalkInterface->detach(this);
fSkywalkAttached = false;
}
if (fSkywalkCtrlAttached) {
detachInterface(fSkywalkInterface, false);
fSkywalkCtrlAttached = false;
}
OSSafeReleaseNULL(fSkywalkInterface);
}
}
} else {
logNvramStage("skywalk_attach_err");
IOLog("QualcommWifi: Erreur - fSkywalkInterface->attach(this) a échoué !\n");
detachInterface(fSkywalkInterface, false);
if (fSkywalkCtrlAttached) {
detachInterface(fSkywalkInterface, false);
fSkywalkCtrlAttached = false;
}
OSSafeReleaseNULL(fSkywalkInterface);
}
}
@@ -865,9 +881,18 @@ void QualcommWifi::stop(IOService *provider) {
IOLog("QualcommWifi: Arret du pilote Wi-Fi natif, liberation MSI, DMA et BAR0.\n");
if (fSkywalkInterface) {
detachInterface(fSkywalkInterface, false); // slot 442 in IO80211Controller
fSkywalkInterface->stop(this);
fSkywalkInterface->detach(this);
if (fSkywalkCtrlAttached) {
detachInterface(fSkywalkInterface, false); // slot 442 in IO80211Controller
fSkywalkCtrlAttached = false;
}
if (fSkywalkStarted) {
fSkywalkInterface->stop(this);
fSkywalkStarted = false;
}
if (fSkywalkAttached) {
fSkywalkInterface->detach(this);
fSkywalkAttached = false;
}
OSSafeReleaseNULL(fSkywalkInterface);
}
+3
View File
@@ -189,6 +189,9 @@ private:
IO80211WorkQueue *fWorkQueue;
IONetworkInterface *fNetif;
IO80211SkywalkInterface *fSkywalkInterface;
bool fSkywalkCtrlAttached;
bool fSkywalkAttached;
bool fSkywalkStarted;
IOEthernetAddress fMacAddress;
// État réseau Wi-Fi
+30
View File
@@ -23,6 +23,7 @@ Ce document répertorie de manière exhaustive l'ensemble des anomalies, blocage
| **INC-13** | Page Fault `CR2: 0x658` dans `ml_thread_policy` au boot | `IOWorkLoop` vs `IO80211WorkQueue` | `IO80211WorkQueue::getThread()` retourne 0, deref `thread->mutex` (+0x658) | **Résolu** |
| **INC-14** | Page Fault `CR2: 0x1` dans `printDataPath` | `IO80211SkywalkInterface.h` | Méthodes `init` et `setInterfaceRole` déclarées `virtual` par erreur | **Résolu** |
| **INC-15** | Kernel Trap / Triple Fault dans `initIvars` & `attachInterface` | Skywalk Heap & Attachment ABI | Tas non initialisé (`bzero` manquant), header incorrect, ordre d'attachement et `registerService` prématuré | **Résolu** |
| **INC-16** | Bootloop par démarrage complet Skywalk & Piège de libération `free()` | Skywalk Lifecycle & Ivars Guard | Exécution aveugle du stade 6 (`start`), `#PF` CR2:0xb8 si `free()` sans `initIvars`, et `stop` non-gardé | **Résolu** |
---
@@ -400,4 +401,33 @@ Ce document répertorie de manière exhaustive l'ensemble des anomalies, blocage
- Introduction du palier `qcom_step` (par défaut `6` = exécution complète de `start(this)` sans appeler prématurément `registerService()`).
- Validation binaire par désassemblage et validation `kmutil libraries` à 100%.
---
### INC-16 : Bootloop par Démarrage Complet Skywalk & Piège de Libération `free()`
* **Symptômes** :
- Bootloop persistant et redémarrage spontané de la machine lors du chargement de `QualcommWifi.kext` (commit `506df09`).
- Nécessité de désactiver manuellement `QualcommWifi.kext` dans OpenCore pour atteindre le bureau macOS.
* **Causes Racines (Breakthroughs & Rétro-ingénierie du Kernel Collection)** :
1. **Piège de Libération sans `initIvars` dans `IO80211SkywalkInterface::free()` (`0xffffff80022f6184`)** :
- Le désassemblage de `IO80211SkywalkInterface::free` révèle :
```assembly
0xffffff80022f618e: movq 0x120(%rdi), %rax ; rax = ivars
0xffffff80022f6195: movq 0xb8(%rax), %rdi ; Déréférencement direct rax + 0xb8 sans vérification NULL !
```
- Si l'interface est allouée (`operator new + C1`) mais que `initIvars` n'a pas encore été invoqué, `0x120` est `NULL`. Tout appel subséquent à `release()` / `free()` déclenche un Kernel Page Fault instantané sur `CR2: 0x00000000000000b8` !
2. **Démarrage complet prématuré par défaut (`skywalk_step=6`)** :
- Dans le commit `506df09`, `skywalk_step` était fixé à 6 par défaut. Lors du boot standard (sans boot-arg explicite), le code franchissait d'un coup l'allocation, l'initialisation, le rôle, l'attachement au contrôleur (slot 441), l'attachement IORegistry ET l'appel `fSkywalkInterface->start(this)`.
- Dans `start(this)`, Apple initialise `CCPipe`, `CCStream`, `IO80211PeerManager`, la gestion d'énergie `joinPMtree` / `registerPowerDriver` / `changePowerStateTo(0)`, `IO80211Glue`, `WCLGlue`, et `IO80211MacAddressAgent`. Un échec non géré dans ces sous-systèmes sans isolation pas-à-pas provoquait le bootloop.
3. **Nettoyage non gardé dans `stop()`** :
- `QualcommWifi::stop` invoquait inconditionnellement `detachInterface`, `fSkywalkInterface->stop(this)` et `fSkywalkInterface->detach(this)`. Or, `IO80211SkywalkInterface::stop` déréférence également `ivars + 0x80` (`movq 0x80(%rax), %rdi`), causant un crash en cascade si l'interface n'était pas entièrement démarrée.
* **Résolutions Techniques Appliquées** :
1. **Invocation explicite et systématique de `initIvars()` dès la création** :
- Déclaration et appel de `_ZN23IO80211SkywalkInterface9initIvarsEv(mem)` dans `createSkywalkInterface` dès l'allocation, garantissant que `ivars` est toujours non-nul et immunisé contre tout `#PF` dans `free()`.
2. **Réduction sécurisée du palier par défaut à l'étape 1 (`skywalk_step=1`)** :
- `int skywalk_step = 1;` par défaut, permettant de valider isolément la stabilité de la création de l'objet avant d'activer les étapes suivantes via `qcom_step`.
3. **Protection conditionnelle du cycle de vie dans `stop()` via des drapeaux d'état** :
- Ajout de `fSkywalkCtrlAttached`, `fSkywalkAttached`, `fSkywalkStarted` pour ne détacher ou arrêter que ce qui a été effectivement attaché ou démarré avec succès.
4. **Validation intégrale de la chaîne de compilation** :
- `build_wifi.sh` validé à 100% avec `kmutil libraries` auprès de `BootKernelExtensions.kc`.