Files
vatrog-vm-introspection-engine/src/proc.c
T
lirent 4015e839eb Zero-copy hot path, correctness hardening
gva_ptr: leaf-bounded zero-copy guest reads. gva_sweep redesigned to drive
on it — large-page leaves are lent to the callback while 4K runs stay
buffered, and the run loop is guarded against wrap at the top of the address
space. gva_gpa fetches PTEs zero-copy; optional W32MS_LTO build option folds
the per-fetch call boundary (shipped -O2 default unchanged).

Correctness: subtract-form bounds check (no add overflow), memcpy decode in
place of type-punned wide loads, zero-init PDB name before compare,
PCI-hole-crossing range rejection, single-sourced VA_CANON and USER bounds.
hot/cold attributes audited across the translation and scan path.
2026-06-15 01:05:00 +03:00

112 lines
3.3 KiB
C

#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "include/memory.h"
#include "../include/include.h"
#define pr_(ctx) ((ctx)->prof)
#define RING_GUARD 100000u
#define MOD_GUARD 4096u
static void grab_ustr(gva_ctx* ctx, uintptr_t cr3, uint64_t va, gtext* out) {
uint16_t len = 0;
uint64_t buf = 0;
out->va = 0;
out->len = 0;
if (gva_read(ctx, cr3, va, &len, 2) || gva_read(ctx, cr3, va + 8, &buf, 8)) {
return;
}
out->va = buf;
out->len = len;
}
int proc_list(gva_ctx* ctx, int skip_system, process* dst, size_t nmax) {
const profile* p = &pr_(ctx);
const uint64_t kcr3 = ctx->kcr3;
if (!kcr3 || !ctx->sysproc) {
return -1;
}
size_t n = 0;
unsigned guard = 0;
uint64_t ep = ctx->sysproc, node;
do {
uint64_t pid = 0, ppid = 0, dtb = 0, peb = 0;
gva_read(ctx, kcr3, ep + p->ep_pid, &pid, 8);
gva_read(ctx, kcr3, ep + p->ep_dtb, &dtb, 8);
if (p->ep_peb) { gva_read(ctx, kcr3, ep + p->ep_peb, &peb, 8); }
if (p->ep_ppid) { gva_read(ctx, kcr3, ep + p->ep_ppid, &ppid, 8); }
if (!skip_system || peb) {
if (n >= nmax) {
return (int)n;
}
process* q = &dst[n++];
q->eprocess = ep;
q->cr3 = dtb & PFN_MASK;
q->peb = peb;
q->pid = (uint32_t)pid;
q->ppid = p->ep_ppid ? (uint32_t)ppid : (uint32_t)-1;
q->create_time = 0;
if (p->ep_createtime) {
gva_read(ctx, kcr3, ep + p->ep_createtime, &q->create_time, 8);
}
memset(q->name, 0, sizeof q->name);
gva_read(ctx, kcr3, ep + p->ep_name, q->name, sizeof q->name - 1);
q->path.va = 0;
q->path.len = 0;
if (p->ep_imgpath) {
grab_ustr(ctx, kcr3, ep + p->ep_imgpath, &q->path); /* read text under kcr3 */
}
}
if (gva_read(ctx, kcr3, ep + p->ep_links, &node, 8)) {
break;
}
ep = node - p->ep_links;
} while (ep != ctx->sysproc && ++guard < RING_GUARD);
return (int)n;
}
int proc_modules(gva_ctx* ctx, const process* pr, pmodule* dst, size_t nmax) {
const profile* p = &pr_(ctx);
const uint64_t cr3 = pr->cr3;
if (!pr->peb || !cr3) {
return 0;
}
uint64_t ldr = 0, head, link;
if (gva_read(ctx, cr3, pr->peb + p->peb_ldr, &ldr, 8) || !ldr) {
return 0;
}
head = ldr + p->ldr_loadlist;
if (gva_read(ctx, cr3, head, &link, 8)) {
return 0;
}
size_t n = 0;
unsigned guard = 0;
while (link != head && n < nmax && ++guard < MOD_GUARD) {
const uint64_t entry = link; /* InLoadOrderLinks at offset 0 of the entry */
uint64_t base = 0;
uint32_t size = 0;
gva_read(ctx, cr3, entry + p->lde_base, &base, 8);
gva_read(ctx, cr3, entry + p->lde_size, &size, 4);
pmodule* m = &dst[n++];
m->pr = pr;
m->entry = entry;
m->base = base;
m->size = size;
grab_ustr(ctx, cr3, entry + p->lde_name, &m->name);
grab_ustr(ctx, cr3, entry + p->lde_fullname, &m->path);
if (gva_read(ctx, cr3, link, &link, 8)) {
break;
}
}
return (int)n;
}