DF-4528 / nvgetlog.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 | /* * DF-4528 / DF-4529 PoC -- NVMe admin-queue race / detach UAF prober. * * Issues NVMEIOCGETLOG (raw admin GET LOG PAGE) against an nvme(4) * namespace device: * * nvgetlog once <dev> single command, dump result (sanity) * nvgetlog spam <dev> <secs> loop getlog, per-ioctl latency stats * nvgetlog victim <dev> <secs> keep ONE fd open across detach and keep * issuing getlog on the (possibly stale) * descriptor; reports errors/latency. * * A call taking more than 3s is reported as STUCK (kernel nvwait with no * timeout -> unkillable thread, DF-4528 lost-command symptom). */ #include <sys/ioccom.h> #include <sys/types.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <signal.h> #include <time.h> typedef struct { unsigned char pad[4096]; } nvme_admin_data_t; typedef struct nvme_getlog_ioctl { uint8_t lid; /* log page to request */ uint8_t reserved01; uint16_t status; /* status return */ uint32_t ret_size; /* NUMDL/NUMDU */ uint32_t beg_offset; /* LPOL/LPOU */ nvme_admin_data_t info; /* return data */ } nvme_getlog_ioctl_t; #define NVMEIOCGETLOG _IOWR('O', 0, struct nvme_getlog_ioctl) static volatile sig_atomic_t stop; static void onsig(int sig) { stop = 1; } static double now(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9; } int main(int argc, char **argv) { nvme_getlog_ioctl_t ioc; double t0, t1, maxlat = 0.0, sum = 0.0; long n = 0, errs = 0, stuck = 0; int fd, delay_close = 0; const char *dev; if (argc < 3) { fprintf(stderr, "usage: %s once|spam|victim <dev> [secs]\n", argv[0]); return 2; } dev = argv[2]; if (strcmp(argv[1], "victim") == 0) delay_close = 1; signal(SIGINT, onsig); signal(SIGTERM, onsig); fd = open(dev, O_RDONLY); if (fd < 0) { perror(dev); return 1; } if (strcmp(argv[1], "once") == 0) { memset(&ioc, 0, sizeof(ioc)); ioc.lid = 1; /* error log */ ioc.ret_size = 4096; if (ioctl(fd, NVMEIOCGETLOG, &ioc) < 0) { perror("ioctl"); return 1; } printf("status=%u lid=%u first16:\n", ioc.status, ioc.lid); for (int i = 0; i < 16; i++) printf("%02x ", ioc.info.pad[i]); printf("\n"); return 0; } { double limit = argc > 3 ? atof(argv[3]) : 30.0; double start = now(); double last = start; while (!stop && now() - start < limit) { memset(&ioc, 0, sizeof(ioc)); ioc.lid = 1; ioc.ret_size = 4096; t0 = now(); if (ioctl(fd, NVMEIOCGETLOG, &ioc) < 0) { errs++; if (!delay_close) { /* re-open (device may have re-appeared) */ close(fd); usleep(20000); fd = open(dev, O_RDONLY); if (fd < 0) usleep(100000); } else { usleep(1000); } continue; } t1 = now(); if (t1 - t0 > maxlat) maxlat = t1 - t0; if (t1 - t0 > 3.0) { stuck++; printf("[%d] STUCK call took %.3fs (#%ld)\n", getpid(), t1 - t0, n); fflush(stdout); } sum += t1 - t0; n++; if (t1 - last > 1.0) { printf("[%d] n=%ld errs=%ld maxlat=%.4f " "avg=%.5f stuck=%ld\n", getpid(), n, errs, maxlat, n ? sum / n : 0.0, stuck); fflush(stdout); last = t1; } } } printf("[%d] DONE n=%ld errs=%ld maxlat=%.4f stuck=%ld\n", getpid(), n, errs, maxlat, stuck); return 0; } |