fix(wifi): conformer FakeFaultReporter à l'ABI IO80211FaultReporter (OSObject) et éliminer le #PF slot 0x120

### Breakthroughs & Rétro-ingénierie
- Disséction binaire de IO80211PeerManager::initWithInterface (0xffffff8002247d3d dans BootKernelExtensions.kc).
- Constat : IO80211PeerManager récupère le faultReporter depuis le contrôleur (+0x58), puis invoque vtable slot 36 (+0x120) : registerCallbacks(callbacks, 16, peerManager, "IO80211PeerManager").
- Cause racine du freeze : FakeFaultReporter dérivait de IOService (taille 0x90). Dans IOService, le slot 36 (+0x120) est copyProperty(const OSString*, ...), qui s'exécutait en tentant de déréférencer les variables locales de pile passées en argument au lieu d'un OSString*, déclenchant un #PF immédiat sur page zéro et un gel processeur matériel.
- Conformité : IO80211FaultReporter dérive directement de OSObject (35 slots virtuels de base, slots 35..37 dédiés à reportFault, registerCallbacks, unregisterCallbacks).

### Modifications apportées
- QualcommWifi/QualcommWifi.cpp :
  * Migration de FakeFaultReporter pour hériter de OSObject au lieu de IOService.
  * Déclaration des méthodes virtuelles reportFault (slot 35), registerCallbacks (slot 36) et unregisterCallbacks (slot 37).
  * Balisage télémétrique NVRAM cb_fault_reg et cb_fault_unreg.
  * Retrait des hooks expérimentaux addEventSource de fWorkQueue désormais obsolètes.

### Validation
- Compilation réussie sous ./build_wifi.sh (Clang C++20, SDK MacOSX26.0).
- kmutil libraries --bundle-path build/Release/QualcommWifi.kext validé à 100% contre BootKernelExtensions.kc.
This commit is contained in:
Erwan committed 2026-10-19 15:18:57 +02:00
1 parent 05b3c5eec1
commit a4e77c9a53
1 file changed
+36 -57
+36 -57
View File
@@ -50,42 +50,46 @@ static inline void logNvramStage(const char *tag) {
} }
} }
typedef IOReturn (*addEventSource_fn)(IOWorkLoop *self, IOEventSource *event); // Classe simulant un IO80211FaultReporter IOKit conforme (attendu par IO80211Controller et IO80211PeerManager)
static addEventSource_fn sOrigAddEventSource = nullptr; // - Hérite directement de OSObject (35 slots de base : 0..34)
static void **sOrigWorkQueueVtable = nullptr; // - Déclare les 3 méthodes virtuelles de IO80211FaultReporter :
static void *sWorkQueueVtable[128]; // slot 35 (offset 0x118) : reportFault(...)
static int sAddEventSourceCount = 0; // slot 36 (offset 0x120) : registerCallbacks(...) -> appelé par IO80211PeerManager::initWithInterface (0xffffff8002247d3d)
// slot 37 (offset 0x128) : unregisterCallbacks(...) -> appelé par IO80211PeerManager::free (0xffffff80022490ac)
static IOReturn hooked_addEventSource(IOWorkLoop *self, IOEventSource *event) { class FakeFaultReporter : public OSObject {
sAddEventSourceCount++;
char tag[64];
snprintf(tag, sizeof(tag), "cb_addEvSrc_%d", sAddEventSourceCount);
logNvramStage(tag);
IOSleep(50);
IOLog("QualcommWifi: >>> HOOK fWorkQueue->addEventSource(#%d, event=0x%p) [ENTER] <<<\n",
sAddEventSourceCount, event);
IOReturn res = sOrigAddEventSource(self, event);
char tagExit[64];
snprintf(tagExit, sizeof(tagExit), "cb_addEvSrc_%d_ok", sAddEventSourceCount);
logNvramStage(tagExit);
IOSleep(50);
IOLog("QualcommWifi: >>> HOOK fWorkQueue->addEventSource(#%d) -> 0x%x [EXIT] <<<\n",
sAddEventSourceCount, res);
return res;
}
// Classe simulant un FaultReporter IOKit valide (attendu par IO80211Controller::findAndAttachToFaultReporter)
// Conforme à l'ABI CCFaultReporter / IOService (267 slots de vtable, taille 0x90)
class FakeFaultReporter : public IOService {
OSDeclareDefaultStructors(FakeFaultReporter) OSDeclareDefaultStructors(FakeFaultReporter)
private: private:
uint8_t _pad[0x90 - sizeof(IOService)]; uint8_t _pad[0x80];
public: public:
virtual bool init() override { virtual bool init() override {
if (!IOService::init()) return false; if (!OSObject::init()) return false;
bzero(_pad, sizeof(_pad)); bzero(_pad, sizeof(_pad));
return true; return true;
} }
// slot 35 (offset 0x118): reportFault
virtual IOReturn reportFault(uint32_t a, const char *b, uint32_t c, const char *d, void *e, int f, const char *g, ...) {
return kIOReturnSuccess;
}
// slot 36 (offset 0x120): registerCallbacks
// Invoqué par IO80211PeerManager::initWithInterface (0xffffff8002247d3d)
virtual IOReturn registerCallbacks(void *callbacks, unsigned int count, OSObject *owner, const char *owner_name) {
logNvramStage("cb_fault_reg");
IOSleep(50);
IOLog("QualcommWifi: >>> FakeFaultReporter::registerCallbacks(count=%u, owner=%s) [SUCCESS] <<<\n",
count, owner_name ? owner_name : "null");
return kIOReturnSuccess;
}
// slot 37 (offset 0x128): unregisterCallbacks
// Invoqué par IO80211PeerManager::free (0xffffff80022490ac)
virtual IOReturn unregisterCallbacks(OSObject *owner) {
logNvramStage("cb_fault_unreg");
IOLog("QualcommWifi: >>> FakeFaultReporter::unregisterCallbacks() [SUCCESS] <<<\n");
return kIOReturnSuccess;
}
static FakeFaultReporter* create() { static FakeFaultReporter* create() {
FakeFaultReporter *inst = new FakeFaultReporter; FakeFaultReporter *inst = new FakeFaultReporter;
if (inst && !inst->init()) { if (inst && !inst->init()) {
@@ -95,8 +99,7 @@ public:
return inst; return inst;
} }
}; };
OSDefineMetaClassAndStructors(FakeFaultReporter, IOService) OSDefineMetaClassAndStructors(FakeFaultReporter, OSObject)
static_assert(sizeof(FakeFaultReporter) == 0x90, "FakeFaultReporter size must match CCFaultReporter ABI (0x90)");
// Buffers statiques globaux BSS : garantis alloués et mappés dès le chargement du kext, // Buffers statiques globaux BSS : garantis alloués et mappés dès le chargement du kext,
// immunisés contre tout échec de heap dynamique durant le boot précoce. // immunisés contre tout échec de heap dynamique durant le boot précoce.
@@ -254,7 +257,6 @@ bool QualcommWifi::init(OSDictionary *dictionary) {
sGetFaultReporterCount = 0; sGetFaultReporterCount = 0;
sInStep6 = false; sInStep6 = false;
sStep6WqCount = 0; sStep6WqCount = 0;
sAddEventSourceCount = 0;
logNvramStage("init_enter"); logNvramStage("init_enter");
if (!super::init(dictionary)) { if (!super::init(dictionary)) {
@@ -305,12 +307,7 @@ void QualcommWifi::free(void) {
OSSafeReleaseNULL(fFaultReporter); OSSafeReleaseNULL(fFaultReporter);
OSSafeReleaseNULL(fLogger); OSSafeReleaseNULL(fLogger);
OSSafeReleaseNULL(fWorkLoop); OSSafeReleaseNULL(fWorkLoop);
if (fWorkQueue) { OSSafeReleaseNULL(fWorkQueue);
if (sOrigWorkQueueVtable) {
*(void ***)fWorkQueue = sOrigWorkQueueVtable;
}
OSSafeReleaseNULL(fWorkQueue);
}
if (sNvramLock) { if (sNvramLock) {
IORecursiveLock *lock = sNvramLock; IORecursiveLock *lock = sNvramLock;
sNvramLock = nullptr; sNvramLock = nullptr;
@@ -1065,13 +1062,7 @@ void QualcommWifi::stop(IOService *provider) {
OSSafeReleaseNULL(fWorkLoop); OSSafeReleaseNULL(fWorkLoop);
} }
if (fWorkQueue) { OSSafeReleaseNULL(fWorkQueue);
if (sOrigWorkQueueVtable) {
*(void ***)fWorkQueue = sOrigWorkQueueVtable;
}
OSSafeReleaseNULL(fWorkQueue);
}
PMstop(); PMstop();
super::stop(provider); super::stop(provider);
} }
@@ -1088,18 +1079,6 @@ bool QualcommWifi::createWorkQueue(void) {
return false; return false;
} }
// Interception chirurgicale de addEventSource sur fWorkQueue (slot 42 = offset 0x150)
sOrigWorkQueueVtable = *(void ***)fWorkQueue;
if (sOrigWorkQueueVtable && !sOrigAddEventSource) {
memcpy(&sWorkQueueVtable[2], sOrigWorkQueueVtable, sizeof(void *) * 100);
sWorkQueueVtable[0] = sOrigWorkQueueVtable[-2];
sWorkQueueVtable[1] = sOrigWorkQueueVtable[-1];
sOrigAddEventSource = (addEventSource_fn)sOrigWorkQueueVtable[42];
sWorkQueueVtable[2 + 42] = (void *)&hooked_addEventSource;
*(void ***)fWorkQueue = &sWorkQueueVtable[2];
IOLog("QualcommWifi: Vtable de fWorkQueue interceptée avec succès (addEventSource=slot 42) !\n");
}
logNvramStage("workQueue_ok"); logNvramStage("workQueue_ok");
IOLog("QualcommWifi: IO80211WorkQueue allouee avec succes (0x%p) !\n", fWorkQueue); IOLog("QualcommWifi: IO80211WorkQueue allouee avec succes (0x%p) !\n", fWorkQueue);
return true; return true;