]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - db/fuzz.c
xfs_db: Don't ASSERT on unrecognized metadata
[thirdparty/xfsprogs-dev.git] / db / fuzz.c
1 /*
2 * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include "libxfs.h"
20 #include <ctype.h>
21 #include <time.h>
22 #include "bit.h"
23 #include "block.h"
24 #include "command.h"
25 #include "type.h"
26 #include "faddr.h"
27 #include "fprint.h"
28 #include "field.h"
29 #include "flist.h"
30 #include "io.h"
31 #include "init.h"
32 #include "output.h"
33 #include "print.h"
34 #include "write.h"
35 #include "malloc.h"
36
37 static int fuzz_f(int argc, char **argv);
38 static void fuzz_help(void);
39
40 static const cmdinfo_t fuzz_cmd =
41 { "fuzz", NULL, fuzz_f, 0, -1, 0, N_("[-c] [-d] field fuzzcmd..."),
42 N_("fuzz values on disk"), fuzz_help };
43
44 void
45 fuzz_init(void)
46 {
47 if (!expert_mode)
48 return;
49
50 add_command(&fuzz_cmd);
51 srand48(clock());
52 }
53
54 static void
55 fuzz_help(void)
56 {
57 dbprintf(_(
58 "\n"
59 " The 'fuzz' command fuzzes fields in any on-disk data structure. For\n"
60 " block fuzzing, see the 'blocktrash' or 'write' commands."
61 "\n"
62 " Examples:\n"
63 " Struct mode: 'fuzz core.uid zeroes' - set an inode uid field to 0.\n"
64 " 'fuzz crc ones' - set a crc filed to all ones.\n"
65 " 'fuzz bno[11] firstbit' - set the high bit of a block array.\n"
66 " 'fuzz keys[5].startblock add' - increase a btree key value.\n"
67 " 'fuzz uuid random' - randomize the superblock uuid.\n"
68 "\n"
69 " Type 'fuzz' by itself for a list of specific commands.\n\n"
70 " Specifying the -c option will allow writes of invalid (corrupt) data with\n"
71 " an invalid CRC. Specifying the -d option will allow writes of invalid data,\n"
72 " but still recalculate the CRC so we are forced to check and detect the\n"
73 " invalid data appropriately.\n\n"
74 ));
75
76 }
77
78 static int
79 fuzz_f(
80 int argc,
81 char **argv)
82 {
83 pfunc_t pf;
84 extern char *progname;
85 int c;
86 bool corrupt = false; /* Allow write of bad data w/ invalid CRC */
87 bool invalid_data = false; /* Allow write of bad data w/ valid CRC */
88 struct xfs_buf_ops local_ops;
89 const struct xfs_buf_ops *stashed_ops = NULL;
90
91 if (x.isreadonly & LIBXFS_ISREADONLY) {
92 dbprintf(_("%s started in read only mode, fuzzing disabled\n"),
93 progname);
94 return 0;
95 }
96
97 if (cur_typ == NULL) {
98 dbprintf(_("no current type\n"));
99 return 0;
100 }
101
102 pf = cur_typ->pfunc;
103 if (pf == NULL) {
104 dbprintf(_("no handler function for type %s, fuzz unsupported.\n"),
105 cur_typ->name);
106 return 0;
107 }
108
109 while ((c = getopt(argc, argv, "cd")) != EOF) {
110 switch (c) {
111 case 'c':
112 corrupt = true;
113 break;
114 case 'd':
115 invalid_data = true;
116 break;
117 default:
118 dbprintf(_("bad option for fuzz command\n"));
119 return 0;
120 }
121 }
122
123 if (corrupt && invalid_data) {
124 dbprintf(_("Cannot specify both -c and -d options\n"));
125 return 0;
126 }
127
128 if (invalid_data &&
129 iocur_top->typ->crc_off == TYP_F_NO_CRC_OFF &&
130 xfs_sb_version_hascrc(&mp->m_sb)) {
131 dbprintf(_("Cannot recalculate CRCs on this type of object\n"));
132 return 0;
133 }
134
135 argc -= optind;
136 argv += optind;
137
138 /*
139 * If the buffer has no verifier or we are using standard verifier
140 * paths, then just fuzz it and return
141 */
142 if (!iocur_top->bp->b_ops ||
143 !(corrupt || invalid_data)) {
144 (*pf)(DB_FUZZ, cur_typ->fields, argc, argv);
145 return 0;
146 }
147
148
149 /* Temporarily remove write verifier to write bad data */
150 stashed_ops = iocur_top->bp->b_ops;
151 local_ops.verify_read = stashed_ops->verify_read;
152 iocur_top->bp->b_ops = &local_ops;
153
154 if (!xfs_sb_version_hascrc(&mp->m_sb)) {
155 local_ops.verify_write = xfs_dummy_verify;
156 } else if (corrupt) {
157 local_ops.verify_write = xfs_dummy_verify;
158 dbprintf(_("Allowing fuzz of corrupted data and bad CRC\n"));
159 } else if (iocur_top->typ->crc_off == TYP_F_CRC_FUNC) {
160 local_ops.verify_write = iocur_top->typ->set_crc;
161 dbprintf(_("Allowing fuzz of corrupted data with good CRC\n"));
162 } else { /* invalid data */
163 local_ops.verify_write = xfs_verify_recalc_crc;
164 dbprintf(_("Allowing fuzz of corrupted data with good CRC\n"));
165 }
166
167 (*pf)(DB_FUZZ, cur_typ->fields, argc, argv);
168
169 iocur_top->bp->b_ops = stashed_ops;
170
171 return 0;
172 }
173
174 /* Write zeroes to the field */
175 static bool
176 fuzz_zeroes(
177 void *buf,
178 int bitoff,
179 int nbits)
180 {
181 char *out = buf;
182 int bit;
183
184 if (bitoff % NBBY || nbits % NBBY) {
185 for (bit = 0; bit < nbits; bit++)
186 setbit_l(out, bit + bitoff, 0);
187 } else
188 memset(out + byteize(bitoff), 0, byteize(nbits));
189 return true;
190 }
191
192 /* Write ones to the field */
193 static bool
194 fuzz_ones(
195 void *buf,
196 int bitoff,
197 int nbits)
198 {
199 char *out = buf;
200 int bit;
201
202 if (bitoff % NBBY || nbits % NBBY) {
203 for (bit = 0; bit < nbits; bit++)
204 setbit_l(out, bit + bitoff, 1);
205 } else
206 memset(out + byteize(bitoff), 0xFF, byteize(nbits));
207 return true;
208 }
209
210 /* Flip the high bit in the (presumably big-endian) field */
211 static bool
212 fuzz_firstbit(
213 void *buf,
214 int bitoff,
215 int nbits)
216 {
217 setbit_l((char *)buf, bitoff, !getbit_l((char *)buf, bitoff));
218 return true;
219 }
220
221 /* Flip the low bit in the (presumably big-endian) field */
222 static bool
223 fuzz_lastbit(
224 void *buf,
225 int bitoff,
226 int nbits)
227 {
228 setbit_l((char *)buf, bitoff + nbits - 1,
229 !getbit_l((char *)buf, bitoff + nbits - 1));
230 return true;
231 }
232
233 /* Flip the middle bit in the (presumably big-endian) field */
234 static bool
235 fuzz_middlebit(
236 void *buf,
237 int bitoff,
238 int nbits)
239 {
240 setbit_l((char *)buf, bitoff + nbits / 2,
241 !getbit_l((char *)buf, bitoff + nbits / 2));
242 return true;
243 }
244
245 /* Format and shift a number into a buffer for setbitval. */
246 static char *
247 format_number(
248 uint64_t val,
249 __be64 *out,
250 int bit_length)
251 {
252 int offset;
253 char *rbuf = (char *)out;
254
255 /*
256 * If the length of the field is not a multiple of a byte, push
257 * the bits up in the field, so the most signicant field bit is
258 * the most significant bit in the byte:
259 *
260 * before:
261 * val |----|----|----|----|----|--MM|mmmm|llll|
262 * after
263 * val |----|----|----|----|----|MMmm|mmll|ll00|
264 */
265 offset = bit_length % NBBY;
266 if (offset)
267 val <<= (NBBY - offset);
268
269 /*
270 * convert to big endian and copy into the array
271 * rbuf |----|----|----|----|----|MMmm|mmll|ll00|
272 */
273 *out = cpu_to_be64(val);
274
275 /*
276 * Align the array to point to the field in the array.
277 * rbuf = |MMmm|mmll|ll00|
278 */
279 offset = sizeof(__be64) - 1 - ((bit_length - 1) / sizeof(__be64));
280 return rbuf + offset;
281 }
282
283 /* Increase the value by some small prime number. */
284 static bool
285 fuzz_add(
286 void *buf,
287 int bitoff,
288 int nbits)
289 {
290 uint64_t val;
291 __be64 out;
292 char *b;
293
294 if (nbits > 64)
295 return false;
296
297 val = getbitval(buf, bitoff, nbits, BVUNSIGNED);
298 val += (nbits > 8 ? 2017 : 137);
299 b = format_number(val, &out, nbits);
300 setbitval(buf, bitoff, nbits, b);
301
302 return true;
303 }
304
305 /* Decrease the value by some small prime number. */
306 static bool
307 fuzz_sub(
308 void *buf,
309 int bitoff,
310 int nbits)
311 {
312 uint64_t val;
313 __be64 out;
314 char *b;
315
316 if (nbits > 64)
317 return false;
318
319 val = getbitval(buf, bitoff, nbits, BVUNSIGNED);
320 val -= (nbits > 8 ? 2017 : 137);
321 b = format_number(val, &out, nbits);
322 setbitval(buf, bitoff, nbits, b);
323
324 return true;
325 }
326
327 /* Randomize the field. */
328 static bool
329 fuzz_random(
330 void *buf,
331 int bitoff,
332 int nbits)
333 {
334 int i, bytes;
335 char *b, *rbuf;
336
337 bytes = byteize_up(nbits);
338 rbuf = b = malloc(bytes);
339 if (!b) {
340 perror("fuzz_random");
341 return false;
342 }
343
344 for (i = 0; i < bytes; i++)
345 *b++ = (char)lrand48();
346
347 setbitval(buf, bitoff, nbits, rbuf);
348 free(rbuf);
349
350 return true;
351 }
352
353 struct fuzzcmd {
354 const char *verb;
355 bool (*fn)(void *buf, int bitoff, int nbits);
356 };
357
358 /* Keep these verbs in sync with enum fuzzcmds. */
359 static struct fuzzcmd fuzzverbs[] = {
360 {"zeroes", fuzz_zeroes},
361 {"ones", fuzz_ones},
362 {"firstbit", fuzz_firstbit},
363 {"middlebit", fuzz_middlebit},
364 {"lastbit", fuzz_lastbit},
365 {"add", fuzz_add},
366 {"sub", fuzz_sub},
367 {"random", fuzz_random},
368 {NULL, NULL},
369 };
370
371 /* ARGSUSED */
372 void
373 fuzz_struct(
374 const field_t *fields,
375 int argc,
376 char **argv)
377 {
378 const ftattr_t *fa;
379 flist_t *fl;
380 flist_t *sfl;
381 int bit_length;
382 struct fuzzcmd *fc;
383 bool success;
384 int parentoffset;
385
386 if (argc != 2) {
387 dbprintf(_("Usage: fuzz fieldname fuzzcmd\n"));
388 dbprintf("Fuzz commands: %s", fuzzverbs->verb);
389 for (fc = fuzzverbs + 1; fc->verb != NULL; fc++)
390 dbprintf(", %s", fc->verb);
391 dbprintf(".\n");
392 return;
393 }
394
395 fl = flist_scan(argv[0]);
396 if (!fl) {
397 dbprintf(_("unable to parse '%s'.\n"), argv[0]);
398 return;
399 }
400
401 /* Find our fuzz verb */
402 for (fc = fuzzverbs; fc->verb != NULL; fc++)
403 if (!strcmp(fc->verb, argv[1]))
404 break;
405 if (fc->fn == NULL) {
406 dbprintf(_("Unknown fuzz command '%s'.\n"), argv[1]);
407 goto out_free;
408 }
409
410 /* if we're a root field type, go down 1 layer to get field list */
411 if (fields->name[0] == '\0') {
412 fa = &ftattrtab[fields->ftyp];
413 ASSERT(fa->ftyp == fields->ftyp);
414 fields = fa->subfld;
415 }
416
417 /* run down the field list and set offsets into the data */
418 if (!flist_parse(fields, fl, iocur_top->data, 0)) {
419 dbprintf(_("parsing error\n"));
420 goto out_free;
421 }
422
423 sfl = fl;
424 parentoffset = 0;
425 while (sfl->child) {
426 parentoffset = sfl->offset;
427 sfl = sfl->child;
428 }
429
430 /*
431 * For structures, fsize * fcount tells us the size of the region we are
432 * modifying, which is usually a single structure member and is pointed
433 * to by the last child in the list.
434 *
435 * However, if the base structure is an array and we have a direct index
436 * into the array (e.g. write bno[5]) then we are returned a single
437 * flist object with the offset pointing directly at the location we
438 * need to modify. The length of the object we are modifying is then
439 * determined by the size of the individual array entry (fsize) and the
440 * indexes defined in the object, not the overall size of the array
441 * (which is what fcount returns).
442 */
443 bit_length = fsize(sfl->fld, iocur_top->data, parentoffset, 0);
444 if (sfl->fld->flags & FLD_ARRAY)
445 bit_length *= sfl->high - sfl->low + 1;
446 else
447 bit_length *= fcount(sfl->fld, iocur_top->data, parentoffset);
448
449 /* Fuzz the value */
450 success = fc->fn(iocur_top->data, sfl->offset, bit_length);
451 if (!success) {
452 dbprintf(_("unable to fuzz field '%s'\n"), argv[0]);
453 goto out_free;
454 }
455
456 /* Write the fuzzed value back */
457 write_cur();
458
459 flist_print(fl);
460 print_flist(fl);
461 out_free:
462 flist_free(fl);
463 }