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