- Breakthroughs & Rétro-ingénierie :
* Analyse de la télémétrie matérielle après freeze au Stade 6 : la puce SPI Flash Gigabyte B550 a enregistré qcom_last_crash = cb_allocLock_exit et qcom_last_trail = ...>cb_allocLock_super_ok>cb_allocLock_exit.
* Découverte irréfutable : QualcommWifi::allocIO80211RecursiveLock() et super::allocIO80211RecursiveLock() retournent avec un succès absolu. Le freeze intervient immédiatement après dans IO80211PeerManager::initWithInterface() ou dans la séquence Power Management de IO80211SkywalkInterface::start().
* Dissection binaire exhaustive du segment BootKernelExtensions.kc :
- Dans PeerManager::initWithInterface() (0xffffff8002246c88) : 3 appels critiques à IO80211TimerSource::allocWithParams() exécutent fWorkQueue->addEventSource(timerSource) via l appel indirect virtuel slot 42 (offset 0x150).
- Dès la fin de createPeerManager() (0xffffff80022f5bc3) : Skywalk appelle controller->joinPMtree(interface) (slot 244 / 0x7a0), registerPowerDriver() puis changePowerStateTo(0).
* Résolution des angles morts :
- Implémentation d un VMT hook non-invasif sur fWorkQueue (objet alloué sur le tas noyau) remplaçant le slot 42 (addEventSource) par une sonde balisée (cb_addEvSrc_1..3).
- Surcharge explicite de joinPMtree(IOService *driver) et des notifications powerStateWillChangeTo / powerStateDidChangeTo.
- Modifications apportées :
* QualcommWifi/QualcommWifi.hpp : déclaration des surcharges joinPMtree, powerStateWillChangeTo et powerStateDidChangeTo.
* QualcommWifi/QualcommWifi.cpp :
- Hook VMT de fWorkQueue->addEventSource (slot 42) dans createWorkQueue() avec restauration dans stop() et free().
- Implémentation de joinPMtree() balisée par cb_joinPMtree_enter et cb_joinPMtree_exit.
- Implémentation de powerStateWillChangeTo / powerStateDidChangeTo balisées.
* docs/ISSUES_AND_POSTMORTEMS.md : rédaction du Postmortem 21 et mise à jour de la table des incidents (INC-21).
* EFI OpenCore : kext déployé sur /Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext (MD5: 1dd315e169bae197a1b05f6af294a725) et boot-args configuré à qcom_step=6 qcom_substep=1.
- Validation :
* Compilation réussie sans avertissement via ./build_wifi.sh.
* kmutil libraries -p build/Release/QualcommWifi.kext validé à 100% contre BootKernelExtensions.kc.
80 KiB
80 KiB
Historique & Post-Mortems des Problèmes Rencontrés
Ce document répertorie de manière exhaustive l'ensemble des anomalies, blocages, panics et bootloops rencontrés lors du développement du pilote natif Wi-Fi Qualcomm WCN7850 (QualcommWifi), avec leurs analyses techniques de cause racine et leurs résolutions vérifiées.
Sommaire des Incidents
| ID | Incident / Symptôme | Composant Touché | Cause Racine | Statut |
|---|---|---|---|---|
| INC-01 | Erreur d'édition de liens : symboles de réservation IONetworkController non résolus |
Linker / KPI macOS | Méthodes virtuelles de réserve non exportées par le noyau Tahoe | Résolu |
| INC-02 | Crash précoce OpenCore : padslot mismatch |
ABI / Vtable | Vtable IO80211Controller allongée à 466 slots sous Darwin 25 |
Résolu |
| INC-03 | Échec silencieux de démarrage : IO80211Controller::start fails |
WorkQueue / IOKit | IONetworkController::start requiert une IO80211WorkQueue |
Résolu |
| INC-04 | Bootloop de 5 secondes : réinitialisation matérielle brutale du PC | Kernel Panic Apple | panic("No ivars->_faultReporter") dans findAndAttachToFaultReporter |
Résolu |
| INC-05 | Échec d'allocation interne : Failed to allocate RNG Agent |
RNG / Logger | IO80211RNGAgent requiert un logger non-nul lors de l'initialisation |
Résolu |
| INC-06 | Désynchronisation du cycle d'initialisation au boot | Cycle Kexts (Info.plist) |
OSBundleRequired positionné à Root au lieu de Network-Root |
Résolu |
| INC-07 | Blocage du chargement firmware AMSS lors du téléversement BHIe | PCIe ASPM | Désynchronisation BAR0 causée par les transitions d'énergie L0s/L1 | Résolu |
| INC-08 | Corruptions mémoires & gel sur plateforme AMD avec > 4 Go RAM | Moteur DMA 36-bit | Pointeur DMA physique tronqué au-delà de 64 Go | Résolu |
| INC-09 | Impossibilité de lire les rapports de panique (redémarrage instantané) | OpenCore / NVRAM | Paramètres de débogage du noyau par défaut provoquant un reboot | Résolu |
| INC-10 | Triple Fault Processeur ("Coupure de Courant") | Logger / CoreCapture | Déréférencement de +0x90 -> +0x58 dans CCLogStream::shouldLog |
Résolu |
| INC-11 | Freeze Apple Logo (non-verbose) / Triple Fault (verbose) | NVRAM & Matching IOKit | Matching prématuré de registerService() & heap dynamique précoce |
Résolu |
| INC-12 | Triple Fault / Freeze Apple Logo persistant | FakeLogStream / FakeFaultReporter |
Vtables tronquées à 38 slots (au lieu de 283/269) & 0x88->0x48 Page 0 #PF dans logv |
Résolu |
| 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 |
| INC-17 | Page Fault sur table de propriétés NULL par court-circuit de super::init(0) |
Skywalk Init Architecture | initIvars prématuré faisait croire à init() que super::init(0) avait déjà eu lieu |
Résolu |
| INC-18 | Page Fault CR2: 0xb8 dans free() par initIvars manquant et absence de boot-arg EFI |
Skywalk Lifecycle & EFI Configuration | free() déréférence [0x120]+0xb8 sans test NULL si init() échoue ; boot-arg qcom_step=1 absent d'OpenCore |
Résolu |
| INC-19 | Saturation du fil d'Ariane NVRAM à 511 octets & latence SPI de repli | Télémétrie NVRAM | Buffer gNvramTrail fixé à 512 octets rejetant les tags de l'Étape 6 & réentrance SPI |
Résolu |
| INC-20 | Freeze au seuil Étape 6 (allocIO80211RecursiveLock) |
Verrou Skywalk & Réentrance UEFI | Non-réentrance de PEWriteNVRAMProperty / UEFI Runtime Services & angle mort télémétrique |
Résolu |
| INC-21 | Freeze post-allocLock dans createPeerManager() / PM Tree |
Chaîne d'initialisation Skywalk & PM Tree | Angle mort d'instrumentation entre allocLock et start_ok ; diagnostic par VMT hooking de fWorkQueue->addEventSource (slot 42) et surcharges PM joinPMtree |
Résolu |
Détail des Post-Mortems
INC-01 : Erreur d'édition de liens sur les réservations IONetworkController
- Symptôme :
Lors de la compilation et de l'édition de liens du binaire Mach-O Kext, le compilateur Clang échouait avec des erreurs
Undefined symbols for architecture x86_64:_ZN19IONetworkController29_RESERVEDIONetworkController2Evjusqu'à7Ev. - Cause Racine :
Dans le Kernel Collection
BootKernelExtensions.kcde macOS Tahoe, Apple conserve les slots virtuels de réservation pour préserver l'ABI C++, mais n'exporte pas leurs symboles dans les tables d'exportation globales (libkern/KPI). - Résolution :
Définition de stubs d'export locaux C dans
QualcommWifi.cpp:extern "C" { void _ZN19IONetworkController29_RESERVEDIONetworkController2Ev(void) {} void _ZN19IONetworkController29_RESERVEDIONetworkController3Ev(void) {} void _ZN19IONetworkController29_RESERVEDIONetworkController4Ev(void) {} void _ZN19IONetworkController29_RESERVEDIONetworkController5Ev(void) {} void _ZN19IONetworkController29_RESERVEDIONetworkController6Ev(void) {} void _ZN19IONetworkController29_RESERVEDIONetworkController7Ev(void) {} }
INC-02 : Crash OpenCore padslot mismatch sur la Vtable
- Symptôme :
Le Kext provoquait un plantage immédiat au chargement par OpenCore ou un KP dans
Vtables.c. - Cause Racine :
OpenCore valide strictement la taille de la table de fonctions virtuelles de chaque classe dérivée. Sous macOS Sonoma/Sequoia (Darwin 24), la vtable d'
IO80211Controllercomptait 448 entrées. Sous macOS Tahoe 26.x (Darwin 25), elle a été portée à 466 entrées (taille binaire de0xe90= 3728 octets). Tout écart dimensionnel invalide la correspondance des slots mémoires. - Résolution :
Vérification systématique par script de la vtable
__ZTV12QualcommWificontreBootKernelExtensions.kc. Tous les slots jusqu'à l'offset0xe90sont rigoureusement alignés sur l'ABI Tahoe.
INC-03 : Échec de IO80211Controller::start sans WorkQueue
- Symptôme :
Le pilote démarrait dans
init()etprobe(), maisIO80211Controller::startaffichaitIO80211Controller::start failset retournaitfalse. - Cause Racine :
L'analyse désassemblée de
IO80211Controller::start(0xffffff8002291f30) a révélé qu'à l'offset+0x41, le code teste le retour deIONetworkController::start(provider). Cette dernière exige impérativement un gestionnaire de queue de travail valide pour instancier ses boucles d'événements. - Résolution :
Implémentation des surcharges de création de la file de travail dans
QualcommWifi.cpp:bool QualcommWifi::createWorkQueue(void) { if (!fWorkQueue) { fWorkQueue = IO80211WorkQueue::workQueue(); } return (fWorkQueue != nullptr); } IO80211WorkQueue* QualcommWifi::getWorkQueue(void) const { return fWorkQueue; } bool QualcommWifi::createWorkLoop(void) { return createWorkQueue(); } IOWorkLoop* QualcommWifi::getWorkLoop(void) const { return getWorkQueue(); }
INC-04 : Le Bootloop de 5 Secondes - Kernel Panic _faultReporter
- Symptôme : L'utilisateur subissait un redémarrage en boucle constant du Mac : OpenCore se lance, commence le chargement de macOS, puis au bout de 5 secondes environ, le PC s'éteint et redémarre instantanément sans message visible.
- Cause Racine (Breakthrough Majeur) :
Désassemblage complet du flux de
IO80211Controller::startà l'adresse0xffffff8002291f30dansBootKernelExtensions.kc:- À
+0x272, le pilote invoquefindAndAttachToFaultReporter()(0xffffff8002292628). - À
+0x13e, cette routine appelle la méthode virtuellethis->getFaultReporterFromDriver()(callq *0xd80(%rax), vtable slot 434). - Notre code renvoyait initialement
nullptr. - À l'adresse
0xffffff8002292737(+0x227),findAndAttachToFaultReportercontient un appel direct et inconditionnel à_panic:panic("\"No ivars->_faultReporter\" @%s:%d", "IO80211Controller.cpp", 2877) - Faute d'argument empêchant le redémarrage, macOS déclenchait immédiatement un hardware reset.
- À
- Résolution :
- Instanciation dans
init()d'un objetOSObjectvalide :fFaultReporter = OSString::withCString("QualcommFaultReporter"); - Surcharge de
getFaultReporterFromDriver()retournantfFaultReporter. - Libération propre dans
free()viaOSSafeReleaseNULL(fFaultReporter). - Dans le noyau,
findAndAttachToFaultReporter()constate que le pointeur est non-nul, stocke l'adresse dansivars->_faultReporter, incrémente la référence viaobj->retain()(*0x20(%rax)), et aucun panic n'est émis.
- Instanciation dans
INC-05 : Échec d'allocation IO80211RNGAgent / getLogger()
- Symptôme :
Échec dans
IO80211Controller::startà l'étape d'initialisation du générateur aléatoire Wi-Fi avec le log :Failed to allocate RNG Agent. - Cause Racine :
L'inspection binaire de
IO80211RNGAgent::withOptions()(0xffffff80022dd55c) a montré qu'à l'offset+0xe6, la fonction appellethis->getLogger()(vtable slot 426,*0xd40(%rax)). Si ce pointeur est nul, elle saute directement en erreur (+0x12f), détruit le descripteur et renvoieNULL. - Résolution :
Instanciation dans
init()d'un objetfLogger = OSString::withCString("QualcommLogger"), et implémentation degetLogger()retournant(void*)fLogger.RNGAgenteffectue son appellogger->retain()et s'initialise avec succès.
INC-06 : Mésalignement du Cycle Kext OSBundleRequired
- Symptôme : Tentative de matching du pilote PCI trop précoce lors de la séquence de boot, provoquant des échecs d'allocation de services réseau.
- Cause Racine :
Dans
Info.plist,OSBundleRequiredavait été temporairement passé àRoot. Or,IO80211Family.kextd'Apple spécifie impérativementNetwork-Root. DéclarerRootforce le chargement du pilote avant que la pile des protocoles réseau et des sockets du noyau ne soit prête. - Résolution :
Rétablissement strict de :
<key>OSBundleRequired</key> <string>Network-Root</string>
INC-07 : Blocage du Téléversement BHIe par ASPM PCIe
- Symptôme : Timeout lors du transfert de l'image AMSS complète (~6 Mo) via BHIe, le statut BHI restant figé en boucle.
- Cause Racine :
La gestion d'énergie active du bus PCIe (Active State Power Management - ASPM) tente de basculer la liaison en veille L0s ou L1 pendant les transferts DMA volumineux, corrompant les accès aux registres MMIO de notification de sonnette (
ATH12K_BHIE_TXVECDB). - Résolution :
Désactivation dynamique d'ASPM avant tout transfert firmware :
UInt8 pciCapOffset = 0; if (fPciDevice->findPCICapability(kIOPCIPCIExpressCapability, &pciCapOffset) && pciCapOffset != 0) { UInt16 linkCtrl = fPciDevice->configRead16(pciCapOffset + 0x10); linkCtrl &= ~0x3; // Désactive L0s et L1 fPciDevice->configWrite16(pciCapOffset + 0x10, linkCtrl); }
INC-08 : Échec et Corruption DMA sur Plateformes AMD 64-bit (> 4 Go RAM)
- Symptôme : Gel ou reboot brutal lors de l'accès aux anneaux de commandes ou lors du démarrage du Primary Boot Loader.
- Cause Racine :
Le SoC WCN7850 possède une unité DMA adressable sur 36 bits maximum (plage physique
0x000000000à0xFFFFFF000). Sur les cartes mères équipées de 32 Go ou 64 Go de RAM, les allocations kernel classiques peuvent renvoyer des tampons au-dessus de 64 Go. Les bits de poids fort étaient tronqués par le matériel, écrivant dans des zones mémoires arbitraires du noyau. - Résolution :
Toutes les allocations DMA contiguës sont contraintes par un masque physique 36 bits :
#define ATH12K_DMA_MASK_36BIT_PAGE 0xFFFFFF000ULL IOBufferMemoryDescriptor::inTaskWithPhysicalMask( kernel_task, kIODirectionInOut | kIOMemoryPhysicallyContiguous, size, ATH12K_DMA_MASK_36BIT_PAGE );
INC-09 : Redémarrages Automatiques Invisibles sans Affichage de KP
- Symptôme : En cas de panic du noyau, le PC redémarrait instantanément sans donner le temps à l'utilisateur de lire la pile d'appels ou la cause du crash.
- Cause Racine : Par défaut, le noyau XNU sous OpenCore réinitialise le processeur en cas de panique non interceptée.
- Résolution :
Injection de
panic=0dans lesboot-argsOpenCore du fichierconfig.plist. En cas d'erreur critique, le noyau gèle l'affichage, permettant de lire textuellement le fichier source et la ligne fautive (ex:IO80211Controller.cpp:2877).
INC-10 : Triple Fault Processeur ("Coupure de Courant") lors de l'appel CCLogStream::shouldLog sur fLogger
- Symptôme :
Lors du démarrage d'OpenCore avec
QualcommWifi.kextactif, le PC s'éteint et redémarre instantanément comme une coupure de courant brutale, sans panic log ni rapport d'erreur NVRAM. - Cause Racine (Breakthrough Fondamental) :
- Lors du commit
5357ca6,getLogger()avait été implémenté en renvoyant une instance d'OSString(OSString::withCString("QualcommLogger")). - Lors de l'exécution de
IO80211Controller::start()->createIOReporters()->addReporterLegend(), le noyau macOS invoqueCCLogStream::shouldLog(this->getLogger(), level)(0xffffff80032ff9e6). - Le désassemblage de
CCLogStream::shouldLoga révélé :movq 0x90(%rdi), %rax ; Lit l'offset +0x90 de l'objet passé en rdi (fLogger) movq 0x58(%rax), %rax ; Déréférence ce pointeur en Ring 0 ! cmpl 0x8(%rax), %esi ; Compare le niveau de log setle %al retq - L'objet
OSStringne mesurant que 32 octets (0x20), l'accès à+0x90pointait sur une adresse mémoire arbitraire ou non mappée dans le heap kernel. Le déréférencement immédiat de cette valeur viamovq 0x58(%rax), %raxdéclenchait une faute de page (#PF) ou une protection générale (#GP) non interceptée en mode superviseur (Ring 0), provoquant un Triple Fault CPU réinitialisant la carte mère instantanément. - Parallèlement,
IO80211RNGAgent::withOptions()(0xffffff80022dd55c) etIO80211ControllerMonitor::initWithControllerAndProvider()(0xffffff80023929d0) requièrent impérativement quegetLogger()retourne un pointeur non-nul et appellentlogger->retain()(*0x20(%rax)).
- Lors du commit
- Résolution Technique :
- Conception d'une classe
FakeLogStream : public OSObjectrespectant au millimètre l'ABI deCCLogStream:- Taille minimale supérieure à
0x98octets avecp1placé exactement à l'offset+0x90(validé parstatic_assert(__builtin_offsetof(FakeLogStream, p1) == 0x90)). p1pointe vers une zone mémoirefP1Mem(0x1000octets) dont l'offset+0x58pointe versfP2Mem(0x100octets initialisés à 0).CCLogStream::shouldLogetCCLogStream::log*lisent ainsi0x90 -> 0x58 -> 0, constatent un niveau de log nul, et retournent proprementfalsesans tenter d'émettre sur un pipe inexistant.FakeLogStreamétant unOSObject, les appelsretain()etrelease()effectués parRNGAgents'exécutent avec une fidélité parfaite.
- Taille minimale supérieure à
- Implémentation locale de
QualcommWifi::getActionFramePoolCapacity()(slot 440) renvoyant0x100(256 frames). - Suppression de l'écriture directe
kIOPCIConfigCommandsur le bus PCIe AMD pour éviter toute déstabilisation du root port.
- Conception d'une classe
INC-11 : Divergence Verbose / Non-Verbose, Heap Dynamique Précoce et Matching Prématuré
- Symptômes :
- Avec mode verbose (
-v) : Réinitialisation instantanée du PC ("reset comme une coupure de courant"). - Sans mode verbose (non-verbose) : Blocage complet et statique sur le logo Apple OpenCore sans redémarrage.
- Avec mode verbose (
- Causes Racines (Breakthroughs Majeurs) :
- Comportement XNU &
debug=0x100:- En mode non-verbose,
debug=0x100désactive l'écran graphique de panique ("Your computer restarted...") et fige l'exécution sur le framebuffer existant (logo Apple). - En mode verbose (
-v), la routinedisplay_panictente d'écrire la trace sur le framebuffer console en cours d'initialisation par le pilote NVIDIA RTX 4070 SUPER (nvfb=1 nvaccel=1 nvfbheads=4). L'accès concurrent au contrôleur vidéo déclenche une double faute / triple faute CPU (reset immédiat). - Le phénomène sous-jacent est donc rigoureusement identique dans les deux cas.
- En mode non-verbose,
- Vulnérabilité d'Allocation Précoce dans
FakeLogStream:FakeLogStream::init()utilisait des allocations dynamiquesIOMallocZero(0x1000)etIOMallocZero(0x100).- Durant le boot très précoce (phase IOKit matching des kexts
Network-Root), le heap kernel peut refuser les allocations dynamiques, laissantp1nul.CCLogStream::shouldLogdéréférençait alors la page 0 (0x90(%rdi) -> 0x58(%rax)).
- Anti-Patron IOKit
registerService()sur le Contrôleur :QualcommWifi::start()appelait directementregisterService()sur le contrôleurIO80211Controlleret publiait des dictionnaires de médiums Ethernet (publishMediumDictionary).- Sous macOS Tahoe 26.x,
IO80211Controllerne publie aucun médium Ethernet, et ne doit JAMAIS appelerregisterService()sur lui-même. C'est l'interface Skywalk fille (attachInterface) qui enregistre son propre service. - L'appel de
registerService()sur le contrôleur déclenchait un matching prématuré parairportdetIO80211Familyavant que toute interface Skywalk ne soit attachée, provoquant une panique immédiate.
- Comportement XNU &
- Résolutions Techniques :
- Buffers Statiques BSS pour
FakeLogStream:- Remplacement de
IOMallocZeropar des tampons statiques BSSgP1Mem[0x1000]etgP2Mem[0x100]alignés sur 64 octets. - Garantis 100% alloués, mappés et initialisés dès le chargement du binaire Mach-O sans solliciter le heap.
- Remplacement de
- Persistance Réelle NVRAM (
logNvramStage) :- Implémentation directe via
IORegistryEntry::fromPath("/options", gIODTPlane)->setProperty("qcom_stage", tag). - Permet une traçabilité absolue des étapes atteintes même à travers une réinitialisation brutale (
nvram -p | grep qcom_stage).
- Implémentation directe via
- Suppression de
publishMediumDictionaryet Contrôle deregisterService:- Élimination des médiums réseau obsolètes.
- Contrôle strict de
registerService()via le boot-argqcom_register=1(désactivé par défaut) pour garantir un démarrage fluide et sécurisé jusqu'au bureau macOS.
- Buffers Statiques BSS pour
INC-12 : Vtables Tronquées à 38 Slots et Déréférencement 0x88 -> 0x48 Page 0 dans CCLogStream::logv
- Symptômes :
- Malgré la désactivation du matching automatique (
qcom_register=0), le contournement des accès MMIO (qcom_mmio=0), et l'utilisation de buffers statiques BSS, le système continue de redémarrer brutalement en mode verbose ("coupure de courant") ou de figer sur le logo Apple en mode non-verbose. - La variable NVRAM
qcom_stagen'apparaît pas au redémarrage car l'UEFI OpenCore write-protecte l'écriture des variables à l'exécution du noyau (general error).
- Malgré la désactivation du matching automatique (
- Causes Racines (Breakthroughs Majeurs & Rétro-ingénierie Noyau) :
- Asymétrie critique des Vtables (
CCLogStreamvsOSObject) :- Dans le Kernel Collection
BootKernelExtensions.kc,CCLogStreamhérite deCCStream, qui hérite lui-même deIOService. IOServicetotalise 266 slots virtuels (jusqu'à l'offset0x848).CCLogStreamy adjoint 14 méthodes virtuelles spécifiques (slots 266 à 279, jusqu'à0x8b8) :profileLoaded(),profileRemoved(),initWithPipeAndName(),setLevel(int)(0x868),getLevel()(0x870),setFlags(uint64_t)(0x878),getFlags()(0x880),setConsoleLogFlag(uint64_t)(0x8b0), etc. Sa vtable totale__ZTV11CCLogStreammesure exactement0x8d8octets (283 slots).- Or, notre classe
FakeLogStreamhéritait initialement deOSObject, dont la vtable ne compte que 38 slots (0x130octets). - Dès que le contrôleur stockait
fLoggerdans la variable globale du noyau_sIO80211ControllerGlobalLogger, tout appel àsetLevel(slot 269, offset0x868) ou àIOService::attach(slot 56, offset0x1c0dansIO80211ControllerMonitor::createReportersAndLegend) déréférençait la vtable plus de 1800 octets au-delà de sa limite, exécutant des données corrompues en Ring 0 et déclenchant un Triple Fault immédiat.
- Dans le Kernel Collection
- Déréférencement de Page 0 dans
CCLogStream::logv(movq 0x48(%rax), %rdi) :- Le désassemblage de
CCLogStream::logv(0xffffff80032fd7d4) a démontré qu'en cas de message filtré/supprimé (quand le niveau requis dépasse le seuil configurégP2Mem[0]), la routine tente d'incrémenter le compteur statistique via :movq 0x88(%rbx), %rax ; Lit streamData à l'offset +0x88 de FakeLogStream movq 0x48(%rax), %rdi ; Déréférence streamData->fCoreCaptureStreamReporter ! callq IOSimpleReporter::incrementValue ; Appelle slot 45 (+0x168) - À l'offset
+0x88, notre implémentation contenait une zone mise à zéro. Le déréférencement0x48(%rax)avec%rax == 0générait un Page Fault (#PF) Ring 0 à l'adresse virtuelle0x0000000000000048(Page 0).
- Le désassemblage de
- Troncature analogue sur
FakeFaultReporter:CCFaultReporterhérite deIOServiceavec une vtable de 269 slots (0x868octets) et une taille d'instance de0x90octets.FakeFaultReporterhéritait d'OSObject(38 slots).
- Asymétrie critique des Vtables (
- Résolutions Techniques Appliquées :
- Héritage
IOServiceet Déclaration des 14 SlotsCCLogStream:class FakeLogStream : public IOServiceimplémente les 14 méthodes virtuelles (slots 266 à 279) avec des retours sûrs no-op.- Sa vtable
__ZTV13FakeLogStreamatteint exactement0x8d8octets (283 slots), en conformité binaire stricte à 100% avec l'ABI Apple deBootKernelExtensions.kc.
- Topologie Mémoire 0x98 Octets et Bouchon Reporter Factice :
streamDataà l'offset+0x88pointe versgStreamMem[0x100].- L'offset
gStreamMem + 0x48pointe vers un objetgDummyReporterdont la table de pointeursgDummyReporterVtable[256]intercepte le slot 45 (getFirstElementIndexà+0x168) avecdummyReporterFunc(retourkIOReturnSuccess). p1à l'offset+0x90pointe versgP1Mem[0x1000], dont+0x58pointe versgP2Mem[0x100].- Taille totale de l'instance fixée à
0x98octets (static_assert(sizeof(FakeLogStream) == 0x98)).
- Mise en Conformité de
FakeFaultReporter:class FakeFaultReporter : public IOServiceavec padding0x90 - sizeof(IOService). Vtable de 269 slots (0x868octets) et taille0x90octets (static_assert(sizeof(FakeFaultReporter) == 0x90)).
- Héritage
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) :
- Désassemblage critique de
IONetworkController::start(provider)(0xffffff80026f3650) :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, ...) - Origine du Pointeur Null et Incompatibilité d'ABI
IOWorkLoopvsIO80211WorkQueue:- Dans
QualcommWifi.cpp, l'implémentation initiale degetWorkLoop()redirigeait directement versgetWorkQueue()(un objetIO80211WorkQueue). - Le désassemblage de
IO80211WorkQueue::getThread()(0xffffff80022ee1ea) dansBootKernelExtensions.kca révélé son implémentation exacte :ffffff80022ee1ea <IO80211WorkQueue::getThread()>: pushq %rbp movq %rsp, %rbp xorl %eax, %eax ; RETOURNE CONSTAMMENT NULL (0) ! popq %rbp retq - Contrairement à un véritable
IOWorkLoopqui maintient un Mach thread dédié (0x30(%rdi)),IO80211WorkQueueretourne toujours0. thread_policy_set_internal(thread_t thread, ...)présume que le thread passé en paramètre est non-nul et exécuteIOLockLock(&thread->mutex)où le mutex de structurethreadse situe à l'offset+0x658.- Le déréférencement
IOLockLock(0 + 0x658)a déclenché le Page Fault#PFsurCR2: 0x0000000000000658.
- Dans
- Désassemblage critique de
- Résolutions Techniques Appliquées :
- Séparation Stricte des Interfaces
IOWorkLoopetIO80211WorkQueue:- Ajout d'une instance dédiée
IOWorkLoop *fWorkLoop;allouée viaIOWorkLoop::workLoop()dansQualcommWifi::createWorkLoop(). QualcommWifi::getWorkLoop()retournefWorkLoop ? fWorkLoop : (getProvider() ? getProvider()->getWorkLoop() : nullptr);.QualcommWifi::createWorkQueue()etgetWorkQueue()conserventIO80211WorkQueue *fWorkQueue;allouée viaIO80211WorkQueue::workQueue().
- Ajout d'une instance dédiée
- Séquence d'Initialisation et Nettoyage :
- Dans
QualcommWifi::start(provider): instanciation séquentielle decreateWorkLoop()puiscreateWorkQueue()avant l'invocation desuper::start(provider). - Dans
IONetworkController::start,workLoop->getThread()invoque désormais l'implémentation native deIOWorkLoop::getThread(movq 0x30(%rdi), %rax), fournissant unthread_tvalide àml_thread_policy, et permettant l'attachement propre duIOCommandGateviafWorkLoop->addEventSource. - Dans
QualcommWifi::stop()etQualcommWifi::free(): libération propre des deux objets viaOSSafeReleaseNULL(fWorkLoop)etOSSafeReleaseNULL(fWorkQueue).
- Dans
- Séparation Stricte des Interfaces
INC-14 : Page Fault Kernel Trap CR2: 0x0000000000000001 par Déroutement Vtable Virtuel dans IO80211SkywalkInterface::printDataPath
- Symptômes :
- Bootloop immédiat (triple fault / redémarrage instantané du kernel) dès l'activation de
qcom_skywalk=1. - Machine bloquée en boucle de redémarrage sans affichage de panic log si NVRAM inaccessible.
- Bootloop immédiat (triple fault / redémarrage instantané du kernel) dès l'activation de
- Causes Racines (Breakthroughs Majeurs & Rétro-ingénierie Noyau) :
- Déclarations
virtualindues dans les headers C++ (IO80211SkywalkInterface.h) :- Dans le header reconstruit
QualcommWireless/include/IOKit/80211/IO80211SkywalkInterface.h, les méthodesinit(IOService*, ether_addr*)etsetInterfaceRole(UInt32)étaient déclarées avec le mot-clévirtual. - Or, l'analyse binaire approfondie de
BootKernelExtensions.kcdémontre sans équivoque :IO80211SkywalkInterface::init(IOService*, ether_addr*)(0xffffff80022f56e2) est une fonction membre NON-VIRTUELLE (seuleinit(void)à l'offset0x88hérite d'OSObject::init).IO80211SkywalkInterface::setInterfaceRole(UInt32)(0xffffff80022f60f0) est une fonction membre NON-VIRTUELLE qui écrit directementroledansthis->ivars + 0x58.
- Dans le header reconstruit
- Collision catastrophique de table virtuelle (Vtable Desynchronization) :
- En déclarant ces deux méthodes comme
virtual, Clang leur a assigné deux nouveaux slots virtuels à la fin de la vtableIOSkywalkEthernetInterface, aux offsets+0xc50(slot 394) et+0xc58(slot 395). - Dans l'objet réel
IO80211SkywalkInterfaceinstancié par le noyau (BootKernelExtensions.kc), la vtable native possède 461 slots (0xe68octets) :- Le slot
+0xc50correspond en réalité àIO80211SkywalkInterface::wowEnabled(). - Le slot
+0xc58correspond àIO80211SkywalkInterface::printDataPath(userPrintCtx*).
- Le slot
- L'appel
fSkywalkInterface->setInterfaceRole(1)a émis l'instruction assembleurcallq *0xc58(%rax). - Le noyau a donc exécuté
printDataPathavec%rdi = fSkywalkInterfaceet%rsi = 0x1(le rôle 1 transmis comme pointeuruserPrintCtx*). - Lors du premier déréférencement mémoire de
%rsià l'adresse0x1, le processeur a déclenché un Page Fault#PFsurCR2: 0x0000000000000001, entraînant un crash critique et un redémarrage instantané (triple fault).
- En déclarant ces deux méthodes comme
- Déclarations
- Résolutions Techniques Appliquées :
- Suppression du Qualificatif
virtual:- Retrait strict de
virtualdevantbool init(IOService *controller, ether_addr *mac);etvoid setInterfaceRole(UInt32 role);dansQualcommWireless/include/IOKit/80211/IO80211SkywalkInterface.h.
- Retrait strict de
- Vérification Binaire de l'Assembleur Généré :
- Le désassemblage de
build/Release/QualcommWifi.kextatteste de la conversion en appels directs non-virtuels :0x2d9e: callq __ZN23IO80211SkywalkInterface4initEP9IOServiceP10ether_addr 0x2dc3: callq __ZN23IO80211SkywalkInterface16setInterfaceRoleEj 0x2de1: callq *0x670(%rax) ; fSkywalkInterface->attach(this) 0x2e08: callq *0x5c0(%rax) ; fSkywalkInterface->start(this) 0x2e31: callq *0xdc8(%rax) ; this->attachInterface(fSkywalkInterface, nullptr) 0x2e4f: callq *0x5b0(%rax) ; fSkywalkInterface->registerService()
- Le désassemblage de
- Conformité des Dépendances :
kmutil libraries -p build/Release/QualcommWifi.kextvalide la résolution intégrale des symboles auprès deBootKernelExtensions.kc.
- Suppression du Qualificatif
INC-15 : Kernel Trap / Triple Fault dans initIvars & Incohérence d'Attachement Skywalk
- Symptômes :
- Bootloop persistant et redémarrage brutal de la machine lors de la phase d'instanciation de
IO80211SkywalkInterface. - Machine incapable de franchir le stade d'attachement réseau natif sans désactivation manuelle de
QualcommWifi.kextdans OpenCore.
- Bootloop persistant et redémarrage brutal de la machine lors de la phase d'instanciation de
- Causes Racines (Breakthroughs & Rétro-ingénierie du Kernel Collection) :
- Piège du tas non initialisé (Uninitialized Heap Trap) dans
initIvars(0xffffff80022f520c) :_ZN23IO80211SkywalkInterfacenwEm(0x128)alloue la mémoire de l'objet viakalloc_type. Le noyau ne met pas la mémoire à zéro.- Dans le constructeur et dans
IO80211SkywalkInterface::initIvars, le noyau évalue :0xffffff80022f5215: cmpq $0x0, 0x120(%rdi) 0xffffff80022f521d: je 0xffffff80022f5265 ; Alloue ivars si NULL ... 0xffffff80022f5244: movq 0x120(%rbx), %rdi ; Garbage pointer ! 0xffffff80022f524b: movl $0xf0, %esi 0xffffff80022f5250: movl $0xf0, %ecx 0xffffff80022f5255: xorl %edx, %edx 0xffffff80022f5257: callq memset ; memset(garbage, 0, 0xf0) -> Page Fault Ring 0 ! - Si
mem + 0x120contient des résidus non nuls de la mémoire du tas, le noyau tente unmemsetde0xf0octets sur cette adresse invalide, causant un Crash Kernel Page Fault / Triple Fault instantané.
- Header erroné
network/IOSkywalkEthernetInterface.h:- Le fichier
QualcommWireless/include/IOKit/network/IOSkywalkEthernetInterface.hdéclarait de manière erronéeclass IOSkywalkEthernetInterface : public IOEthernetController. - Dans l'architecture Apple Skywalk,
IOSkywalkEthernetInterfacehérite en réalité deIOSkywalkNetworkInterface->IOSkywalkInterface->IOService, sans aucun lien d'héritage avecIOEthernetController.
- Le fichier
- Ordre d'attachement et second argument du contrôleur (Slot 441) :
- Dans
AppleBCMWLANCore::createSkywalkInterface(0xffffff80016aaa8a), Apple invoquethis->attachInterface(fSkywalkInterface, this)(slot 441 d'IO80211Controller) avantfSkywalkInterface->attach(this)etfSkywalkInterface->start(this). - De plus, le second paramètre transmis doit être
this(le contrôleur), ce qui renseignecontroller->ivars + 0x188(fPrimaryInterface). L'ancien code passaitnullptraprèsstart().
- Dans
- Publication prématurée via
registerService():- Appeler
registerService()surfSkywalkInterfacepublie le service auprès du système IOKit matching et d'airportdavant que les queues Skywalk et les pools de buffers ne soient négociés, provoquant une panique réseau immédiate.
- Appeler
- Piège du tas non initialisé (Uninitialized Heap Trap) dans
- Résolutions Techniques Appliquées :
- Remise à zéro explicite du bloc mémoire avant construction :
void *mem = _ZN23IO80211SkywalkInterfacenwEm(0x128); if (!mem) return nullptr; bzero(mem, 0x128); _ZN23IO80211SkywalkInterfaceC1EPK11OSMetaClass(mem, &_ZN23IO80211SkywalkInterface10gMetaClassE); - Correction des inclusions d'en-têtes Skywalk :
- Redirection du faux header vers
<IOKit/skywalk/IOSkywalkEthernetInterface.h>et ajout dutypedef uint32_t kern_packet_svc_class_t;dansIOSkywalkTypes.h.
- Redirection du faux header vers
- Ordonnancement conforme du cycle d'attachement Skywalk :
- Invocation de
attachInterface(fSkywalkInterface, this)au slot 441 avantattach(this)etstart(this).
- Invocation de
- Contrôle granulaire d'exécution par palier via le boot-arg
qcom_step:- Introduction du palier
qcom_step(par défaut6= exécution complète destart(this)sans appeler prématurémentregisterService()). - Validation binaire par désassemblage et validation
kmutil librariesà 100%.
- Introduction du palier
- Remise à zéro explicite du bloc mémoire avant construction :
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(commit506df09). - Nécessité de désactiver manuellement
QualcommWifi.kextdans OpenCore pour atteindre le bureau macOS.
- Bootloop persistant et redémarrage spontané de la machine lors du chargement de
- Causes Racines (Breakthroughs & Rétro-ingénierie du Kernel Collection) :
- Piège de Libération sans
initIvarsdansIO80211SkywalkInterface::free()(0xffffff80022f6184) :- Le désassemblage de
IO80211SkywalkInterface::freerévèle :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 queinitIvarsn'a pas encore été invoqué,0x120estNULL. Tout appel subséquent àrelease()/free()déclenche un Kernel Page Fault instantané surCR2: 0x00000000000000b8!
- Le désassemblage de
- 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'appelfSkywalkInterface->start(this). - Dans
start(this), Apple initialiseCCPipe,CCStream,IO80211PeerManager, la gestion d'énergiejoinPMtree/registerPowerDriver/changePowerStateTo(0),IO80211Glue,WCLGlue, etIO80211MacAddressAgent. Un échec non géré dans ces sous-systèmes sans isolation pas-à-pas provoquait le bootloop.
- Dans le commit
- Nettoyage non gardé dans
stop():QualcommWifi::stopinvoquait inconditionnellementdetachInterface,fSkywalkInterface->stop(this)etfSkywalkInterface->detach(this). Or,IO80211SkywalkInterface::stopdéréférence égalementivars + 0x80(movq 0x80(%rax), %rdi), causant un crash en cascade si l'interface n'était pas entièrement démarrée.
- Piège de Libération sans
- Résolutions Techniques Appliquées :
- Invocation explicite et systématique de
initIvars()dès la création :- Déclaration et appel de
_ZN23IO80211SkywalkInterface9initIvarsEv(mem)danscreateSkywalkInterfacedès l'allocation, garantissant queivarsest toujours non-nul et immunisé contre tout#PFdansfree().
- Déclaration et appel de
- 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 viaqcom_step.
- Protection conditionnelle du cycle de vie dans
stop()via des drapeaux d'état :- Ajout de
fSkywalkCtrlAttached,fSkywalkAttached,fSkywalkStartedpour ne détacher ou arrêter que ce qui a été effectivement attaché ou démarré avec succès.
- Ajout de
- Validation intégrale de la chaîne de compilation :
build_wifi.shvalidé à 100% aveckmutil librariesauprès deBootKernelExtensions.kc.
- Invocation explicite et systématique de
INC-17 : Page Fault sur Table de Propriétés NULL par Court-Circuit de super::init(0)
- Symptômes :
- Bootloop lors du passage au stade 5 (commit
79d7726), alors que le stade 1 (commitb7e32db) démarrait impeccablement jusqu'au bureau macOS.
- Bootloop lors du passage au stade 5 (commit
- Causes Racines (Breakthroughs & Rétro-ingénierie du Kernel Collection) :
- Analyse de la condition d'initialisation dans
IO80211SkywalkInterface::init(0xffffff80022f56e2) :- Le désassemblage précis montre la logique suivante :
0xffffff80022f5710: cmpq $0x0, 0x120(%r14) ; ivars est-il NULL ? 0xffffff80022f5718: jne 0xffffff80022f5733 ; Si NON-NULL, SAUTE super::init(0) ! 0xffffff80022f571a: movq 0x15aa07(%rip), %rax ; __ZTV26IOSkywalkEthernetInterface 0xffffff80022f5724: movq %r14, %rdi 0xffffff80022f5729: callq *0x258(%rax) ; super::init(0) -> IOService::init() 0xffffff80022f5733: movq %r14, %rdi 0xffffff80022f5736: callq initIvars ; initIvars() - Dans le commit
b7e32db/79d7726, nous appelionsinitIvarsdèscreateSkywalkInterface()pour éviter le piège defree(). - Par conséquent, lors de l'appel à
fSkywalkInterface->init(...),mem + 0x120était déjà non-nul ! - Le saut conditionnel
jne 0x5733a donc été emprunté, court-circuitant totalement l'appelsuper::init(0)(IOService::init()) !
- Le désassemblage précis montre la logique suivante :
- Conséquence fatale sur
IOServiceet les tables de propriétés :- Sans
IOService::init(), l'infrastructure de base de l'objet (dictionnaire de propriétésfPropertyTable, table de registre IORegistry, verrous de synchronisation) n'était pas allouée. - Dès l'étape suivante (
setInterfaceRole(1)), le noyau exécutaitsetPropertyHelper(this, "IO80211InterfaceRole", ...)qui tente d'écrire dans la table de propriétés NULL de l'objet, déclenchant un Kernel Page Fault instantané !
- Sans
- Analyse de la condition d'initialisation dans
- Résolutions Techniques Appliquées :
- Suppression de l'appel prématuré à
initIvarsdanscreateSkywalkInterface:createSkywalkInterfacene fait plus queoperator new+ mise à zéro explicite (bzero(0x128)) + constructeur C1.mem + 0x120reste strictement NULL.fSkywalkInterface->init(...)constate0x120 == 0, invoque légitimementsuper::init(0)pour initialiserIOService, puis invoqueinitIvars().- L'objet est alors 100% cohérent à la fois côté
IOServiceet côtéIO80211SkywalkInterface.
- Calibrage sécurisé du palier par défaut sur l'étape 2 (
skywalk_step=2) :- Permet de valider isolément que
createSkywalkInterface+init(this, mac)démarre avec succès sans risque d'effets de bord ultérieurs.
- Permet de valider isolément que
- Validation binaire et dépendances :
- Compilation
build_wifi.shet validationkmutil librariesà 100%.
- Compilation
- Suppression de l'appel prématuré à
INC-18 : Page Fault CR2: 0xb8 dans free() et Manque de boot-arg OpenCore pour le Stade 1
- Symptômes :
- Bootloop sur le stade 2 (commit
0519af4) lorsqueinitIvarsa été retiré decreateSkywalkInterface. - L'utilisateur n'a pas pu forcer le retour au stade 1 car le boot-arg
qcom_step=1n'était pas présent dans leconfig.plistd'OpenCore.
- Bootloop sur le stade 2 (commit
- Causes Racines (Breakthroughs & Rétro-ingénierie du Kernel Collection) :
- Déréférencement aveugle dans
IO80211SkywalkInterface::free()(0xffffff80022f6184) :- Le désassemblage précis de
free()révèle :0xffffff80022f618e: mov rax, qword ptr [rdi + 0x120] ; rax = ivars 0xffffff80022f6195: mov rdi, qword ptr [rax + 0xb8] ; [ivars + 0xb8] (sans test rax!=0 !) - Si
init(...)échoue, retournefalseou sifree()est déclenché alors que0x120est NULL, le processeur exécutemov rdi, [0 + 0xb8], provoquant un#PFimmédiat surCR2: 0x00000000000000b8. - Le stade 1 (commit
b7e32db) avait démarré jusqu'au bureau avec un succès total précisément parce queinitIvarsallouait0x120dèscreateSkywalkInterface, immunisant l'objet contre tout crash de libération.
- Le désassemblage précis de
- Absence du boot-arg
qcom_stepdans OpenCore :- Le
config.plistne contenait pasqcom_step, forçant le pilote à utiliser la valeur hardcodée en C++ (skywalk_step = 2).
- Le
- Déréférencement aveugle dans
- Résolutions Techniques Appliquées :
- Restauration de
initIvarsdanscreateSkywalkInterface:- Garantit que
mem + 0x120est toujours non-nul, éliminant définitivement le piège defree()(CR2: 0xb8).
- Garantit que
- Rétablissement du palier par défaut sur l'étape 1 (
skywalk_step = 1) :- Garantit le boot immédiat jusqu'au bureau macOS comme au commit
b7e32db.
- Garantit le boot immédiat jusqu'au bureau macOS comme au commit
- Injection explicite de
qcom_step=1dans lesboot-argsOpenCore (config.plist) :- Ajouté dans
/Volumes/EFI/EFI/OC/config.plistsous NVRAM7C436110-AB2A-4BBB-A880-FE41995C9F82->boot-args. - Permet à l'utilisateur de modifier le palier directement depuis OpenCore ou NVRAM.
- Ajouté dans
- Déploiement et synchronisation EFI :
QualcommWifi.kextcompilé et copié sur/Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext(MD5:388636543393e9cf34e5816e4a011cce).- Vérifié
QualcommWifi.kextactivé (Enabled: True) dansconfig.plist.
- Restauration de
INC-19 : Découverte de IO80211SkywalkInterface::init() (Sans Arguments) et Résolution Définitive de la Table de Propriétés pour le Stade 3
- Symptômes & Constats :
- Succès total du Stade 2 (
qcom_step=2) :- Démarrage 100% propre jusqu'au bureau macOS avec
QualcommWifi.kext(1.1.0) etQualcommWireless.kext(1.1.0) chargés simultanément. - Journal noyau (
/usr/bin/log) confirmant l'exécution parfaite :IO80211SkywalkInterface::init with controller start IO80211SkywalkInterface::initIvars complete IO80211SkywalkInterface::init complete - IORegistry confirmant l'attachement propre de l'objet
QualcommWifiavec ses pipes CoreCapture (CCLogPipeUserClient).
- Démarrage 100% propre jusqu'au bureau macOS avec
- Succès total du Stade 2 (
- Causes Racines (Breakthroughs & Rétro-ingénierie du Kernel Collection) :
- Désassemblage exhaustif de
IO80211SkywalkInterface::init()sans arguments (0xffffff80022f568e) :Cette méthode effectue exactement la chaîne complète attendue :0xffffff80022f56b5: mov rax, qword ptr [rip + 0x15aa6c] ; vtable de IOSkywalkEthernetInterface 0xffffff80022f56bc: mov rdi, rbx 0xffffff80022f56bf: xor esi, esi ; OSDictionary* = NULL 0xffffff80022f56c1: call qword ptr [rax + 0x258] ; IOSkywalkEthernetInterface::init(nullptr) 0xffffff80022f56c7: test al, al 0xffffff80022f56c9: je 0x22f56d9 0xffffff80022f56cb: mov rdi, rbx 0xffffff80022f56d4: jmp 0xffffff80022f520c ; jmp initIvars() !- Appel de
IOSkywalkEthernetInterface::init(nullptr)->IOSkywalkNetworkInterface::init(nullptr)->IOService::init(nullptr). - Allocation de
fPropertyTable,fRegistryTable, etfLocksurIOService. - Allocation du verrou interne à l'offset
+0x118(IOLockAlloc). - Appel terminal à
initIvars()pour allouer la structureivarsà l'offset+0x120.
- Appel de
- Comportement de
IO80211SkywalkInterface::setInterfaceRole(UInt32 role)(0xffffff80022f60f0) :- Écrit le rôle dans
ivars + 0x58(1 = kIO80211InterfaceRoleInfra). - Appelle
IOService::SetProperties()avec la clé"IO80211InterfaceRole"et valeur"Infra". - Grâce à l'appel préalable à
IO80211SkywalkInterface::init(),fPropertyTableest garantie allouée et valide, éliminant totalement le risque de Page Fault lors desetInterfaceRole.
- Écrit le rôle dans
- Désassemblage exhaustif de
- Résolutions Techniques Appliquées :
- Intégration du symbole exporté
_ZN23IO80211SkywalkInterface4initEvdanscreateSkywalkInterface:- Invoqué immédiatement après le constructeur C1 pour initialiser la chaîne complète d'héritage (
IOService+IOSkywalkEthernetInterface+ lock+0x118+ivars+0x120).
- Invoqué immédiatement après le constructeur C1 pour initialiser la chaîne complète d'héritage (
- Passage au palier 3 (
qcom_step=3) dans OpenCore :boot-argsmis à jour dans/Volumes/EFI/EFI/OC/config.plist:qcom_step=3pour exécuterfSkywalkInterface->setInterfaceRole(1).
- Compilation et déploiement :
build_wifi.shvalidé à 100% aveckmutil libraries.- Kext déployé sur
/Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext.
- Validation Live du Stade 3 (
qcom_step=3) :- Boot 100% propre jusqu'au bureau macOS.
QualcommWifi.kext(1.1.0) etQualcommWireless.kext(1.1.0) chargés simultanément.- Initialisation complète et configuration du rôle
kIO80211InterfaceRoleInfra(IO80211InterfaceRole= "Infra") sans incident.
- Validation Live du Stade 4 (
qcom_step=4) :- Boot 100% propre jusqu'au bureau macOS avec les deux kexts chargés.
- Exécution nominale de
attachInterface(fSkywalkInterface, this)(slot 441 deIO80211Controller). IONetworkController::prepare()etIOEthernetController::publishProperties()exécutés avec succès.- Publication dans l'IORegistry des propriétés réseau Ethernet fondamentales (
IOMACAddress = <00037f123456>,IOMaxPacketSize = 1518,IOMinPacketSize = 64,IOPacketFilters). - Liaison établie dans les structures internes du contrôleur (
controller->ivars + 0x188 = fSkywalkInterface).
- Validation Live du Stade 5 (
qcom_step=5) :- Boot 100% propre jusqu'au bureau macOS avec les deux kexts chargés.
- Confirmation dans l'IORegistry de l'attachement effectif de
IO80211SkywalkInterfacesous l'arborescenceQualcommWifi. - Propriété
"IO80211InterfaceRole" = "Infrastructure"vérifiée sur le nœudIO80211SkywalkInterface. - L'arborescence matérielle IOKit complète est désormais formée (
QualcommWifi->CCPipe,CCLogStream,IO80211SkywalkInterface).
- Armement du Stade 6 (
qcom_step=6) :boot-argsconfiguré surqcom_step=6dans/Volumes/EFI/EFI/OC/config.plistpour démarrer le sous-système Skywalk (fSkywalkInterface->start(this): initialisation PMtree, PeerManager, IO80211Glue et association au WorkQueue).
- Intégration du symbole exporté
INC-20 : Analyse de l'Échec du Stade 6 (qcom_step=6) & Résolution par Implémentation du Power Management IOKit
- Date : 18 Octobre 2026
- Composant :
QualcommWifi.kext/IO80211SkywalkInterface::start(IOService *controller) - Symptômes & Constats :
- Lors du test initial de
qcom_step=6, la machine a rencontré un redémarrage en boucle (bootloop). - L'utilisateur est revenu temporairement sur
qcom_step=5, qui a redémarré de manière 100% stable jusqu'au bureau avecIO80211SkywalkInterfaceparfaitement attaché (IO80211InterfaceRole= "Infrastructure").
- Lors du test initial de
- Causes Racines (Breakthroughs & Rétro-ingénierie du Kernel Collection) :
- Analyse de la séquence interne de
IO80211SkywalkInterface::start(IOService *provider)(0xffffff80022f5792) :- Le désassemblage Capstone direct de
BootKernelExtensions.kca révélé les étapes précises :- Vérification
ivars + 0x28 == NULL(protection contre appel réentrant : panic"Calling start twice in a row is not allowed"). - Appel de
super::start(provider)(IOService::start(provider)). - Cast dynamique du contrôleur via
OSDynamicCast(IO80211Controller, provider). - Récupération et conservation de
controller->getWorkQueue(). - Création des canaux CoreCapture
CCPipeet flux de journalisationCCStream. - Instanciation du
IO80211PeerManagerviacreatePeerManager()(0xffffff80022f7be6) qui alloue le verrou récursifcontroller->allocIO80211RecursiveLock()(slot 435 / offset+0xd98) et leIO80211CommandGate. - Étape critique de gestion de l'énergie (Power Management) :
0xffffff80022f5c6c: mov rax, qword ptr [r12] 0xffffff80022f5c70: mov rdi, r12 0xffffff80022f5c73: call qword ptr [rax + 0x790] ; fSkywalkInterface->PMinit() (slot 242) 0xffffff80022f5c79: mov rax, qword ptr [r15] ; r15 = QualcommWifi (contrôleur) 0xffffff80022f5c7c: mov rdi, r15 0xffffff80022f5c7f: mov rsi, r12 ; rsi = fSkywalkInterface 0xffffff80022f5c82: call qword ptr [rax + 0x7a0] ; QualcommWifi->joinPMtree(fSkywalkInterface) (slot 244) 0xffffff80022f5c88: mov rax, qword ptr [r12] 0xffffff80022f5c8c: lea rdx, [rip + 0x19564d] ; tableau des 2 états IOPMPowerState 0xffffff80022f5c93: mov ecx, 2 0xffffff80022f5c98: mov rdi, r12 0xffffff80022f5c9b: mov rsi, r12 0xffffff80022f5c9e: call qword ptr [rax + 0x7a8] ; fSkywalkInterface->registerPowerDriver(...) (slot 245) 0xffffff80022f5ca4: mov esi, 1 0xffffff80022f5ca9: mov rdi, r12 0xffffff80022f5cac: call 0xffffff8000abbb80 ; changePowerStateToPriv(1) 0xffffff80022f5cb1: mov rdi, r12 0xffffff80022f5cb4: xor esi, esi 0xffffff80022f5cb6: call 0xffffff8000abbaf0 ; changePowerStateTo(0)
- Vérification
- Le désassemblage Capstone direct de
- Origine du Panic / Page Fault : Absence de PM sur le Contrôleur Parent :
- À
0xffffff80022f5c82,IO80211SkywalkInterfaceinvoqueQualcommWifi->joinPMtree(fSkywalkInterface)afin d'intégrer l'interface réseau dans l'arbre d'alimentation du pilote hôte. - Dans le noyau XNU (
0xffffff8000ab9a00),IOService::joinPMtreetransmet àgIOPMrootDomain->joinPMtree(QualcommWifi, fSkywalkInterface). - Cette méthode manipule obligatoirement le contexte de puissance du parent (
QualcommWifi->pm_varsà l'offset+0x60). - Puisque
QualcommWifin'avait jamais exécutéPMinit(), son pointeurpm_varsétait nul (0x0), provoquant un déréférencement immédiat de pointeur nul et un Kernel Trap Page Fault (CR2: 0x00000000000000d8/_IOLockLock).
- À
- Dangerosité du gestionnaire d'erreur de
QualcommWifi.cpp:- En cas de retour
falsedestart(), l'ancien code de repli appelaitdetachInterface(fSkywalkInterface, false)(qui est une méthode d'IOEthernetControllerattendant unIONetworkInterface*) et libéraitfSkywalkInterfacealors qu'il était déjà enregistré dans l'IORegistry et danscontroller->ivars + 0x188, garantissant un panic même sistart()échouait proprement.
- En cas de retour
- Analyse de la séquence interne de
- Résolutions Techniques Appliquées :
- Intégration du cycle de vie Power Management complet dans
QualcommWifi:- Ajout de
PMinit()etjoinPMtree(provider)dansQualcommWifi::start()dès l'ouverture du périphérique PCIe. - Enregistrement du modèle standard d'alimentation à 2 états (State 0: Off, State 1: On /
IOPMDeviceUsable | IOPMPowerOn) viaregisterPowerDriver(this, sPowerStates, 2)et transition initiale vers l'état actif (changePowerStateToPriv(1)). - Surcharge de
setPowerState(unsigned long powerStateOrdinal, IOService *whatDevice)dansQualcommWifi.hppetQualcommWifi.cpprenvoyantIOPMAckImplied. - Appel de
PMstop()dansQualcommWifi::stop().
- Ajout de
- Sécurisation du gestionnaire d'erreur de
start():- Suppression de tous les appels destructeurs
detachInterface()etOSSafeReleaseNULL()dans les branches d'erreur deskywalk_start,skywalk_attachetskywalk_init: l'interface est préservée intacte pour analyse des journaux système sans déstabiliser le noyau.
- Suppression de tous les appels destructeurs
- Validation et Déploiement :
- Recompilation avec
./build_wifi.sh: validation à 100% aveckmutil librariescontreBootKernelExtensions.kc. - Déploiement du nouveau kext sur
/Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext. - Configuration du boot-arg dans
/Volumes/EFI/EFI/OC/config.plist:qcom_step=6prêt pour test en direct.
- Recompilation avec
- Intégration du cycle de vie Power Management complet dans
Postmortem 16 : Diagnostic par Persistance NVRAM du Crash Early-Boot (Régression de l'Alias WorkLoop et Isolation Chirurgicale du Stade 6)
- Date : 18-19 Octobre 2026
- Cible : macOS Tahoe 26.x (Darwin 25.6.0 x86_64), AMD Ryzen 7 5800X3D,
QualcommWifi.kext(WCN78500x17cb:0x1107) - Symptômes & Constats :
- Après avoir testé le commit
e0414a9avecqcom_step=6puis en revenant surqcom_step=5, la machine est restée bloquée en bootloop immédiat dès les premières lignes du noyau XNU. - La pause visuelle de 10 secondes prévue dans
qcom_substep=1ne s'exécutait même plus. - L'utilisateur a dû désactiver temporairement
QualcommWifi.kext(Enabled: false) via Linux pour redémarrer sur le bureau macOS. - Interrogation en direct de la mémoire NVRAM macOS (
nvram -p | grep qcom) :qcom_stage createWorkQueue boot-args ... qcom_step=5 qcom_substep=1
- Après avoir testé le commit
- Causes Racines (Breakthroughs & Rétro-ingénierie du Noyau) :
- Origine du Panic Instantané : Régression de l'Alias
createWorkLoop():- Dans le commit
e0414a9, une tentative d'unification avait été faite pour quecreateWorkLoop()appellecreateWorkQueue()et quegetWorkLoop()retourne(IOWorkLoop*)fWorkQueue. - Dans la hiérarchie IOKit,
IONetworkController::start(provider)invoquecreateWorkLoop()puisgetWorkLoop(), s'attendant impérativement à une instance allouée viaIOWorkLoop::workLoop(). IO80211WorkQueuen'hérite pas deIOWorkLoop: leurs tables virtuelles (vtables) sont incompatibles.- Dès le déréférencement de
fWorkLoopdanssuper::start(provider)(ligne 706 deQualcommWifi.cpp), le noyau a subi un Kernel Panic immédiat avant même d'atteindre le Stade 1 ou le Stade 5, et avant leIOSleep(10000). - La variable NVRAM
qcom_stagefigée surcreateWorkQueuea fourni la preuve irréfutable du lieu exact du crash.
- Dans le commit
- Confirmation du Comportement Matériel de la NVRAM UEFI :
- La NVRAM UEFI réside physiquement dans la puce SPI Flash de la carte mère.
- Elle est totalement non-volatile : un reboot, une coupure de courant ou un démarrage sous Linux ne supprime aucunement les propriétés NVRAM.
- L'impossibilité d'écrire dans un fichier disque en early boot : au chargement des kexts de boot, les volumes APFS sont scellés cryptographiquement en lecture seule (snapshot système) et le VFS BSD n'est pas encore monté.
PEWriteNVRAMPropertyconstitue ainsi l'unique canal de diagnostic persistant bas-niveau disponible.
- Cartographie Précise du Chemin d'Exécution entre
getWorkQueue()etallocIO80211RecursiveLock()dansIO80211SkywalkInterface::start():- Le désassemblage Capstone complet a révélé la séquence exacte exécutée par
IO80211SkywalkInterface::start(IOService *provider)(0xffffff80022f5792) :0xffffff80022f5867:call [rax + 0xc68]-> 1er appel àQualcommWifi::getWorkQueue().0xffffff80022f5888:fWorkQueue->retain().0xffffff80022f59f4:call CCPipe::withOwnerNameCapacity(CoreCapture data/log pipe).0xffffff80022f5a62:call IO80211Controller::io80211isDebuggable(bool*).0xffffff80022f5a8f:call CCStream::withPipeAndName.0xffffff80022f5abc:call CCPipe::startPipe.0xffffff80022f5bca:call IO80211SkywalkInterface::createPeerManager()(0xffffff80022f7be6).- Dans
IO80211PeerManager::initWithInterface()(0xffffff8002246c88) :0xffffff8002246ed5:call [rax + 0xc68]-> 2ème appel àQualcommWifi::getWorkQueue().0xffffff8002246ee6:fWorkQueue->retain().0xffffff8002246f04:call [rax + 0xd88]->QualcommWifi::allocIO80211RecursiveLock().
- Le désassemblage Capstone complet a révélé la séquence exacte exécutée par
- Origine du Panic Instantané : Régression de l'Alias
- Résolutions Techniques Appliquées :
- Restauration de l'Allocation Dédiée
IOWorkLoop:- Réintégration de
fWorkLoop = IOWorkLoop::workLoop()danscreateWorkLoop()et du retour defWorkLoopdansgetWorkLoop().
- Réintégration de
- Suppression de
kprintf():- Élimination de
kprintfdelogNvramStage()pour prévenir tout blocage spinlock/MSR avant l'attachement de la console série.
- Élimination de
- Traçage Numéroté Granulaire dans la NVRAM :
getWorkQueue()incrémente un compteur statique et enregistrecb_getWorkQueue_1,cb_getWorkQueue_2dans la NVRAM.allocIO80211RecursiveLock()enregistrecb_allocLockdans la NVRAM.
- Validation :
- Compilation réussie sans avertissement (
./build_wifi.sh), validationkmutil librariesà 100% contreBootKernelExtensions.kc.
- Compilation réussie sans avertissement (
- Restauration de l'Allocation Dédiée
Postmortem 17 : Audit Binaire Exhaustif des Callbacks Skywalk / airportd et Armement Sécurisé du Stade 6
- Date : 19 Octobre 2026
- Cible : macOS Tahoe 26.x (Darwin 25.6.0 x86_64), AMD Ryzen 7 5800X3D,
QualcommWifi.kext(WCN78500x17cb:0x1107) - Contexte & Démarche (/grill-me) :
- Suite à l'éradication de la régression d'alias
IOWorkLoop(Postmortem 16), un audit désassemblé systématique des interactions binaires entreIO80211Family.kextet le contrôleurIO80211Controller/QualcommWifia été mené afin d'anticiper et de neutraliser tout déréférencement fatal ou point de blocage avant le démarrage matériel.
- Suite à l'éradication de la régression d'alias
- Breakthroughs & Rétro-ingénierie du Kernel Collection (
BootKernelExtensions.kc) :- Cartographie Complète des 38 Slots Virtuels Sollicités dans
IO80211Family:- Le balayage automatique Capstone sur les 2,5 Mo du segment
IO80211Familya recensé l'intégralité des 38 slots de méthodes virtuelles invoqués sur le contrôleur et l'interface :- Slot 394 (
0xc50) :isCommandProhibited(int)(1 appel) -> retournefalse. - Slot 397 (
0xc68) :getWorkQueue()(47 appels) -> retournefWorkQueue. - Slot 399 (
0xc78) :getIO80211CommandGate()(10 appels) -> instancié danssuper::start(ivars + 0x3c0). - Slot 400 (
0xc80) :getPrimarySkywalkInterface()(99 appels !) :- Révélation critique à l'adresse
0xffffff80021d9836:call [rax + 0xc80] ; controller->getPrimarySkywalkInterface() mov rax, qword ptr [rax + 0x120] ; Déréférencement direct de ivars SANS test de nullité ! - Si
getPrimarySkywalkInterface()retourneNULL, le noyau subit un Kernel Page Fault instantanéCR2: 0x0000000000000120. - L'implémentation par défaut de
IO80211Controllerinterrogeivars + 0x188. - Blindage : Surcharge explicite de
getPrimarySkywalkInterface()dansQualcommWifirenvoyant directementfSkywalkInterface.
- Révélation critique à l'adresse
- Slot 403 (
0xc98) :handleCardSpecific()(5 appels) -> redirigé vershandleApple80211Request. - Slot 404 (
0xca0) :hardwareOutputQueueDepth()(2 appels) -> retourne 0. - Slot 405 (
0xca8) :performCountryCodeOperation()(2 appels) -> retourne 0. - Slot 406 (
0xcb0) :dataLinkLayerAttachComplete()-> accusé de réception journalisé NVRAM. - Slot 424 (
0xd40) :getLogger()(54 appels) -> retournefLogger(conformeFakeLogStream). - Slot 432 (
0xd80) :getFaultReporterFromDriver()(1 appel) -> retournefFaultReporter(conformeFakeFaultReporter). - Slot 433 (
0xd88) :allocIO80211RecursiveLock()(1 appel) -> allocation concrète du verrou récursif pourIO80211PeerManager. - Slot 435 (
0xd98) :wasDynSARInFailSafeMode()(1 appel) -> stub sécurisé renvoyant 0. - Slot 438 (
0xdb0) :getActionFramePoolCapacity()(2 appels) -> retourne0x100. - Slot 462 (
0xe70) :setMulticastList(ether_addr const*, UInt32)(5 appels) :- Surcharge explicite ajoutée retournant
kIOReturnSuccesspour sécuriser les requêtes multicast de la pile réseau BSD.
- Surcharge explicite ajoutée retournant
- Slot 394 (
- Le balayage automatique Capstone sur les 2,5 Mo du segment
- Stratégie d'Amorçage Validée : Isolation Logicielle (
qcom_mmio=0) et Stade 6 (qcom_step=6) :- Démarrage en mode logiciel pur : pas d'accès direct MMIO/ASPM aux registres matériels du SoC WCN7850 afin de tester la chaîne Skywalk/airportd dans un environnement parfaitement déterministe.
- Configuration du boot-arg
qcom_substep=1: pause de 10 secondes précédant l'appelstart(this)pour observation visuelle sur console série/écran.
- Cartographie Complète des 38 Slots Virtuels Sollicités dans
- Modifications Apportées :
QualcommWifi/QualcommWifi.hpp: déclaration des surchargesgetPrimarySkywalkInterface()etsetMulticastList().QualcommWifi/QualcommWifi.cpp: implémentation avec journalisation NVRAM (cb_getPrimarySkywalk,cb_setMulticast).- Déploiement :
- Recompilation avec
./build_wifi.sh(validationkmutil libraries100%). - Déploiement vers
/Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext. - Mise à jour du
boot-argsOpenCore dans/Volumes/EFI/EFI/OC/config.plist:qcom_step=6 qcom_substep=1.
- Recompilation avec
Postmortem 18 : Découpage Chirurgical en 3 Pauses de l'Étape 6 et Blindage du Fil d'Ariane NVRAM sans Lock IORegistry
- Date : 19 Octobre 2026
- Cible : macOS Tahoe 26.x (Darwin 25.6.0 x86_64), AMD Ryzen 7 5800X3D,
QualcommWifi.kext(WCN78500x17cb:0x1107) - Symptômes & Retours du Test en Direct (Stade 6) :
- Boot-args armé :
qcom_step=6 qcom_substep=1. - La pause visuelle de 10 secondes précédant l'appel
fSkywalkInterface->start(this)s'est exécutée avec un succès absolu : le mode verbose de macOS est resté figé 10s de manière saine, attestant de la stabilité intégrale des étapes 1 à 5 (allocation, initialisation, rôle infrastructure, attachement IOKit et arborescenceCCPipe). - Dès la fin exacte des 10 secondes, lors de l'engagement de
fSkywalkInterface->start(this)ou immédiatement après, le système a crashé et redémarré instantanément sans afficher l'écran de Kernel Panic classique (malgrédebug=0x100). - Au redémarrage de secours sous
qcom_step=5,QualcommWifi::start()a complété son initialisation et réécrit"start_done"dansqcom_stage, masquant le point d'interruption du crash.
- Boot-args armé :
- Causes Racines & Breakthroughs Techniques :
- Écrasement Silencieux de la Télémétrie NVRAM :
- Lorsque l'utilisateur revient sur le bureau avec
qcom_step=5pour diagnostiquer le crash,QualcommWifi::init()etstart()s'exécutent avec succès et écrasentqcom_stage. - Résolution : Implémentation dans
QualcommWifi::init()d'une détection de crash antécédent. Siqcom_stagene vaut passtart_doneouclean, il est automatiquement copié de manière non volatile dansqcom_last_crash, et le fil d'Ariane est copié dansqcom_last_trail.
- Lorsque l'utilisateur revient sur le bureau avec
- Contention et Risque de Deadlock sous IOKit Power Management :
logNvramStage()appelait systématiquementIORegistryEntry::fromPath("/options", gIODTPlane)pour y inscrire la propriété IOKit.- Dans
IO80211SkywalkInterface::start(), macOS initialise la gestion d'alimentation (PMinit(),joinPMtree(this),registerPowerDriver), puis ordonne les transitionschangePowerStateToPriv(1)etchangePowerStateTo(0), déclenchant le callbackQualcommWifi::setPowerState(). - Tenter de traverser et verrouiller le plan
/optionsdu Device Tree (gIODTPlane) sous un verrou PM d'un thread noyau peut provoquer une contention mortelle ou un blocage critique. - Résolution : Suppression totale de l'accès à
IORegistryEntry::fromPathdanslogNvramStage(). Utilisation exclusive et directe dePEWriteNVRAMProperty(Platform Expert) qui écrit dans la NVRAM matérielle sans aucun verrou de plan IORegistry.
- Manque de Temporisation Post-
start(this)et Fil d'Ariane Continu :- Sous
qcom_substep=1, aucune pause n'était observée aprèsstart(this). Sistart(this)réussissait, le kext sortait immédiatement destart(), exposant l'interface aux démons macOS (airportd,Skywalk) sans permettre de vérifier sis6_2_start_okavait été atteint. - Résolution : Implémentation d'un fil d'Ariane continu (
qcom_trail) enchaînant chaque balise (s6_0>s6_1>cb_wq_1>cb_lock>...) et découpage de l'Étape 6 en 3 pauses balisées explicites :[PAUSE 1/3]: 3 secondes avantfSkywalkInterface->start(this).[PAUSE 2/3]: 5 secondes après retour réussi destart(this)avec bannière géante[SUCCES MAJEUR].[PAUSE 3/3]: 3 secondes avant la fin deQualcommWifi::start().
- Sous
- Écrasement Silencieux de la Télémétrie NVRAM :
- Modifications Apportées :
QualcommWifi/QualcommWifi.cpp:- Élimination des locks IORegistry dans
logNvramStage(). - Ajout de
gNvramTrailet écriture en temps réel deqcom_trail. - Sauvegarde automatique de
qcom_last_crashetqcom_last_traildansinit(). - Découpage en 3 pauses de l'Étape 6 sous
qcom_substep >= 1.
- Élimination des locks IORegistry dans
- Déploiement :
- Compilation sans erreur avec
./build_wifi.sh(validationkmutilà 100%). - Déploiement dans
/Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext. - Armement de
qcom_step=6 qcom_substep=1dans/Volumes/EFI/EFI/OC/config.plist.
- Compilation sans erreur avec
Postmortem 19 : Élucidation de la Saturation NVRAM (511 Octets), Latence SPI et Synchronisation Matérielle de l'Étape 6
- Date : 19 Octobre 2026
- Cible : macOS Tahoe 26.x (Darwin 25.6.0 x86_64), AMD Ryzen 7 5800X3D,
QualcommWifi.kext(WCN78500x17cb:0x1107) - Symptômes & Retours Utilisateur (/grill-me) :
- L'utilisateur a testé l'Étape 6 (
qcom_step=6 qcom_substep=1) et la machine a redémarré (crash / triple fault). - En régressant à
qcom_step=5pour relancer macOS, l'utilisateur a constaté qu'un temps d'arrêt / pause d'environ 2 à 3 secondes persistait au démarrage. - La console verbose n'affiche aucun nouveau message après
MB: B550 AORUS ELITE V2 Gigabyte Technology Co., Ltd.car le framebuffer vidéo se fige à ce stade alors que le noyau poursuit son initialisation en tâche de fond. - La variable
qcom_last_trailcapturée dans la NVRAM matérielle se terminait abruptement parregister_bypasans faire apparaître les balises de l'Étape 6 (s6_0_before_start,s6_1_calling_start).
- L'utilisateur a testé l'Étape 6 (
- Causes Racines & Breakthroughs Techniques :
- Saturation Mathématique du Buffer
gNvramTrailà 511 Octets :- Le buffer
static char gNvramTrail[512]avait une capacité maximale de 511 caractères utiles (+\0). - Le cumul des 37 balises d'initialisation a atteint très exactement 511 octets au tag
register_bypa(troncature deregister_bypassed). - Par conséquent,
strlcatrejetait silencieusement toutes les balises ultérieures.
- Le buffer
- Explication Physique de la Pause Résiduelle en Régressant de Step :
- La persistance perçue au boot sous
qcom_step=5provient de deux facteurs :qcom_substep=1était toujours mémorisé dans lesboot-argsde la NVRAM.PEWriteNVRAMPropertyest un appel synchrone bloquant vers les EFI Runtime Services pour écrire dans la puce SPI Flash de la carte mère Gigabyte B550. Avec 37 écritures successives (dont des callbacks répétés commecb_getWorkQueueetcb_getLogger), le CPU accumulait 50 à 100 ms par écriture, soit 2,5 à 3,5 secondes de freeze matériel, parfaitement confondu avec une pause logicielle.
- La persistance perçue au boot sous
- Perte Volatile des Écritures lors d'un Triple Fault Immédiat :
- Lors du crash dans
fSkywalkInterface->start(this), le hard reset intervenait dans la microseconde suivants6_1_calling_start, empêchant le contrôleur SPI d'achever son cycle d'écriture.
- Lors du crash dans
- Saturation Mathématique du Buffer
- Résolutions Techniques Appliquées :
- Quadruplement du Buffer Télémétrique (
gNvramTrail) :- Extension de
gNvramTrailetprevTrailà 2048 octets.
- Extension de
- Filtrage des Callbacks Répétitifs pour Supprimer la Latence SPI :
getWorkQueue()ne loggue dans la NVRAM que ses 3 premières invocations.getLogger()etgetFaultReporterFromDriver()ne logguent dans la NVRAM que leurs 2 premières invocations.- Élimination de plus de 80% des écritures SPI inutiles au boot.
- Délai de Synchronisation Matérielle (
IOSleep(200)) :- Insertion d'une temporisation de 200 ms immédiatement après
logNvramStage("s6_1_calling_start")pour garantir que l'écriture est physiquement gravée dans la puce flash avant l'engagement defSkywalkInterface->start(this).
- Insertion d'une temporisation de 200 ms immédiatement après
- Validation :
- Compilation réussie sans avertissement (
./build_wifi.sh), conformitékmutil librariesà 100% contreBootKernelExtensions.kc.
- Compilation réussie sans avertissement (
- Quadruplement du Buffer Télémétrique (
Postmortem 20 : Analyse du Freeze / Deadlock dans l'Étape 6 (allocIO80211RecursiveLock), Sécurisation Anti-Réentrance NVRAM et Télémétrie Chirurgicale
- Date : 19 Octobre 2026
- Cible : macOS Tahoe 26.x (Darwin 25.6.0 x86_64), AMD Ryzen 7 5800X3D,
QualcommWifi.kext(WCN78500x17cb:0x1107) - Symptômes & Retours Utilisateur (/grill-me) :
- Élimination complète du Kernel Panic / Triple Fault : la machine ne redémarre plus brusquement au seuil de l'Étape 6.
- Le système se fige (freeze / deadlock matériel) durant
fSkywalkInterface->start(this). - La télémétrie non volatile gravée dans la puce SPI Flash capture la balise exacte :
qcom_last_crash = cb_allocLockqcom_last_trail = ...>s6_0_before_start>s6_1_calling_start>cb_allocLock.
- Causes Racines & Breakthroughs Techniques :
- Analyse Désassemblée Approfondie de la Chaîne d'Allocation du Verrou :
- Dans
fSkywalkInterface->start(this)(0xffffff80022f5792), Skywalk appellecreatePeerManager()(0xffffff80022f7be6). - Dans
IO80211PeerManager::initWithInterface()(0xffffff8002246c88) :- À
0xffffff8002246ed5, Skywalk appellecontroller->getWorkQueue(). - À
0xffffff8002246f04, Skywalk appellecontroller->allocIO80211RecursiveLock(). - Notre implémentation appelait
super::allocIO80211RecursiveLock(), qui invoquecontroller->getWorkQueue()puis saute dansIO80211IORecursiveLock::allocWithParams(workQueue)(0xffffff8002194baa). - L'objet
IO80211IORecursiveLockalloue un wrapperOSObject(taille 24 octets, vtable__ZTV22IO80211IORecursiveLock) contenant unIORecursiveLock*interne et une référence versfWorkQueue. IO80211PeerManagerstocke ce verrou dansivars + 0x78sans l'acquérir immédiatement.
- À
- Dans
- Non-Réentrance Critique des Services d'Exécution UEFI (
PEWriteNVRAMProperty) :PEWriteNVRAMPropertyfait appel aux EFI Runtime Services (SetVariable) pour communiquer directement avec la puce SPI Flash de la carte mère Gigabyte B550.- Les services d'exécution UEFI ne sont pas réentrants. Si deux threads (par exemple le thread principal exécutant
start(this)et le thread asynchrone de gestion d'énergieIOPMrootDomainexécutantsetPowerState()) tentent d'écrire simultanément dans la NVRAM matérielle, le contrôleur SPI de la carte mère subit un interblocage (deadlock matériel / spinlock infini), figeant irrémédiablement le processeur.
- Angle Mort Télémétrique Post-
cb_allocLock:- Le filtrage des écritures NVRAM imposé au Postmortem 19 supprimait les logs de
getWorkQueue()au-delà de 3 appels. Or,start(this)invoquegetWorkQueue()à nouveau pourcreatePeerManager()etallocIO80211RecursiveLock(). - Aucune balise n'était inscrite entre
cb_allocLockets6_2_start_ok, englobant plus de 20 opérations critiques (CommandGate, 4 filesCommandQueue,TimerSource,PMinit,joinPMtree,registerPowerDriver,changePowerStateTo,IO80211Glue,IO80211MacAddressAgent).
- Le filtrage des écritures NVRAM imposé au Postmortem 19 supprimait les logs de
- Analyse Désassemblée Approfondie de la Chaîne d'Allocation du Verrou :
- Résolutions Techniques Appliquées :
- Sécurisation Anti-Réentrance NVRAM par Verrou Récursif IOKit (
sNvramLock) :- Allocation d'un
IORecursiveLock *sNvramLockdèsinit(). - Protection intégrale de
logNvramStage()parIORecursiveLockLock/Unlock, garantissant la sérialisation absolue de toutes les écritures SPI Flash et interdisant toute collision entre threads noyau.
- Allocation d'un
- Balisage Granulaire Chirurgical de
allocIO80211RecursiveLock:- Découpage en 3 balises horodatées matérielles :
cb_allocLock_enter: confirmation d'entrée dans le callback.cb_allocLock_super_ok: confirmation de retour sans crash desuper::allocIO80211RecursiveLock().cb_allocLock_exit: confirmation de sortie et retour du pointeur de verrou.
- Insertion de délais de gravure physique
IOSleep(50).
- Découpage en 3 balises horodatées matérielles :
- Traçage Spécifique de l'Étape 6 pour
getWorkQueue:- Activation d'un drapeau d'état
sInStep6encadrantfSkywalkInterface->start(this). - Journalisation fine des appels de file de travail sous l'Étape 6 (
cb_wq_s6_1,cb_wq_s6_2...), bornée à 5 écritures pour prévenir toute saturation.
- Activation d'un drapeau d'état
- Balisage des Callbacks Dérivés :
- Ajout de balises proactives sur
getActionFramePoolCapacity(cb_actFrameCap),setPOWER(cb_setPOWER),setMulticastListetsetPowerState.
- Ajout de balises proactives sur
- Validation :
- Compilation réussie sans avertissement (
./build_wifi.sh), conformitékmutil librariesà 100% contreBootKernelExtensions.kc.
- Compilation réussie sans avertissement (
- Sécurisation Anti-Réentrance NVRAM par Verrou Récursif IOKit (
Postmortem 21 : Élucidation du Freeze Post-allocLock, VMT Hooking de fWorkQueue->addEventSource (Slot 42) et Traçage de l Arborescence Power Management
- Date : 19 Octobre 2026
- Cible : macOS Tahoe 26.x (Darwin 25.6.0 x86_64), AMD Ryzen 7 5800X3D,
QualcommWifi.kext(WCN78500x17cb:0x1107) - Symptômes & Retours Utilisateur (/grill-me) :
- Boot-args armé :
qcom_step=6 qcom_substep=1. - Le système se fige (freeze complet) immédiatement au franchissement du seuil de l Étape 6.
- La télémétrie ultra-précise de la puce SPI Flash de la carte mère Gigabyte B550 a enregistré :
qcom_last_crash = cb_allocLock_exitqcom_last_trail = ...>s6_0_before_start>s6_1_calling_start>cb_wq_s6_1>cb_wq_s6_2>cb_allocLock_enter>cb_wq_s6_3>cb_allocLock_super_ok>cb_allocLock_exit - Preuve irréfutable :
QualcommWifi::allocIO80211RecursiveLock()etsuper::allocIO80211RecursiveLock()s exécutent jusqu au bout sans faillir (cb_allocLock_exit). Le freeze a donc lieu immédiatement après le retour de cette fonction.
- Boot-args armé :
- Breakthroughs & Rétro-ingénierie Binaire Exhaustive (
BootKernelExtensions.kc) :- Dissection Instruction par Instruction Post-
allocIO80211RecursiveLockdansIO80211PeerManager::initWithInterface(0xffffff8002246c88) :- Après le retour de
allocIO80211RecursiveLock()(0xffffff8002246f0a),PeerManagerexécute :0xffffff8002246f25:IO80211CommandGate::allocWithParams(peerManager, fWorkQueue)- Parsing de 5 boot args (
if80211_packet_pool_size,if80211_packet_queue_size_*) - Boucle d allocation de 4
IO80211CommandQueue::withGateAndCapacity - Boucle d allocation de 8 tampons
IOMallocZero(0x800) 0xffffff8002247552:IO80211Peer::withAddressAndManager(bssid, peerManager)- 3 appels critiques à
IO80211TimerSource::allocWithParams(priority, peerManager, fWorkQueue, action):- Aux adresses
0xffffff800224759d,0xffffff80022478aaet0xffffff80022478d9. - À chaque appel,
TimerSource::initWithParams(0xffffff80021a72ba) invoquefWorkQueue->addEventSource(timerSource)via un appel indirect :0xffffff80021a730b: call qword ptr [rax + 0x150](Slot 42 /0x150de la vtableIOWorkLoop/IO80211WorkQueue).
- Aux adresses
IO80211InfraPeersDatabase::withPeerManager(0xffffff80021a01a4)mbuf_allocpacket(0, ...)(0xffffff80008e07b0) avechow = MBUF_WAITOK (0)- Initialisation des 8 loggers et reporters (
initPeerManagerTextLogger,initPeerManagerDatapathPcapLogger, etc.).
- Après le retour de
- Dissection de la Chaîne Power Management dans
IO80211SkywalkInterface::start()(0xffffff80022f5792) :- Dès que
createPeerManager()retourne, Skywalk enchaîne :0xffffff80022f5be5:IO80211FlowQueueDatabase::initialize()0xffffff80022f5c07:OSDictionary::withCapacity(5)0xffffff80022f5c73:interface->PMinit()(slot 242 /0x790)0xffffff80022f5c82:controller->joinPMtree(interface)(slot 244 /0x7a0) !0xffffff80022f5c9e:interface->registerPowerDriver(interface, sPowerStates, 2)(slot 245 /0x7a8)0xffffff80022f5cac:interface->changePowerStateToPriv(1)0xffffff80022f5cb6:interface->changePowerStateTo(0)
- Dès que
- Identification des Deux Hypothèses du Freeze :
- Hypothèse A : Le blocage survient lors de l attachement d un
TimerSourcesurfWorkQueue(fWorkQueue->addEventSource(timerSource)au slot 42). - Hypothèse B : Le blocage survient dans
controller->joinPMtree(interface)ou dans la transition d énergiechangePowerStateTo(0)oùgIOPMrootDomaininvoque l arbre de dépendances sans notification réactive.
- Hypothèse A : Le blocage survient lors de l attachement d un
- Dissection Instruction par Instruction Post-
- Résolutions Techniques Appliquées :
- VMT Hooking Non-Invasif de
fWorkQueue->addEventSource(Slot 42 / Offset0x150) :fWorkQueueest un objet alloué sur le tas noyau (OSMetaClass::alloc). Sa table de méthodes virtuelles peut être remplacée au niveau de l instance sans modifier la mémoire constante du noyau (CR0.WP).- Dans
createWorkQueue(), duplication des 100 slots de la vtable résolue dans un tableau statique dédiésWorkQueueVtable[128]. - Remplacement chirurgical du slot 42 par
hooked_addEventSourcequi journalise dans la NVRAM :cb_addEvSrc_1->cb_addEvSrc_1_okcb_addEvSrc_2->cb_addEvSrc_2_okcb_addEvSrc_3->cb_addEvSrc_3_ok
- Restauration de la vtable d origine dans
stop()etfree().
- Surcharge Complète de
joinPMtreeet des Notifications d Alimentation IOKit :- Déclaration et implémentation dans
QualcommWifide :joinPMtree(IOService *driver): balisé parcb_joinPMtree_enteretcb_joinPMtree_exit.powerStateWillChangeToetpowerStateDidChangeTo: balisés parcb_pm_will_*etcb_pm_did_*.
- Si
cb_joinPMtree_enterapparaît, nous avons la certitude que 100% decreatePeerManager()a réussi.
- Déclaration et implémentation dans
- Validation & Déploiement :
- Compilation réussie sans avertissement (
./build_wifi.sh). - Validation
kmutil librariesà 100% contreBootKernelExtensions.kc. - Binaire déployé sur
/Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext(MD5:1dd315e169bae197a1b05f6af294a725).
- Compilation réussie sans avertissement (
- VMT Hooking Non-Invasif de