mirror of
https://github.com/zer0condition/GoodmansKernel/
synced 2026-08-19 01:14:35 +00:00
151 lines
3.9 KiB
C
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);
|
|
}
|