DF-2675 / bufscan.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | /* * DF-2675 bufscan: locate struct buf's b_kvabase field by signature * (buf[n].b_kvabase - buf[n-1].b_kvabase == MAXBSIZE), then report the * buffer-slot KVA range and, for each header, the number of present PTEs * in its slot (via /dev/mem page-table walk restricted to that range). * * Output: "SLOTBASE <kva>" then "OVR n=<header> kva=<slot kva> pages=<p>" * for every slot with more than MAXBSIZE/PAGE_SIZE (16) present PTEs. */ #include <sys/types.h> #include <err.h> #include <fcntl.h> #include <kvm.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define PMASK 0x000FFFFFFFFFF000ULL #define MAXBSIZE_ 65536 static int memfd; static uint64_t KPML4; static int rdphys(uint64_t pa, void *buf, size_t len) { return (pread(memfd, buf, len, (off_t)pa) == (ssize_t)len); } static uint64_t virt2pte(uint64_t va, uint64_t *pte_out) { uint64_t pml4[512], pud[512], pd[512]; int i4, i3, i2; i4 = (int)((va >> 39) & 511); i3 = (int)((va >> 30) & 511); i2 = (int)((va >> 21) & 511); if (!rdphys(KPML4, pml4, 4096)) return 1; if (!(pml4[i4] & 1)) return 1; if (!rdphys(pml4[i4] & PMASK, pud, 4096)) return 1; if (!(pud[i3] & 1)) return 1; if (pud[i3] & 0x80) return 2; /* 1GB */ if (!rdphys(pud[i3] & PMASK, pd, 4096)) return 1; if (!(pd[i2] & 1)) return 1; if (pd[i2] & 0x80) return 2; /* 2MB */ { uint64_t pt[512]; if (!rdphys(pd[i2] & PMASK, pt, 4096)) return 1; *pte_out = pt[(va >> 12) & 511]; } return 0; } int main(void) { kvm_t *kd; struct nlist nl[] = { { "_buf" }, { "_KPML4phys" }, { NULL } }; unsigned char *arr; long nbuf, szbuf, n; int off, bestoff = -1; long besthits = 0; kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "bufscan"); if (!kd) return 1; if (kvm_nlist(kd, nl) != 0 || nl[0].n_value == 0 || nl[1].n_value == 0) errx(1, "nlist buf/KPML4phys"); if (kvm_read(kd, nl[1].n_value, &KPML4, 8) != 8) errx(1, "read KPML4phys"); { size_t l; int v; l = sizeof(v); if (sysctlbyname("vfs.nbuf", &v, &l, NULL, 0) < 0) err(1, "sysctl vfs.nbuf"); nbuf = v; l = sizeof(v); if (sysctlbyname("debug.sizeof.buf", &v, &l, NULL, 0) < 0) err(1, "sysctl debug.sizeof.buf"); szbuf = v; } kvm_close(kd); fprintf(stderr, "nbuf=%ld sizeof(buf)=%ld\n", nbuf, szbuf); memfd = open("/dev/mem", O_RDONLY); if (memfd < 0) err(1, "/dev/mem"); arr = malloc(nbuf * szbuf); if (!arr) err(1, "malloc"); kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "bufscan2"); if (!kd) return 1; { uint64_t bufva; if (kvm_read(kd, nl[0].n_value, &bufva, 8) != 8) errx(1, "kvm_read &buf pointer"); fprintf(stderr, "buf array VA = %#llx\n", (unsigned long long)bufva); if (kvm_read(kd, bufva, arr, nbuf * szbuf) != nbuf * szbuf) errx(1, "kvm_read buf array (%ld bytes)", nbuf * szbuf); } kvm_close(kd); for (off = 0; off + 8 <= szbuf; off += 8) { long hits = 0; uint64_t prev = 0; for (n = 0; n < nbuf; n++) { uint64_t v; memcpy(&v, arr + n * szbuf + off, 8); if (prev && v && v - prev == MAXBSIZE_) ++hits; if (v) prev = v; if (hits > besthits) { besthits = hits; bestoff = off; } } } fprintf(stderr, "b_kvabase offset = %d (hits=%ld of %ld)\n", bestoff, besthits, nbuf); if (bestoff < 0 || besthits < nbuf / 2) errx(1, "could not locate b_kvabase field"); { uint64_t base0, last = 0; memcpy(&base0, arr + bestoff, 8); for (n = nbuf - 1; n >= 0; n--) { memcpy(&last, arr + n * szbuf + bestoff, 8); if (last) break; } printf("SLOTBASE %#llx slots %ld .. %#llx\n", (unsigned long long)base0, nbuf, (unsigned long long)last); /* count present PTEs per slot; report slots > 16 */ for (n = 0; n < nbuf; n++) { uint64_t kv, p; int pages = 0, i, rc; memcpy(&kv, arr + n * szbuf + bestoff, 8); if (kv == 0) continue; for (i = 0; i < MAXBSIZE_ / 4096 + 2; i++) { rc = virt2pte(kv + (uint64_t)i * 4096, &p); if (rc != 0) { if (rc == 2) { pages = -1; } break; } if (p & 1) pages++; } if (pages > MAXBSIZE_ / 4096) printf("OVR n=%ld kva=%#llx pages=%d\n", n, (unsigned long long)kv, pages); } } return 0; } |