Files
zer0condition-GoodmansKernel/driver/module_table.c
T
zer0condition 00a3adf890 initial commit
2026-08-19 01:39:41 +05:30

151 lines
3.9 KiB
C

/* module_table.c - fixed-size slot allocator for loaded modules.
* refcount is atomic (LONG). teardown holds the module's call_mutex so
* any in-flight m3_Call has to finish before the runtime is freed. */
#include "inc/gvm.h"
#include "../shared/goodmans_ioctl.h"
static gvm_module g_modules[GVM_MAX_MODULES];
static KSPIN_LOCK g_lock;
NTSTATUS
gvm_modtab_init(void)
{
RtlZeroMemory(g_modules, sizeof(g_modules));
KeInitializeSpinLock(&g_lock);
// init every mutex up front so iterators that wait on all slots (e.g. the
// callback dispatch worker) don't touch an uninitialized DISPATCHER_HEADER
for (unsigned int i = 0; i < GVM_MAX_MODULES; i++)
KeInitializeMutex(&g_modules[i].call_mutex, 0);
return STATUS_SUCCESS;
}
void
gvm_modtab_teardown(void)
{
for (unsigned int i = 0; i < GVM_MAX_MODULES; i++) {
if (g_modules[i].used) {
g_modules[i].refcount = 1; // force teardown
gvm_modtab_free(&g_modules[i]);
}
}
}
gvm_module*
gvm_modtab_alloc(void)
{
KIRQL irql;
gvm_module* out = NULL;
KeAcquireSpinLock(&g_lock, &irql);
for (unsigned int i = 0; i < GVM_MAX_MODULES; i++) {
if (!g_modules[i].used) {
g_modules[i].used = TRUE;
g_modules[i].id = i + 1;
g_modules[i].refcount = 1;
KeInitializeMutex(&g_modules[i].call_mutex, 0);
out = &g_modules[i];
break;
}
}
KeReleaseSpinLock(&g_lock, irql);
return out;
}
gvm_module*
gvm_modtab_get(unsigned int id)
{
if (id == 0 || id > GVM_MAX_MODULES) return NULL;
gvm_module* m = &g_modules[id - 1];
return m->used ? m : NULL;
}
gvm_module*
gvm_modtab_find_by_hash(unsigned long long hash)
{
KIRQL irql;
gvm_module* out = NULL;
KeAcquireSpinLock(&g_lock, &irql);
for (unsigned int i = 0; i < GVM_MAX_MODULES; i++) {
if (g_modules[i].used && g_modules[i].hash == hash) {
out = &g_modules[i];
break;
}
}
KeReleaseSpinLock(&g_lock, irql);
return out;
}
// atomic find + increment under the table lock. safer than
// find_by_hash + separate refcount++ which races with concurrent unload.
gvm_module*
gvm_modtab_find_by_hash_incref(unsigned long long hash, unsigned int expected_size)
{
KIRQL irql;
gvm_module* out = NULL;
KeAcquireSpinLock(&g_lock, &irql);
for (unsigned int i = 0; i < GVM_MAX_MODULES; i++) {
if (g_modules[i].used
&& g_modules[i].hash == hash
&& g_modules[i].wasm_size == expected_size) {
InterlockedIncrement(&g_modules[i].refcount);
out = &g_modules[i];
break;
}
}
KeReleaseSpinLock(&g_lock, irql);
return out;
}
gvm_module*
gvm_modtab_iter(unsigned int idx)
{
if (idx >= GVM_MAX_MODULES) return NULL;
return &g_modules[idx];
}
gvm_module*
gvm_modtab_owner_of_runtime(IM3Runtime rt)
{
if (!rt) return NULL;
for (unsigned int i = 0; i < GVM_MAX_MODULES; i++) {
if (g_modules[i].used && g_modules[i].runtime == rt)
return &g_modules[i];
}
return NULL;
}
void
gvm_modtab_free(gvm_module* m)
{
if (!m || !m->used) return;
// only the last reference proceeds to teardown
if (InterlockedDecrement(&m->refcount) > 0) return;
// block until any in-flight m3_Call finishes so the runtime is quiescent
KeWaitForSingleObject(&m->call_mutex, Executive, KernelMode, FALSE, NULL);
if (m->runtime) {
m3_FreeRuntime(m->runtime);
m->runtime = NULL;
}
if (m->env) {
m3_FreeEnvironment(m->env);
m->env = NULL;
}
if (m->wasm_bytes) {
ExFreePoolWithTag(m->wasm_bytes, GVM_TAG_WBUF);
m->wasm_bytes = NULL;
}
m->module = NULL;
m->wasm_size = 0;
m->hash = 0;
// publish used=FALSE before releasing so waiters see it on retry
m->used = FALSE;
KeReleaseMutex(&m->call_mutex, FALSE);
}