DF-3109 / corrupt_root.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 | /* * DF-3109: corrupt the CRC of the ROOT B-Tree node of a HAMMER1 image so * the first hammer_get_node(vol0_btree_root) in hammer_init_cursor fails * (hammer_load_node, hammer_ondisk.c:1315-1333 -> EIO/EDOM), which hits * * KKASSERT(error == 0); hammer_cursor.c:202 * // (the original hammer_done_cursor cleanup line is commented out upstream) * * (the cleanup line is commented out) on INVARIANTS kernels and leaks * ip->lock + cursor_ip_refs on production ones. * * Strategy: scan the image for CRC-valid B-Tree nodes; the ROOT node is * the one whose parent offset is 0. Flip one byte in the middle of it so * the stored CRC no longer matches. * * usage: corrupt_root <image> */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <fcntl.h> #include <unistd.h> #define BUFSIZE 4096 #define BTREE_TYPE_L (('L') & 0xff) #define BTREE_TYPE_I (('I') & 0xff) static uint32_t crc32_tab[256]; static void crc32_init(void) { for (uint32_t i = 0; i < 256; i++) { uint32_t c = i; for (int k = 0; k < 8; k++) c = (c & 1) ? 0xEDB88320U ^ (c >> 1) : c >> 1; crc32_tab[i] = c; } } static uint32_t crc32_buf(const void *p, size_t n) { const unsigned char *b = p; uint32_t c = 0xFFFFFFFFU; for (size_t i = 0; i < n; i++) c = crc32_tab[(c ^ b[i]) & 0xff] ^ (c >> 8); return c ^ 0xFFFFFFFFU; } static uint32_t crc32c_tab[256]; static void crc32c_init(void) { for (uint32_t i = 0; i < 256; i++) { uint32_t c = i; for (int k = 0; k < 8; k++) c = (c & 1) ? 0x82F63B78U ^ (c >> 1) : c >> 1; crc32c_tab[i] = c; } } static uint32_t crc32c_buf(const void *p, size_t n) { const unsigned char *b = p; uint32_t c = 0xFFFFFFFFU; for (size_t i = 0; i < n; i++) c = crc32c_tab[(c ^ b[i]) & 0xff] ^ (c >> 8); return c ^ 0xFFFFFFFFU; } int main(int argc, char **argv) { unsigned char node[BUFSIZE]; uint64_t parent, root_off; uint32_t stored, calc_a, calc_b; off_t off, vol0_root = -1; int fd, count, patched = 0, nodes = 0; if (argc != 2) { fprintf(stderr, "usage: %s <image>\n", argv[0]); exit(2); } crc32_init(); crc32c_init(); fd = open(argv[1], O_RDWR); if (fd < 0) { perror("open"); exit(2); } /* vol0_btree_root lives at offset 240 in the root volume header */ if (pread(fd, &root_off, 8, 240) != 8) { perror("pread hdr"); exit(2); } printf("vol0_btree_root = 0x%016jx (zone %jd)\n", (uintmax_t)root_off, (uintmax_t)(root_off >> 60)); for (off = 0; ; off += 512) { if (pread(fd, node, BUFSIZE, off) != BUFSIZE) break; memcpy(&stored, node, 4); memcpy(&count, node + 16, 4); if (node[20] != BTREE_TYPE_L && node[20] != BTREE_TYPE_I) continue; if (count < 1 || count > 63) continue; calc_a = crc32_buf(node + 4, BUFSIZE - 4); calc_b = crc32c_buf(node + 4, BUFSIZE - 4); if (stored != calc_a && stored != calc_b) continue; memcpy(&parent, node + 8, 8); nodes++; if (parent != 0) continue; /* root node: flip a byte in the middle -> CRC mismatch */ node[2048] ^= 0x5A; if (pwrite(fd, node, BUFSIZE, off) != BUFSIZE) { perror("pwrite"); exit(1); } printf("corrupted ROOT node @ image offset %jd " "(stored crc %08x, now invalid)\n", (intmax_t)off, stored); patched++; } printf("done: %d valid btree nodes seen, %d root node(s) corrupted\n", nodes, patched); close(fd); return patched ? 0 : 1; } |