DF-2675 / ptecheck.c
/* * DF-2675 ptecheck: dump all present 4K PTEs in the kernel half of the * page tables of the RUNNING system, via /dev/mem + KPML4phys (kvm_nlist). * * Usage: ptecheck > snapshot.txt * Each line: "<KVA> <PhysAddr>" * * Diffing two snapshots around a single getblk() shows the pages the * buffer installed. A legitimate buffer can only add <= MAXBSIZE/PAGE_SIZE * (16) consecutive pages inside its own slot; a 17-page consecutive run is * the vfs_bio.c allocbuf() KVA-slot overflow signature. */ #include <sys/types.h> #include <err.h> #include <fcntl.h> #include <kvm.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define PMASK 0x000FFFFFFFFFF000ULL static int memfd; static uint64_t KPML4; static uint64_t rdphys(uint64_t pa, void *buf, size_t len) { return (pread(memfd, buf, len, (off_t)pa) == (ssize_t)len); } static void scan_pt(uint64_t ptphys, uint64_t va_base) { uint64_t pt[512]; int i; if (!rdphys(ptphys, pt, sizeof(pt))) return; for (i = 0; i < 512; ++i) { if ((pt[i] & 1) == 0) continue; if (pt[i] & 0x80) /* 2MB page - not buffer KVA */ continue; printf("%llu %llu\n", (unsigned long long)(va_base + ((uint64_t)i << 12)), (unsigned long long)(pt[i] & PMASK)); } } int main(int argc, char **argv) { kvm_t *kd; struct nlist nl[] = { { "_KPML4phys" }, { NULL } }; uint64_t pml4[512], pud[512]; int p4, p3; if (argc > 1) { fprintf(stderr, "usage: ptecheck > file\n"); return 1; } kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "ptecheck"); if (kd == NULL) return 1; if (kvm_nlist(kd, nl) != 0 || nl[0].n_value == 0) errx(1, "kvm_nlist: KPML4phys not found"); if (kvm_read(kd, nl[0].n_value, &KPML4, sizeof(KPML4)) != sizeof(KPML4)) errx(1, "kvm_read KPML4phys failed"); kvm_close(kd); memfd = open("/dev/mem", O_RDONLY); if (memfd < 0) err(1, "open /dev/mem"); if (!rdphys(KPML4, pml4, sizeof(pml4))) errx(1, "cannot read PML4 at phys %#jx", (uintmax_t)KPML4); for (p4 = 256; p4 < 512; ++p4) { /* kernel half */ if ((pml4[p4] & 1) == 0) continue; if (!rdphys(pml4[p4] & PMASK, pud, sizeof(pud))) continue; for (p3 = 0; p3 < 512; ++p3) { uint64_t va = ((uint64_t)p4 << 39) | ((uint64_t)p3 << 30); if ((pud[p3] & 1) == 0) continue; if (pud[p3] & 0x80) /* 1GB page */ continue; scan_pt(pud[p3] & PMASK, va); } } close(memfd); return 0; } |