Skip to content

Commit b291f8d

Browse files
Revert "fix: kext: sysfs_iokit: read kext list from gLoadedKextSummaries"
This reverts commit 0aa5c91.
1 parent 0aa5c91 commit b291f8d

1 file changed

Lines changed: 72 additions & 81 deletions

File tree

kext/sysfs_iokit.cpp

Lines changed: 72 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -37,45 +37,23 @@
3737
#include <libkern/c++/OSCollectionIterator.h>
3838

3939
/*
40-
* The release kernel no longer exports OSKext::copyLoadedKextInfo() - the
41-
* kpi.libkern symbol set was pruned (verified against kernel.release.t8142 for
42-
* macOS 26.5.2, build 25F84). The loaded-kext snapshot is instead read from
43-
* the kernel's own loaded-kext summary table, published as the exported global
44-
* gLoadedKextSummaries: a write-protected header followed by one entry per
45-
* loaded kext, rebuilt on every kext load/unload
46-
* (libkern/c++/OSKext.cpp: OSKext::updateLoadedKextSummaries()). It is the same
47-
* table the panic log and lldb consume.
40+
* OSKext::copyLoadedKextInfo() is declared here rather than by including
41+
* <libkern/c++/OSKext.h>, which is unusable from a kext build: it pulls in
42+
* <libkern/OSKextLibPrivate.h>, a private header the SDK does not ship. A
43+
* one-method declaration is enough - a static member call mangles from the
44+
* class name and parameter types alone, so this emits exactly the same symbol
45+
* reference the real header would, without vendoring private headers.
4846
*
49-
* The structures mirror OSKextLoadedKextSummaryHeader/OSKextLoadedKextSummary
50-
* from <libkern/OSKextLibPrivate.h>. entry_size is validated at read time so a
51-
* layout drift fails loudly instead of walking garbage. name[] is KMOD_MAX_NAME
52-
* (64) bytes as fixed in <mach/kmod.h>.
47+
* The return type is spelled OSDictionary* because OSPtr<T> is a plain T* in
48+
* kext builds, and the return type takes no part in C++ mangling regardless.
49+
*
50+
* Caller owns a reference to the returned dictionary.
5351
*/
54-
#define SYSFS_KEKXT_SUMMARY_VERSION 2
55-
56-
struct sysfs_oskext_summary {
57-
char name[64];
58-
uint8_t uuid[16];
59-
uint64_t address;
60-
uint64_t size;
61-
uint64_t version; /* packed OSKextVersion, decoded below */
62-
uint32_t loadTag;
63-
uint32_t flags;
64-
uint64_t reference_list;
65-
uint64_t text_exec_address;
66-
size_t text_exec_size;
67-
};
68-
69-
struct sysfs_oskext_summary_header {
70-
uint32_t version;
71-
uint32_t entry_size;
72-
uint32_t numSummaries;
73-
uint32_t reserved;
74-
struct sysfs_oskext_summary summaries[0];
52+
class OSKext {
53+
public:
54+
static OSDictionary *copyLoadedKextInfo(OSArray *kextIdentifiers,
55+
OSArray *keys);
7556
};
76-
77-
extern struct sysfs_oskext_summary_header *gLoadedKextSummaries;
78-
7957
#include <libkern/libkern.h>
8058

8159
#include <string.h>
@@ -383,34 +361,46 @@ sysfs_count_entries(void)
383361
/*
384362
* Capture the loaded kext list into the snapshot, for /sys/module.
385363
*
386-
* The list is read from gLoadedKextSummaries, the kernel's exported loaded-kext
387-
* summary table (mirror structs above). It is write-protected and rebuilt on
388-
* every kext load/unload, and it is built for exactly this kind of external
389-
* read (panic log, lldb). Entries with a zero address are empty slots and are
390-
* skipped. The table is not reference-counted, so refcnt is published as 0;
391-
* name, load_tag, load_size and version are populated from the summary.
392-
*
393-
* version is a packed OSKextVersion: major*1e8 + minor*1e6 + revision*1e4 +
394-
* stage*1e3 + stage_level, with stages dev=1, alpha=3, beta=5, candidate=7,
395-
* release=9 (libkern/OSKextVersion.c). It is rendered the way the kernel's own
396-
* OSKextVersionGetString() would: "major.minor[.revision][a|b|f|d<n>]".
364+
* copyLoadedKextInfo() is asked for only the four keys we publish rather than
365+
* the full per-kext dictionary, which keeps the work (and the time the kext
366+
* lock is held) proportional to what we actually use. A kext missing any of
367+
* them is still listed - the value simply reads as zero or empty - because a
368+
* present-but-incomplete module is more useful than a hidden one.
397369
*
398370
* Failure here is never fatal: the snapshot is published regardless and
399371
* /sys/module reads as empty, exactly as it does before the first refresh.
400372
*/
401373
static void
402374
sysfs_snap_build_modules(struct sysfs_snapshot *snap)
403375
{
404-
struct sysfs_oskext_summary_header *hdr = gLoadedKextSummaries;
376+
static const char * const wanted[] = {
377+
"OSBundleLoadTag",
378+
"OSBundleLoadSize",
379+
"OSBundleRetainCount",
380+
"CFBundleVersion",
381+
};
382+
383+
OSArray *keys = OSArray::withCapacity(4);
384+
if (keys == nullptr) {
385+
return;
386+
}
387+
for (unsigned i = 0; i < 4; i++) {
388+
const OSSymbol *sym = OSSymbol::withCString(wanted[i]);
389+
if (sym != nullptr) {
390+
keys->setObject(const_cast<OSSymbol *>(sym));
391+
sym->release();
392+
}
393+
}
405394

406-
if (hdr == nullptr ||
407-
hdr->version != SYSFS_KEKXT_SUMMARY_VERSION ||
408-
hdr->entry_size != sizeof(struct sysfs_oskext_summary)) {
395+
OSDictionary *info = OSKext::copyLoadedKextInfo(nullptr, keys);
396+
keys->release();
397+
if (info == nullptr) {
409398
return;
410399
}
411400

412-
uint32_t cap = hdr->numSummaries;
401+
uint32_t cap = info->getCount();
413402
if (cap == 0) {
403+
info->release();
414404
return;
415405
}
416406
if (cap > SYSFS_IOKIT_MAXMODULES) {
@@ -421,46 +411,47 @@ sysfs_snap_build_modules(struct sysfs_snapshot *snap)
421411
struct sysfs_snap_module *arr =
422412
(struct sysfs_snap_module *)sysfs_alloc(bytes);
423413
if (arr == nullptr) {
414+
info->release();
424415
return;
425416
}
426417

427-
uint32_t n = 0;
428-
for (uint32_t i = 0; i < cap; i++) {
429-
const struct sysfs_oskext_summary *s = &hdr->summaries[i];
418+
OSCollectionIterator *it = OSCollectionIterator::withCollection(info);
419+
if (it == nullptr) {
420+
sysfs_free(arr, bytes);
421+
info->release();
422+
return;
423+
}
430424

431-
if (s->address == 0 && s->text_exec_address == 0) {
425+
uint32_t n = 0;
426+
OSObject *k;
427+
while ((k = it->getNextObject()) != nullptr && n < cap) {
428+
OSString *ident = OSDynamicCast(OSString, k);
429+
if (ident == nullptr) {
430+
continue;
431+
}
432+
OSDictionary *kd = OSDynamicCast(OSDictionary, info->getObject(ident));
433+
if (kd == nullptr) {
432434
continue;
433435
}
434436

435437
struct sysfs_snap_module *m = &arr[n];
436-
strlcpy(m->name, s->name, sizeof(m->name));
437-
m->load_tag = s->loadTag;
438-
m->load_size = s->size;
439-
m->refcnt = 0; /* not present in the summary */
440-
441-
uint64_t v = s->version;
442-
if (v != 0) {
443-
unsigned major = (unsigned)(v / 100000000u);
444-
unsigned minor = (unsigned)((v / 1000000u) % 100u);
445-
unsigned rev = (unsigned)((v / 10000u) % 100u);
446-
unsigned stage = (unsigned)((v / 1000u) % 10u);
447-
if (rev != 0) {
448-
snprintf(m->version, sizeof(m->version),
449-
"%u.%u.%u", major, minor, rev);
450-
} else {
451-
snprintf(m->version, sizeof(m->version),
452-
"%u.%u", major, minor);
453-
}
454-
if (stage != 9) { /* kOSKextVersionStageRelease */
455-
size_t o = strlen(m->version);
456-
snprintf(m->version + o, sizeof(m->version) - o, "%c%u",
457-
stage == 1 ? 'd' : stage == 3 ? 'a' :
458-
stage == 5 ? 'b' : stage == 7 ? 'f' : '?',
459-
(unsigned)(v % 1000u));
460-
}
438+
strlcpy(m->name, ident->getCStringNoCopy(), sizeof(m->name));
439+
440+
OSNumber *num = OSDynamicCast(OSNumber, kd->getObject("OSBundleLoadTag"));
441+
m->load_tag = (num != nullptr) ? num->unsigned64BitValue() : 0;
442+
num = OSDynamicCast(OSNumber, kd->getObject("OSBundleLoadSize"));
443+
m->load_size = (num != nullptr) ? num->unsigned64BitValue() : 0;
444+
num = OSDynamicCast(OSNumber, kd->getObject("OSBundleRetainCount"));
445+
m->refcnt = (num != nullptr) ? num->unsigned64BitValue() : 0;
446+
447+
OSString *ver = OSDynamicCast(OSString, kd->getObject("CFBundleVersion"));
448+
if (ver != nullptr) {
449+
strlcpy(m->version, ver->getCStringNoCopy(), sizeof(m->version));
461450
}
462451
n++;
463452
}
453+
it->release();
454+
info->release();
464455

465456
snap->modules = arr;
466457
snap->module_cap = cap;

0 commit comments

Comments
 (0)