]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - db/fuzz.c
xfs_db: free field list when failing out of fuzz
[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 && iocur_top->typ->crc_off == TYP_F_NO_CRC_OFF) {
129 dbprintf(_("Cannot recalculate CRCs on this type of object\n"));
130 return 0;
131 }
132
133 argc -= optind;
134 argv += optind;
135
136 /*
137 * If the buffer has no verifier or we are using standard verifier
138 * paths, then just fuzz it and return
139 */
140 if (!iocur_top->bp->b_ops ||
141 !(corrupt || invalid_data)) {
142 (*pf)(DB_FUZZ, cur_typ->fields, argc, argv);
143 return 0;
144 }
145
146
147 /* Temporarily remove write verifier to write bad data */
148 stashed_ops = iocur_top->bp->b_ops;
149 local_ops.verify_read = stashed_ops->verify_read;
150 iocur_top->bp->b_ops = &local_ops;
151
152 if (corrupt) {
153 local_ops.verify_write = xfs_dummy_verify;
154 dbprintf(_("Allowing fuzz of corrupted data and bad CRC\n"));
155 } else if (iocur_top->typ->crc_off == TYP_F_CRC_FUNC) {
156 local_ops.verify_write = iocur_top->typ->set_crc;
157 dbprintf(_("Allowing fuzz of corrupted data with good CRC\n"));
158 } else { /* invalid data */
159 local_ops.verify_write = xfs_verify_recalc_crc;
160 dbprintf(_("Allowing fuzz of corrupted data with good CRC\n"));
161 }
162
163 (*pf)(DB_FUZZ, cur_typ->fields, argc, argv);
164
165 iocur_top->bp->b_ops = stashed_ops;
166
167 return 0;
168 }
169
170 /* Write zeroes to the field */
171 static bool
172 fuzz_zeroes(
173 void *buf,
174 int bitoff,
175 int nbits)
176 {
177 char *out = buf;
178 int bit;
179
180 if (bitoff % NBBY || nbits % NBBY) {
181 for (bit = 0; bit < nbits; bit++)
182 setbit_l(out, bit + bitoff, 0);
183 } else
184 memset(out + byteize(bitoff), 0, byteize(nbits));
185 return true;
186 }
187
188 /* Write ones to the field */
189 static bool
190 fuzz_ones(
191 void *buf,
192 int bitoff,
193 int nbits)
194 {
195 char *out = buf;
196 int bit;
197
198 if (bitoff % NBBY || nbits % NBBY) {
199 for (bit = 0; bit < nbits; bit++)
200 setbit_l(out, bit + bitoff, 1);
201 } else
202 memset(out + byteize(bitoff), 0xFF, byteize(nbits));
203 return true;
204 }
205
206 /* Flip the high bit in the (presumably big-endian) field */
207 static bool
208 fuzz_firstbit(
209 void *buf,
210 int bitoff,
211 int nbits)
212 {
213 setbit_l((char *)buf, bitoff, !getbit_l((char *)buf, bitoff));
214 return true;
215 }
216
217 /* Flip the low bit in the (presumably big-endian) field */
218 static bool
219 fuzz_lastbit(
220 void *buf,
221 int bitoff,
222 int nbits)
223 {
224 setbit_l((char *)buf, bitoff + nbits - 1,
225 !getbit_l((char *)buf, bitoff));
226 return true;
227 }
228
229 /* Flip the middle bit in the (presumably big-endian) field */
230 static bool
231 fuzz_middlebit(
232 void *buf,
233 int bitoff,
234 int nbits)
235 {
236 setbit_l((char *)buf, bitoff + nbits / 2,
237 !getbit_l((char *)buf, bitoff));
238 return true;
239 }
240
241 /* Format and shift a number into a buffer for setbitval. */
242 static char *
243 format_number(
244 uint64_t val,
245 __be64 *out,
246 int bit_length)
247 {
248 int offset;
249 char *rbuf = (char *)out;
250
251 /*
252 * If the length of the field is not a multiple of a byte, push
253 * the bits up in the field, so the most signicant field bit is
254 * the most significant bit in the byte:
255 *
256 * before:
257 * val |----|----|----|----|----|--MM|mmmm|llll|
258 * after
259 * val |----|----|----|----|----|MMmm|mmll|ll00|
260 */
261 offset = bit_length % NBBY;
262 if (offset)
263 val <<= (NBBY - offset);
264
265 /*
266 * convert to big endian and copy into the array
267 * rbuf |----|----|----|----|----|MMmm|mmll|ll00|
268 */
269 *out = cpu_to_be64(val);
270
271 /*
272 * Align the array to point to the field in the array.
273 * rbuf = |MMmm|mmll|ll00|
274 */
275 offset = sizeof(__be64) - 1 - ((bit_length - 1) / sizeof(__be64));
276 return rbuf + offset;
277 }
278
279 /* Increase the value by some small prime number. */
280 static bool
281 fuzz_add(
282 void *buf,
283 int bitoff,
284 int nbits)
285 {
286 uint64_t val;
287 __be64 out;
288 char *b;
289
290 if (nbits > 64)
291 return false;
292
293 val = getbitval(buf, bitoff, nbits, BVUNSIGNED);
294 val += (nbits > 8 ? 2017 : 137);
295 b = format_number(val, &out, nbits);
296 setbitval(buf, bitoff, nbits, b);
297
298 return true;
299 }
300
301 /* Decrease the value by some small prime number. */
302 static bool
303 fuzz_sub(
304 void *buf,
305 int bitoff,
306 int nbits)
307 {
308 uint64_t val;
309 __be64 out;
310 char *b;
311
312 if (nbits > 64)
313 return false;
314
315 val = getbitval(buf, bitoff, nbits, BVUNSIGNED);
316 val -= (nbits > 8 ? 2017 : 137);
317 b = format_number(val, &out, nbits);
318 setbitval(buf, bitoff, nbits, b);
319
320 return true;
321 }
322
323 /* Randomize the field. */
324 static bool
325 fuzz_random(
326 void *buf,
327 int bitoff,
328 int nbits)
329 {
330 int i, bytes;
331 char *b, *rbuf;
332
333 bytes = byteize_up(nbits);
334 rbuf = b = malloc(bytes);
335 if (!b) {
336 perror("fuzz_random");
337 return false;
338 }
339
340 for (i = 0; i < bytes; i++)
341 *b++ = (char)lrand48();
342
343 setbitval(buf, bitoff, nbits, rbuf);
344 free(rbuf);
345
346 return true;
347 }
348
349 struct fuzzcmd {
350 const char *verb;
351 bool (*fn)(void *buf, int bitoff, int nbits);
352 };
353
354 /* Keep these verbs in sync with enum fuzzcmds. */
355 static struct fuzzcmd fuzzverbs[] = {
356 {"zeroes", fuzz_zeroes},
357 {"ones", fuzz_ones},
358 {"firstbit", fuzz_firstbit},
359 {"middlebit", fuzz_middlebit},
360 {"lastbit", fuzz_lastbit},
361 {"add", fuzz_add},
362 {"sub", fuzz_sub},
363 {"random", fuzz_random},
364 {NULL, NULL},
365 };
366
367 /* ARGSUSED */
368 void
369 fuzz_struct(
370 const field_t *fields,
371 int argc,
372 char **argv)
373 {
374 const ftattr_t *fa;
375 flist_t *fl;
376 flist_t *sfl;
377 int bit_length;
378 struct fuzzcmd *fc;
379 bool success;
380 int parentoffset;
381
382 if (argc != 2) {
383 dbprintf(_("Usage: fuzz fieldname fuzzcmd\n"));
384 dbprintf("Fuzz commands: %s", fuzzverbs->verb);
385 for (fc = fuzzverbs + 1; fc->verb != NULL; fc++)
386 dbprintf(", %s", fc->verb);
387 dbprintf(".\n");
388 return;
389 }
390
391 fl = flist_scan(argv[0]);
392 if (!fl) {
393 dbprintf(_("unable to parse '%s'.\n"), argv[0]);
394 return;
395 }
396
397 /* Find our fuzz verb */
398 for (fc = fuzzverbs; fc->verb != NULL; fc++)
399 if (!strcmp(fc->verb, argv[1]))
400 break;
401 if (fc->fn == NULL) {
402 dbprintf(_("Unknown fuzz command '%s'.\n"), argv[1]);
403 goto out_free;
404 }
405
406 /* if we're a root field type, go down 1 layer to get field list */
407 if (fields->name[0] == '\0') {
408 fa = &ftattrtab[fields->ftyp];
409 ASSERT(fa->ftyp == fields->ftyp);
410 fields = fa->subfld;
411 }
412
413 /* run down the field list and set offsets into the data */
414 if (!flist_parse(fields, fl, iocur_top->data, 0)) {
415 dbprintf(_("parsing error\n"));
416 goto out_free;
417 }
418
419 sfl = fl;
420 parentoffset = 0;
421 while (sfl->child) {
422 parentoffset = sfl->offset;
423 sfl = sfl->child;
424 }
425
426 /*
427 * For structures, fsize * fcount tells us the size of the region we are
428 * modifying, which is usually a single structure member and is pointed
429 * to by the last child in the list.
430 *
431 * However, if the base structure is an array and we have a direct index
432 * into the array (e.g. write bno[5]) then we are returned a single
433 * flist object with the offset pointing directly at the location we
434 * need to modify. The length of the object we are modifying is then
435 * determined by the size of the individual array entry (fsize) and the
436 * indexes defined in the object, not the overall size of the array
437 * (which is what fcount returns).
438 */
439 bit_length = fsize(sfl->fld, iocur_top->data, parentoffset, 0);
440 if (sfl->fld->flags & FLD_ARRAY)
441 bit_length *= sfl->high - sfl->low + 1;
442 else
443 bit_length *= fcount(sfl->fld, iocur_top->data, parentoffset);
444
445 /* Fuzz the value */
446 success = fc->fn(iocur_top->data, sfl->offset, bit_length);
447 if (!success) {
448 dbprintf(_("unable to fuzz field '%s'\n"), argv[0]);
449 goto out_free;
450 }
451
452 /* Write the fuzzed value back */
453 write_cur();
454
455 flist_print(fl);
456 print_flist(fl);
457 out_free:
458 flist_free(fl);
459 }