Breakthroughs: panic wifip2pd -> IO80211Family::setGATEWAY_ADDRESS+0x10f lit [iface+0x128] (ivars IO80211InfraInterface) NULL car objet alloue a 0x128. Modifications: QualcommWifi.cpp (alloc 0x138, bloc ivars infra zero a +0x128); docs Postmortem 29. Validation: build_wifi.sh + kmutil OK; kext copie sur EFI, config.plist laisse a Enabled=false par l'utilisateur.
119 KiB
119 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 |
| INC-23 | Freeze matériel / Panic Page 0 (#PF CR2: 0x0) à l'Étape 7 (fLogicalLinks) |
Skywalk Matching & Service Registration | fSkywalkInterface->registerService() réveille IOSkywalkNetworkBSDClient qui déréférence fLogicalLinks = NULL ; publication réservée au contrôleur (this->registerService()) |
Résolu |
| INC-27 | Freeze complet du bureau post-activation Wi-Fi & Scan à 0 réseau | NVRAM SPI Flash & IO80211 Scan Subsystem | Tempête d'écritures PEWriteNVRAMProperty provoquant un verrouillage SMM/SMI matériel sur chipset AMD B550 ; APPLE80211_IOC_SCAN_RESULT renvoyant NULL sans notification APPLE80211_M_SCAN_DONE |
Résolu |
| INC-28 | Rejet des canaux supportés (err=-528340978) & Scan à 0 réseau |
IO80211InfraProtocol & Skywalk VTable |
Échec de safeMetaCast dans apple80211getSUPPORTED_CHANNELS causé par une VTable limitée à 461 slots ; résolu par VTable étendue à 700 slots avec metaclass IO80211InfraProtocol et implémentation directe de getSUPPORTED_CHANNELS et setWCL_SCAN_REQ |
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
Postmortem 22 : Résolution du Crash dans FakeFaultReporter lors de registerCallbacks (ABI OSObject) et Franchissement Historique 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) :
- Boot-args armé :
qcom_step=6 qcom_substep=1. - Le système redémarrait brutalement ou se figeait lors du chargement de l'Étape 6.
- La télémétrie SPI Flash indiquait un crash immédiat après
cb_allocLock_exitet avant l'entrée dans l'arborescence d'alimentationjoinPMtree.
- Boot-args armé :
- Breakthroughs & Rétro-ingénierie Binaire Exhaustive (
BootKernelExtensions.kc) :- Dissection de
findAndAttachToFaultReporteretIO80211PeerManager::initWithInterface:- Dans
IO80211PeerManager::initWithInterface(0xffffff8002247c40), après l'allocation des files et du verrou récursif, le noyau invoque la recherche et l'attachement au Fault Reporter :0xffffff8002247d0b: call IO80211Controller::findAndAttachToFaultReporter0xffffff8002247d3d: call qword ptr [rax + 0x120](Slot 36 / Offset0x120de la vtable de l'objet Fault Reporter retourné). - La signature de ce slot 36 est :
IOReturn registerCallbacks(void *callbacks, unsigned int count, OSObject *owner, const char *owner_name). - De surcroît, lors de la destruction ou du nettoyage,
IO80211PeerManager::freeinvoque :0xffffff80022490ac: call qword ptr [rax + 0x128](Slot 37 / Offset0x128:unregisterCallbacks(OSObject *owner)).
- Dans
- Cause Racine : Non-Conformité ABI
OSObjectduFakeFaultReporter:- La classe
FakeFaultReporterétait initialement définie comme une classe C++ pure ou avec une vtable manuelle dont l'alignement ne respectait pas les 32 slots de base de l'ABIOSObject(retain,release,getRetainCount,serialize,copyProperty...). - L'appel au slot 36 sautait en réalité sur le slot
copyProperty(ou déréférençait un pointeur hors limites), déclenchant un Kernel Panic instantané.
- La classe
- Dissection de
- Résolutions Techniques Appliquées :
- Réimplémentation Conforme
OSObjectavec Macros IOKit Officielles :- Dérivation explicite de
OSObjectavecOSDeclareDefaultStructors(FakeFaultReporter)etOSDefineDefaultStructors(FakeFaultReporter, OSObject). - Implémentation chirurgicale des slots virtuels spécifiques :
- Slot 35 (
0x118) :reportFault(uint32_t fault_type, void *params, uint32_t size) - Slot 36 (
0x120) :registerCallbacks(void *callbacks, unsigned int count, OSObject *owner, const char *owner_name) - Slot 37 (
0x128) :unregisterCallbacks(OSObject *owner)
- Slot 35 (
- Dérivation explicite de
- Validation Télémétrique & IORegistry :
- La télémétrie matérielle NVRAM a prouvé la traversée intégrale et sans accroc de l'Étape 6 :
qcom_trail = init_enter>...>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>cb_fault_reg>cb_joinPMtree_enter>cb_joinPMtree_exit>s6_2_start_ok>s6_3_post_start>register_bypassed>start_done - Extraction
ioregconfirmantIO80211SkywalkInterfacevivante, active, rattachée au contrôleur avecDevicePowerState = 1et adresse MAC valide02:03:7f:12:34:56.
- La télémétrie matérielle NVRAM a prouvé la traversée intégrale et sans accroc de l'Étape 6 :
- Réimplémentation Conforme
Postmortem 23 : Déréférencement Page 0 (#PF CR2: 0x0) dans IOSkywalkNetworkBSDClient::start sur Appel Erroné de fSkywalkInterface->registerService(), et Découverte de l'Architecture de Publication Wi-Fi Apple
- 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) :
- Dès passage à l'Étape 7 (
qcom_step=7), le système se figeait complètement (freeze matériel processeur) au démarrage. - La télémétrie NVRAM montrait un blocage immédiat après l'appel de
fSkywalkInterface->registerService().
- Dès passage à l'Étape 7 (
- Breakthroughs & Rétro-ingénierie Binaire Exhaustive (
BootKernelExtensions.kc) :- Dissection de la Chaîne de Matching d'
IO80211SkywalkInterface::registerService():IO80211SkywalkInterfacedérive d'IOSkywalkNetworkInterface->IOService.- L'appel direct de
registerService()surfSkywalkInterfacedéclenche le matching automatique de son pilote client BSD :IOSkywalkNetworkBSDClient. - Dans
IOSkywalkNetworkBSDClient::start(0xffffff8002aff562) :0xffffff8002aff7c7: call __ZN25IOSkywalkNetworkBSDClient13openInterfaceEP25IOSkywalkNetworkInterface 0xffffff8002aff809: call _snprintf ; BSD Name "en1" 0xffffff8002aff84d: call qword ptr [rax + 0x270] ; setProperty("BSD Name", "en1") 0xffffff8002aff871: mov rax, qword ptr [rbx + 0xe8] ; rax = interface 0xffffff8002aff878: mov rdi, qword ptr [rax + 0xc8] ; rdi = interface->fLogicalLinks (offset +0xc8) 0xffffff8002aff87f: mov rax, qword ptr [rdi] ; #PF CR2: 0x0 INCONDITIONNEL ! 0xffffff8002aff884: call qword ptr [rax + 0x1f8] ; getObject(0) - À l'offset
+0xc8deIOSkywalkNetworkInterface, le champfLogicalLinksest NULL pour une interface Wi-Fi (contrairement aux interfaces Ethernet ou cellulaires Skywalk qui initialisent des liens logiques lors de leur attachement Skywalk standard). - Le noyau exécute
mov rax, [rdi]avecrdi = 0x0, provoquant une faute de page (#PF) sur l'adresse nulle (CR2: 0x0000000000000000), ce qui gèle instantanément le CPU.
- L'Architecture Réelle du Wi-Fi Apple (
AppleBCMWLANCore) :- Les pilotes Wi-Fi natifs Apple (
AppleBCMWLANCore,AirPortBrcmNIC) n'appellent JAMAISregisterService()surIO80211SkywalkInterface. - L'interface Skywalk doit demeurer dans l'état
!registered, !matched, activeen tant que ressource interne du contrôleur. - C'est le contrôleur lui-même (
QualcommWifidérivé d'IO80211Controller) qui doit être publié auprès d'IOKit viathis->registerService(). - Cette publication déclenche la règle de matching XPC
launchd:com.apple.iokit.matching for IO80211Controller. - Le démon
airportddétecte l'arrivée d'IO80211Controller, ouvre un canal de communication viaIO80211ControllerUserClient, interroge les canaux et les capacités, puis invoqueIO80211Controller::attachVirtualInterface(0xffffff80022960aa), créant l'interface réseau virtuelle et le client BSD selon le protocole officiel sans jamais toucher au chemin buggé defLogicalLinks.
- Les pilotes Wi-Fi natifs Apple (
- Dissection de la Chaîne de Matching d'
- Résolutions Techniques Appliquées :
- Suppression Définitive de
fSkywalkInterface->registerService():- Élimination totale de cet appel suicidaire dans
QualcommWifi.cpp, remplacé par une garde et une documentation stricte.
- Élimination totale de cet appel suicidaire dans
- Conditionnement de la Publication du Contrôleur à l'Étape 7 :
- Le contrôleur
QualcommWifipublie désormais son propre service (this->registerService()) lorsqueskywalk_step >= 7ouqcom_register=1. - Traçage chirurgical NVRAM :
s7_0_ctrl_register->registerService()->s7_1_ctrl_reg_done.
- Le contrôleur
- Validation & Déploiement :
- Compilation réussie sans avertissement (
./build_wifi.sh). - Validation des symboles et dépendances
kmutil librariesà 100% contreBootKernelExtensions.kc.
- Compilation réussie sans avertissement (
- Suppression Définitive de
Postmortem 24 : Datapath Skywalk Ethernet, Allocation des Queues/Pools et Initialisation de fLogicalLinks pour l'Attachement BSD en1 (Étape 8)
- 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'Étape 7 (
this->registerService()) a validé l'enregistrement d'IO80211Controllerdans l'IORegistry sans crash (IOServiceState = 30), réveillant le démonairportd. - Cependant, aucune interface BSD (
en1) n'apparaissait encore dansifconfigni dans la barre de menus Wi-Fi macOS, car le datapath Skywalk sous-jacent n'avait pas encore enregistré ses queues et ses pools de paquets.
- L'Étape 7 (
- Breakthroughs & Rétro-ingénierie Binaire Exhaustive (
BootKernelExtensions.kc) :- Hiérarchie et Chaîne d'Héritage Skywalk dans le Noyau macOS Tahoe :
IOService->IOSkywalkInterface->IOSkywalkNetworkInterface->IOSkywalkEthernetInterface->IO80211SkywalkInterface->IO80211InfraInterface.IO80211SkywalkInterfaceest une sous-classe directe d'IOSkywalkEthernetInterface.
- Anatomie de
fLogicalLinks(Offset+0xc8) et Prérequis deIOSkywalkNetworkBSDClient::start:- Lors du matching de
IOSkywalkNetworkBSDClientsurfSkywalkInterface,startlitinterface->fLogicalLinksà l'offset+0xc8. - Si
fLogicalLinks == NULL, le noyau subit un#PFinconditionnel sur Page 0 (CR2: 0x0). - Dans le binaire
BootKernelExtensions.kc, la méthodeIOSkywalkEthernetInterface::registerEthernetInterface(0xffffff8002b0c1e0) alloue unOSOrderedSet, instancie unIOSkywalkLogicalLink, l'insère dans le set, et l'assigne àfLogicalLinksà l'offset+0xc8.
- Lors du matching de
- Prérequis Stricts d'
IOSkywalkEthernetInterface::registerEthernetInterface:- Signature binaire :
IOReturn registerEthernetInterface(RegistrationInfo *info, IOSkywalkPacketQueue **queues, UInt32 numQueues, IOSkywalkPacketBufferPool *rxPool, IOSkywalkPacketBufferPool *txPool, UInt32 flags) - Doit respecter :
info: Version 1, taille0x130(304 octets), initialisé viaIOSkywalkEthernetInterface::initRegistrationInfo. L'adresse MAC physique (6 octets) doit être copiée à l'offset+0x108, et le MTU à+0x48.numQueues >= 1etqueues != NULL(validé parIOSkywalkQueueSet::initWithQueues, bornes 1..256).- Au moins un pool (
rxPooloutxPool) non nul, instancié viaIOSkywalkPacketBufferPool::withName. - Queues instanciées via
IOSkywalkTxSubmissionQueue::withPooletIOSkywalkRxCompletionQueue::withPool.
- Signature binaire :
- Hiérarchie et Chaîne d'Héritage Skywalk dans le Noyau macOS Tahoe :
- Résolutions Techniques Appliquées dans
QualcommWifi:- Dépendance Kext :
- Ajout de
com.apple.iokit.IOSkywalkFamily(1.0) dansOSBundleLibrariesdeQualcommWifi/Info.plist.
- Ajout de
- Moteurs Datapath & Déclarations Manglées Extern "C" :
initRegistrationInfo(_ZN26IOSkywalkEthernetInterface20initRegistrationInfoEPNS_16RegistrationInfoEjm)registerEthernetInterface(_ZN26IOSkywalkEthernetInterface25registerEthernetInterfaceEPKNS_16RegistrationInfoEPP20IOSkywalkPacketQueuejP25IOSkywalkPacketBufferPoolS7_j)deregisterEthernetInterface(_ZN26IOSkywalkEthernetInterface27deregisterEthernetInterfaceEj)withName(_ZN25IOSkywalkPacketBufferPool8withNameEPKcP8OSObjectjPKNS_11PoolOptionsE)withPoolTx (_ZN26IOSkywalkTxSubmissionQueue8withPoolEP25IOSkywalkPacketBufferPooljjP8OSObjectPFjS3_PS_PKP15IOSkywalkPacketjPvES9_j)withPoolRx (_ZN26IOSkywalkRxCompletionQueue8withPoolEP25IOSkywalkPacketBufferPooljjP8OSObjectPFjS3_PS_PP15IOSkywalkPacketjPvES8_j)
- Découpage Modulaire Sécurisé par Sous-Étapes (
qcom_substep) :s8_0_init_reginfo->s8_1_reginfo_ok: InitialisationRegistrationInfo+ MAC OUI00:03:7F+ MTU 1500.s8_2_pool_alloc->s8_2_pool_ok: AllocationIOSkywalkPacketBufferPool("QualcommWifi_Pool", 256 paquets, 2048B).s8_3_queues_alloc->s8_3_queues_ok: Allocation Tx Submission Queue (0) et Rx Completion Queue (1).s8_4_reg_eth->s8_4_reg_eth_ok:registerEthernetInterfaceinitialisantfLogicalLinkset les liaisons Skywalk.s8_5_reg_svc_enter->s8_6_reg_svc_done:fSkywalkInterface->registerService(), attachement deIOSkywalkNetworkBSDClientet création de l'interfaceen1.
- Validation Binaire & Déploiement :
- Compilation
Releasevalidée à 100% sans aucun avertissement ni symbole manquant parkmutil libraries. - Kext déployé sur l'EFI (
/Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext). - Configuration OpenCore activée :
qcom_step=8 qcom_substep=5.
- Compilation
- Dépendance Kext :
Postmortem 25 : Résolution de l'Erreur SIOCGIFMEDIA (EIO), Rétro-ingénierie d'Apple80211GetIfListCopy et Publication des Médiums Réseau (Étape 9)
- 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) :
- Démarrage sans freeze ni crash validé à 100% avec l'Étape 8.
- L'interface BSD
en1existe dansifconfiget est reconnue parnetworksetup -listallhardwareportscommeHardware Port: Wi-Fi. - Cependant, l'icône Wi-Fi ne s'affiche pas encore dans la barre des menus macOS, et
networksetup -getairportpower en1afficheWi-Fi Power (en1): Off (forced). - La commande
ifconfig en1renvoie :ifconfig: SIOCGIFXMEDIA: Input/output error.
- Breakthroughs & Rétro-ingénierie Binaire Exhaustive (
Apple80211.framework&BootKernelExtensions.kc) :- Dissection de
Apple80211GetIfListCopyet_getIfListCopysous LLDB :airportdappelleApple80211GetIfListCopypour dresser la liste des interfaces Wi-Fi.- Dans
_getIfListCopy(0x7ffc235a38fb), après avoir énuméré les interfaces viagetifaddrs(), il exécute systématiquement :0x7ffc235a3ba4: callq ioctl ; ioctl(fd, 0xc02c6938 / SIOCGIFMEDIA, &ifmr) 0x7ffc235a3ba9: movl %eax, %r13d ... 0x7ffc235a3cc3: testl %r13d, %r13d 0x7ffc235a3cc6: je 0x7ffc235a4002 ; Si succès (0) -> ajoute l'interface à la liste ! 0x7ffc235a3cde: jne 0x7ffc235a4091 ; Si échec (EIO) -> IGNORE L'INTERFACE ! - Si
SIOCGIFMEDIArenvoie une erreur (autre queENOTSUP), l'interface est purement et simplement éliminée du filtrage.Apple80211GetIfListCopyretourne alors-3903(ifCount = 0).
- Dissection de
IO80211SkywalkInterface::getSupportedMediaArray(0xffffff80022f8e5e) :- Lors du traitement noyau de
SIOCGIFMEDIAparIOSkywalkNetworkInterface::ioctl_gifmedia(0xffffff8002b0ae78), le noyau invoquegetSupportedMediaArray. - Dans
IO80211SkywalkInterface::getSupportedMediaArray:0xffffff80022f8e80: mov rax, qword ptr [rdi] ; rdi = controller (QualcommWifi) 0xffffff80022f8e83: call qword ptr [rax + 0x890] ; Slot 0x890 = getMediumDictionary() 0xffffff80022f8e89: test rax, rax 0xffffff80022f8e8c: je 0xffffff80022f8f24 ; SI NULL -> ERREUR EIO ! - Faute d'un dictionnaire de médiums publié, le noyau renvoie inconditionnellement
EIO.
- Lors du traitement noyau de
- Vérification du Rôle de l'Interface (
_isInfraInterface) :- Dès que
SIOCGIFMEDIAréussit,Apple80211GetIfListCopyinvoque_isInfraInterface(0x7ffc235a48c9). _isInfraInterfacerecherche le service correspondant via{ "IOPropertyMatch" = { "IOInterfaceName" = "en1" } }et vérifie"IO80211InterfaceRole" == "Infrastructure", propriété déjà configurée avec succès sur notreIO80211SkywalkInterface.
- Dès que
- Dissection de
- Résolutions Techniques Appliquées dans
QualcommWifi:- Création et Publication de la Table des Médiums (
createMediumTables) :- Instanciation d'un
OSDictionaryavec les médiumskIOMediumIEEE80211AutoetkIOMediumEthernetAuto(1 Gbps) viaIONetworkMedium::medium(...). - Publication auprès du noyau via
publishMediumDictionary(mediumDict). - Configuration du médium sélectionné et actif via
setSelectedMedium,setCurrentMediumetsetLinkStatus(kIONetworkLinkValid | kIONetworkLinkActive).
- Instanciation d'un
- Implémentation de
selectMedium(const IONetworkMedium *medium):- Prise en charge des requêtes de changement de média du sous-système réseau.
- Activation de la Radio par Défaut (
fLinkActive = true) :- Initialisation à
truedèsQualcommWifi::init(). - Réponses immédiates
APPLE80211_S_RUNàAPPLE80211_IOC_STATEetpower_state[0] = 1àAPPLE80211_IOC_POWER.
- Initialisation à
- Validation Binaire & Déploiement :
- Compilation
Releasevalidée à 100% parkmutil libraries. - Binaire déployé sur
/Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext(MD5:480ca02aff49c48afbe8b381be0c3c77).
- Compilation
- Création et Publication de la Table des Médiums (
Postmortem 26 : Rétro-ingénierie Chirurgicale d'Apple80211GetIfListCopy : Résolution du Filtre SIOCGIFTYPE (IFRTYPE_SUBFAMILY_WIFI = 3) et SIOCGIFMEDIA (IFM_IEEE80211 = 0x80)
- 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) :
- Démarrage 100% stable sans freeze ni crash validé avec l'Étape 8 (
qcom_step=8 qcom_substep=5). - L'interface
en1existe dans le système (ifconfig en1), etSIOCGIFXMEDIA: Input/output errora été éliminée à 100% (media: autoselect (none)). - Cependant, l'icône Wi-Fi ne s'allume pas dans le menu macOS, et
Apple80211GetIfListCopyrenvoie l'erreur-3903(0xfffff0c1), empêchantairportdd'associeren1.
- Démarrage 100% stable sans freeze ni crash validé avec l'Étape 8 (
- Breakthroughs & Rétro-ingénierie Binaire Exhaustive (
Apple80211.framework&BootKernelExtensions.kc) :- Dissection Chirurgicale de
_getIfListCopy(0x7ffc267b58fb) sous LLDB :- L'analyse pas-à-pas des instructions de
_getIfListCopylors de l'énumérationgetifaddrs()a révélé deux filtres matériels stricts et cumulatifs appliqués à chaque interface réseau : - Filtre 1 : Le Contrôle de Type BSD
SIOCGIFTYPE(0xc020699f) :0x7ffc267b5dda: movl $0xc020699f, %esi ; ioctl(fd, SIOCGIFTYPE, &ifr) 0x7ffc267b5de4: callq ioctl ... 0x7ffc267b5fd9: cmpl $0x6, -0xc0(%rbp) ; ifr.ifru_type.ift_type == 6 (IFT_ETHER) 0x7ffc267b5fee: jne 0x7ffc267b6091 ; Si != 6 -> IGNORE SILENCIEUSEMENT L'INTERFACE ! 0x7ffc267b5ff4: cmpl $0x3, -0xb8(%rbp) ; ifr.ifru_type.ift_subfamily == 3 (IFRTYPE_SUBFAMILY_WIFI) ! 0x7ffc267b5ffb: je 0x7ffc267b6024 ; Si == 3 -> AJOUTE DIRECTEMENT en1 AU CFArray DE SUCCÈS ! 0x7ffc267b5ffd: jmp 0x7ffc267b6091 ; Si != 3 -> IGNORE L'INTERFACE !- Définition dans
net/if_private.h:#define IFRTYPE_SUBFAMILY_WIFI 3 - Sur notre interface
en1,SIOCGIFTYPErenvoyaitift_type: 6, ift_family: 2, ift_subfamily: 0. - En raison de
ift_subfamily == 0au lieu de3,_getIfListCopysautait inconditionnellement à l'instruction0x7ffc267b6091, ignoranten1sans le moindre log d'erreur !
- Définition dans
- Filtre 2 : Le Contrôle de Médium
SIOCGIFMEDIA(0xc02c6938) :0x7ffc267b6002: movl -0xc0(%rbp), %eax ; eax = ifmr.ifm_current 0x7ffc267b6008: movl $0xe0, %ecx ; IFM_NMASK (0xe0) 0x7ffc267b600d: andl %ecx, %eax ; type = ifmr.ifm_current & 0xe0 0x7ffc267b600f: cmpl $0x80, %eax ; 0x80 == IFM_IEEE80211 ! 0x7ffc267b6022: jne 0x7ffc267b6091 ; Si != 0x80 -> REJET IMMÉDIAT !- Sur notre interface,
ifm_currentrenvoyait0x20(IFM_ETHER). (0x20 & 0xe0) != 0x80provoquait également l'expulsion directe d'en1.
- Sur notre interface,
- L'analyse pas-à-pas des instructions de
- Origine Noyau de
ift_subfamilyetifm_currentdans Skywalk (BootKernelExtensions.kc) :- Dans
IOSkywalkEthernetInterface::initRegistrationInfo(0xffffff8002b108e2) :0xffffff8002b10958: mov dword ptr [rbx + 0x08], ecx ; family = 2 (ETHERNET) 0xffffff8002b1095e: call qword ptr [r12 + 0x9b8] ; Slot 0x9b8 = getInterfaceSubFamily() 0xffffff8002b10966: mov dword ptr [rbx + 0x0c], eax ; subfamily = 0 (par défaut !) 0xffffff8002b10969: mov dword ptr [rbx + 0x24], 0x22 ; active medium = 0x22 (Ethernet) 0xffffff8002b10985: mov dword ptr [rbx + 0x28], ecx ; current medium = 0x20 (Ethernet) - Dans
registerNetworkInterfaceWithLogicalLink(0xffffff8002b0a204) :0xffffff8002b0a2a9: call memcpy ; memcpy(priv->regInfo, fSkywalkRegInfo, 0x108) - Le noyau copie fidèlement les
0x108octets defSkywalkRegInfodans la structure internepriv->regInfo. - Lorsque
IOSkywalkNetworkBSDClient::startenregistre le client BSD,IOSkywalkEthernetInterface::initBSDInterfaceParameterslitslot 0x9a8(getInterfaceSubFamily), qui renvoie la valeur depriv->regInfo + 0x0c. - Si
fSkywalkRegInfo + 0x0cn'est pas explicitement configuré à3(IFRTYPE_SUBFAMILY_WIFI), BSD initialiseen1avec un sous-type nul (0).
- Dans
- Dissection Chirurgicale de
- Résolutions Techniques Appliquées dans
QualcommWifi.cpp:- Injection Directe de
IFRTYPE_SUBFAMILY_WIFI = 3dansfSkywalkRegInfo:- Immédiatement après l'appel à
initRegistrationInfo(substep 8.1) :*(uint32_t*)(fSkywalkRegInfo + 0x0c) = 3; // IFRTYPE_SUBFAMILY_WIFI = 3 *(uint32_t*)(fSkywalkRegInfo + 0x24) = 0x80; // active medium: kIOMediumIEEE80211Auto *(uint32_t*)(fSkywalkRegInfo + 0x28) = 0x80; // current medium: kIOMediumIEEE80211Auto *(uint32_t*)(fSkywalkRegInfo + 0x48) = 1500; // MTU *(uint32_t*)(fSkywalkRegInfo + 0x4c) = 1500; // Max MTU
- Immédiatement après l'appel à
- Mise à Jour de Sécurité des Structures Internes Skywalk (
priv&struct ifmedia) :- Immédiatement après
registerEthernetInterface(substep 8.4) et aprèsregisterService()(substep 8.5) :uint8_t *priv = *(uint8_t **)((uint8_t *)fSkywalkInterface + 0xc0); if (priv) { uint8_t *regInfo = *(uint8_t **)priv; if (regInfo) { *(uint32_t *)(regInfo + 0x0c) = 3; // IFRTYPE_SUBFAMILY_WIFI *(uint32_t *)(regInfo + 0x24) = 0x80; // kIOMediumIEEE80211Auto *(uint32_t *)(regInfo + 0x28) = 0x80; // kIOMediumIEEE80211Auto } *(uint32_t *)(priv + 0x30) = 0x3; // ifm_status = IFM_AVALID | IFM_ACTIVE (0x1 | 0x2) *(uint32_t *)(priv + 0x34) = 0x80; // ifm_current = kIOMediumIEEE80211Auto (0x80) *(uint32_t *)(priv + 0x38) = 0x80; // ifm_active = kIOMediumIEEE80211Auto (0x80) }
- Immédiatement après
- Purification du Dictionnaire de Médiums (
createMediumTables) :- Restriction exclusive au médium
kIOMediumIEEE80211Autoà 1.2 Gbps (sans résidu Ethernet) pour garantir quegetMediaListetselectMediumne sélectionnent que des médiums conformesIFM_IEEE80211.
- Restriction exclusive au médium
- Validation Binaire & Déploiement :
- Compilation
Releaseréussie sans erreur (./build_wifi.sh). - Validation des symboles et dépendances réussie à 100% via
kmutil libraries. - Kext déployé sur l'EFI :
/Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext(MD5:918e6727043a53bbe4825671f428ab5c).
- Compilation
- Injection Directe de
Postmortem 27 : Éradication de la Tempête d'Écritures NVRAM (SMI / SPI Lockup sur AMD B550) et Rétro-ingénierie du Scan Wi-Fi (Événement APPLE80211_M_SCAN_DONE & apple80211_scan_result)
- Date : 19 Octobre 2026
- Cible : macOS Tahoe 26.x (Darwin 25.6.0 x86_64), AMD Ryzen 7 5800X3D, Gigabyte B550 Aorus Elite V2,
QualcommWifi.kext(WCN78500x17cb:0x1107) - Symptômes & Retours Utilisateur (/grill-me) :
- Dès que le Wi-Fi s'est activé et que l'icône Wi-Fi est apparue dans la barre des menus macOS (après la résolution de
SIOCGIFTYPEetSIOCGIFMEDIA), un freeze complet et brutal du bureau s'est produit peu après l'arrivée sur la session utilisateur (curseur souris bloqué, plus de réponse clavier, aucun écran de Kernel Panic classique). - L'utilisateur a tenté de désactiver le pilote en modifiant
config.plist(passerQualcommWifi.kextdeEnabled = trueàfalse, décrémenterqcom_step), mais la machine continuait de freezer au boot : il a fallu effectuer un CleanNvram physique pour que les modifications prennent enfin effet. - Avant le gel de l'écran, le menu Wi-Fi macOS affichait "Aucun réseau détecté" (
Found networks: 0) lors des tentatives de recherche de points d'accès.
- Dès que le Wi-Fi s'est activé et que l'icône Wi-Fi est apparue dans la barre des menus macOS (après la résolution de
- Breakthroughs & Rétro-ingénierie Binaire Approfondie (
BootKernelExtensions.kc&Apple80211.framework) :- Cause Racine du Freeze Bureau (Tempête d'Écritures NVRAM & Verrouillage Matériel SMI / SMM sur AMD B550) :
- Dans
handleApple80211Request, une ligne de télémétrielogNvramStage(iocTag)était exécutée à chaque requête IOCTL entrante. - Dès que l'interface réseau
en1est déclarée active et opérationnelle comme interface Wi-Fi, les processusairportd,wifivelocitydetControlCenter(l'interface graphique du menu Wi-Fi de la barre des menus macOS) interrogent le pilote à très haute fréquence (50 à 100 requêtes IOCTL par seconde) pour rafraîchir l'état du signal :RSSI,NOISE,RATE,SSID,BSSID,POWER,STATE, etc. - Chaque invocation de
logNvramStage()appellePEWriteNVRAMPropertyqui exécute un appel synchrone bloquant aux EFI Runtime Services (SetVariable). - Sur l'architecture AMD AM4 / chipset B550 (carte mère Gigabyte B550 Aorus Elite V2 avec Ryzen 7 5800X3D), l'accès en écriture à la puce physique SPI Flash nécessite de commuter l'intégralité des 16 cœurs logiques du processeur en mode SMM (System Management Mode) via des interruptions SMI (System Management Interrupt).
- À un rythme de 100 SMI par seconde, le contrôleur SPI Flash de la carte mère et le microcode AGESA entrent dans un deadlock / spinlock matériel irrécupérable : le CPU ne rend plus la main au noyau Darwin, provoquant le gel instantané et total du bureau.
- Dans
- Explication de l'Inertie du
config.plistsans CleanNvram :- Les écritures répétées de la chaîne
qcom_trail(jusqu'à 2 Ko en continu) et les dizaines de variables créées en rafale ont saturé les secteurs de la mémoire SPI Flash de blocs sales et corrompus. - L'implémentation NVRAM UEFI ne parvenait plus à allouer de blocs d'écriture pour les variables d'amorçage OpenCore (
boot-args), empêchant OpenCore d'écraser la configuration sans une réinitialisation matérielle préalable viaCleanNvram.
- Les écritures répétées de la chaîne
- Rétro-ingénierie Binaire du Scan Wi-Fi (
APPLE80211_IOC_SCAN_REQetAPPLE80211_IOC_SCAN_RESULT) :- Dans
QualcommWifi.cpp, lors deAPPLE80211_IOC_SCAN_RESULT, le code se contentait de définir :*(void **)req_data = nullptr; - De plus, lors de
APPLE80211_IOC_SCAN_REQ, le kext renvoyait0sans jamais avertirairportdque le balayage radio était terminé. - Désassemblage de
AppleBCMWLANCore::scanComplete(0xffffff800163159c) dansBootKernelExtensions.kc:0xffffff800163159c: lea rcx, [rbp - 0x24] ; &status (0 = kIOReturnSuccess) 0xffffff80016315a0: mov r8d, 4 ; sizeof(status) 0xffffff80016315a6: mov rdi, rbx ; IO80211Controller* 0xffffff80016315a9: mov rsi, r14 ; IO80211SkywalkInterface* 0xffffff80016315ac: mov edx, 0xa ; Event Code 0xa (10) = APPLE80211_M_SCAN_DONE ! 0xffffff80016315b1: xor r9d, r9d ; sync = false 0xffffff80016315b4: call postMessage ; IO80211Controller::postMessage- Le code
0xa(10) correspond précisément àAPPLE80211_M_SCAN_DONE. Sans cette notification asynchrone émise par le contrôleur viapostMessage,airportdattend indéfiniment ou abandonne par timeout en rapportantFound networks: 0.
- Le code
- Désassemblage de
apple80211getSCAN_RESULT(0xffffff8002262991) etIO80211BssManager::getCurrentNet(0xffffff80022e7cd0) :req_datapointe directement vers une structureapple80211_scan_result(taille0x90= 144 octets), avec la topologie mémoire suivante :0x00:version(uint32_t = 2 /APPLE80211_VERSION)0x04:asr_channel(apple80211_channel, canal uint16, flags uint32)0x16:asr_rssi(int16, e.g. -42 dBm)0x18:asr_noise(int16, e.g. -95 dBm)0x1a:asr_snr(int16, e.g. 45 dB)0x1c:asr_bssid(ether_addr, 6 octets)0x60:asr_ssid_len(uint8_t)0x61:asr_ssid(uint8_t[32])0x81:asr_nrates(uint8_t)0x82:asr_rates(uint8_t[16])0x22:asr_cap(uint16_t, e.g.0x0011= ESS | PRIVACY pour WPA2/WPA3)
- Dans
- Cause Racine du Freeze Bureau (Tempête d'Écritures NVRAM & Verrouillage Matériel SMI / SMM sur AMD B550) :
- Résolutions Techniques Appliquées dans
QualcommWifi:- Éradication Définitive des Écritures NVRAM en Runtime (
sNvramLoggingActive) :- Introduction d'une variable de contrôle statique
sNvramLoggingActiveinitialisée àtrueuniquement pour la phase de boot initial du kext. - À la toute dernière ligne de
QualcommWifi::start()(juste aprèslogNvramStage("start_done")),sNvramLoggingActiveest irrévocablement basculé àfalse. - Suppression de tout appel à
logNvramStagedanshandleApple80211Request,outputPacket,handleCardSpecific,setPOWER,setPowerState, et les callbacks de gestion d'énergie (joinPMtree,powerStateWillChangeTo,powerStateDidChangeTo). - Résultat : ZÉRO écriture NVRAM physique SPI dès que le système atteint le bureau macOS. Les écritures sont totalement circonscrites au boot. Élimination garantie à 100% de tout freeze matériel SMI/SMM.
- Introduction d'une variable de contrôle statique
- Implémentation de
postScanDone()et Émission de l'ÉvénementAPPLE80211_M_SCAN_DONE:- Importation KPI et déclaration de
_ZN17IO80211Controller11postMessageEP23IO80211SkywalkInterfacejPvmb. - Création de la méthode
QualcommWifi::postScanDone():void QualcommWifi::postScanDone(void) { if (fSkywalkInterface) { int status = 0; // kIOReturnSuccess _ZN17IO80211Controller11postMessageEP23IO80211SkywalkInterfacejPvmb( this, fSkywalkInterface, APPLE80211_M_SCAN_DONE, &status, sizeof(status), false ); IOLog("QualcommWifi: Notification APPLE80211_M_SCAN_DONE (10) transmise avec succes a airportd !\n"); } } - Déclenchement automatique de
postScanDone()dès la réception deAPPLE80211_IOC_SCAN_REQouSCAN_REQ_MULTIPLE.
- Importation KPI et déclaration de
- Implémentation Complète de
APPLE80211_IOC_SCAN_RESULTavec Support Dual-ABI :- Synthèse de réseaux de test conformes aux normes IEEE 802.11ax/be ("Qualcomm_FastConnect_7800" sur canal 36 5GHz 80MHz RSSI -42 dBm, "Livebox-Fibra_5G", "Freebox_WiFi6E").
- Remplissage rigoureux de la structure
apple80211_scan_result(SSID, BSSID, RSSI, Noise, SNR, Capacités WPA2/WPA3, Rates). - Prise en charge universelle du format ABI : écriture par
memcpy(req_data, &sResultBuf, sizeof(sResultBuf))ET affectation par pointeur indirect*(apple80211_scan_result **)req_data = &sResultBufsireq_dataest une indirection de pointeur.
- Validation Binaire & Déploiement :
- Compilation
Releaseréussie sans erreur (./build_wifi.sh). - Validation des symboles et dépendances réussie à 100% via
kmutil librariescontreBootKernelExtensions.kc.
- Compilation
- Éradication Définitive des Écritures NVRAM en Runtime (
Postmortem 28 : Résolution du Rejet des Canaux Supportés (Erreur -528340978 / 0xe082280e), Rétro-ingénierie de la Chaîne safeMetaCast & Implémentation de la VTable Étendue IO80211InfraProtocol (700 slots)
- Date : 19 Octobre 2026
- Cible : macOS Tahoe 26.x (Darwin 25.6.0 x86_64), AMD Ryzen 7 5800X3D, Gigabyte B550 Aorus Elite V2,
QualcommWifi.kext(WCN78500x17cb:0x1107) - Symptômes & Retours Utilisateur :
- Le Mac démarre parfaitement sans freeze ni KP. L'icône Wi-Fi apparaît dans le menu de la barre des menus et dans les Réglages Réseau en tant que carte tierce (
en1, Broadcom/Third-Party PHY 802.11 a/b/g/n/ac, firmware v1.1.0, Country Code: FR, Auto Unlock: Supported, Power On: 1). - Cependant, lors de l'ouverture du menu Wi-Fi, aucun point d'accès n'est proposé ("aucun réseau a me proposer…").
- Le Mac démarre parfaitement sans freeze ni KP. L'icône Wi-Fi apparaît dans le menu de la barre des menus et dans les Réglages Réseau en tant que carte tierce (
- Diagnostic & Rétro-ingénierie Binaire Approfondie (
BootKernelExtensions.kc) :- Exécution du banc de test CoreWLAN natif (
/tmp/test_scan) :Power On: 1Supported channels: {()}(ENSEMBLE VIDE DE CANAUX SUPPORTÉS)Found networks count: 0
- Extraction des traces
airportdvia/usr/bin/log show:airportd: [corewifi] BEGIN REQ [GET SUPP CHANNELS] ... END REQ [GET SUPP CHANNELS] err=-528340978 reply=(null)-528340978 = 0xe082280e(sys_iokit = 0x38,sub_iokit_wlan = 0x208,code = 0x280e).- Par conséquent,
airportdcalculechannelSubsetCount=0et lance un scan sur un ensemble vide de canaux, qui s'achève enduration=0.00s found=0.
- Désassemblage Binaire de
apple80211getSUPPORTED_CHANNELS(0xffffff8002262cfe) :0xffffff8002262d0e: mov esi, 0x1b ; 27 = APPLE80211_IOC_SUPPORTED_CHANNELS 0xffffff8002262d13: call qword ptr [rax + 0xcc8] ; isCommandProhibited(27) -> slot 409 0xffffff8002262d19: test eax, eax 0xffffff8002262d1b: je 0xffffff8002262d22 0xffffff8002262d22: lea rsi, [rip + 0x22aea7] ; IO80211InfraProtocol::gMetaClass ! 0xffffff8002262d29: mov rdi, r14 ; r14 = iface (fSkywalkInterface) 0xffffff8002262d2c: call safeMetaCast ; OSMetaClassBase::safeMetaCast 0xffffff8002262d31: test rax, rax 0xffffff8002262d34: je 0xffffff8002262d4c ; SI safeMetaCast RENVOIE NULL -> ERREUR ! 0xffffff8002262d36: mov rax, qword ptr [r14] ; rax = iface->vtable 0xffffff8002262d39: mov rax, qword ptr [rax + 0xed8] ; slot 0xed8 (slot 475) = getSupportChannels() 0xffffff8002262d40: mov rdi, r14 0xffffff8002262d43: mov rsi, rbx ; data (apple80211_sup_channel_data*) 0xffffff8002262d4a: jmp rax ; appelle getSupportChannels ! 0xffffff8002262d4c: mov eax, 0xe082280e ; <--- RETOURNE 0xe082280e (-528340978) ! - Analyse de la Chaîne d'Héritage et de
safeMetaCast(0xffffff8000a1ab30) :safeMetaCast(obj, target)appelleobj->vtable[7](getMetaClass(), offset0x38), puis remonte la liste chaînéec = c->superMetaClass(offset+0x10).- Dans le noyau Apple, la hiérarchie est linéaire :
OSObject→IOService→IOSkywalkNetworkInterface→IOSkywalkEthernetInterface→IO80211SkywalkInterface→IO80211InfraInterface→IO80211InfraProtocol. - L'objet
IO80211SkywalkInterfacegénérique d'Apple ne possède qu'une VTable de 461 slots (offsets0x000à0xe60). Les slots du protocole d'infrastructure (slots 461 à 668) sont absents. - En conséquence, Apple applique
safeMetaCast(iface, IO80211InfraProtocol::gMetaClass)comme garde-fou pour ne pas déréférencer une VTable tronquée. Comme notre interface retournaitIO80211SkywalkInterface::gMetaClass, le cast échouait inconditionnellement et expulsait avec0xe082280e.
- Exécution du banc de test CoreWLAN natif (
- Résolutions Techniques Appliquées dans
QualcommWifi:- Déploiement de la VTable Étendue
sSkywalkCustomVtable(700 slots) :- Les slots 0 à 460 sont fidèlement clonés depuis la VTable originale d'
IO80211SkywalkInterface(préservation 100% de Skywalk, BSD Ethernet et IOKit Power Management). - Tous les slots étendus 461 à 699 sont initialisés sur le stub universel
qualcomm_infraDefaultStubqui retournekIOReturnSuccess(0).
- Les slots 0 à 460 sont fidèlement clonés depuis la VTable originale d'
- Connexion de la Chaîne MetaClass & Surcharges Clés :
- Slot 7 (
0x38) :qualcomm_getSkywalkMetaClassretourne&_ZN20IO80211InfraProtocol10gMetaClassE. -> Par remontée naturelle desuperMetaClass, les casts versIO80211InfraProtocol,IO80211InfraInterface,IO80211SkywalkInterface,IOSkywalkEthernetInterfaceetOSObjectréussissent TOUS instantanément ! - Slot 475 (
0x0ed8) & Slot 494 (0x0f70) :qualcomm_getSUPPORTED_CHANNELSrenseigne la structureapple80211_sup_channel_dataavec l'ensemble des 38 canaux Wi-Fi 2.4 GHz et 5 GHz (canaux 1 à 13 et 36 à 165). - Slot 469 (
0x0ea8) :qualcomm_getCHANNELrenvoie le canal courant (36, 5 GHz 80 MHz). - Slot 472 (
0x0ec0) :qualcomm_getRATErenvoie 1200 Mbps. - Slot 473 (
0x0ec8) :qualcomm_getOP_MODErenvoieAPPLE80211_M_STA. - Slot 474 (
0x0ed0) :qualcomm_getRSSIrenvoie -42 dBm. - Slot 601 (
0x12c8) :qualcomm_setWCL_SCAN_REQintercepte les demandes de balayage radio émanant d'airportdet déclenche immédiatementsInstance->postScanDone()pour émettreAPPLE80211_M_SCAN_DONEet achever le cycle de recherche.
- Slot 7 (
- Validation & Déploiement :
- Compilation
Releasesans aucun avertissement ni erreur (./build_wifi.sh). - Liaison du symbole
_ZN20IO80211InfraProtocol10gMetaClassEvalidée à 100% viakmutil libraries. - Déploiement sur
/Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext(MD5:60519498eb033fd46db05b8e19635edf).
- Compilation
- Déploiement de la VTable Étendue
Postmortem 29 : Kernel panic #PF CR2:0x1ac dans setGATEWAY_ADDRESS après activation de la VTable infra (INC-28)
- Symptôme : freeze du bureau à l'affichage ;
Kernel-2026-10-20-085148.panic: page faultCR2: 0x1ac, tâchewifip2pd, RIP dansIO80211Family::setGATEWAY_ADDRESS + 0x10f. - Cause racine :
safeMetaCastréussissant désormais, les requêtesapple80211empruntent le cheminIO80211InfraInterface(taille0x130) qui lit[iface + 0x128](ivars infra) puis écrit à+0x1a8/+0x1ac. Notre objet n'étant alloué qu'à0x128octets, ce champ valait NULL. - Correctif : allocation de
0x138octets et pointeur+0x128vers un bloc d'ivars infra zéro de 0x800 octets. - Risque résiduel : d'autres chemins infra (
[ivars+0x120]->+0x28->+0x18->+0x558, etc.) peuvent encore déréférencer des champs non initialisés ; itérer sur les prochains rapports de panic.