]>
git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - db/fuzz.c
2 * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
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.
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.
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
37 static int fuzz_f(int argc
, char **argv
);
38 static void fuzz_help(void);
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
};
50 add_command(&fuzz_cmd
);
59 " The 'fuzz' command fuzzes fields in any on-disk data structure. For\n"
60 " block fuzzing, see the 'blocktrash' or 'write' commands."
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"
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"
84 extern char *progname
;
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
;
91 if (x
.isreadonly
& LIBXFS_ISREADONLY
) {
92 dbprintf(_("%s started in read only mode, fuzzing disabled\n"),
97 if (cur_typ
== NULL
) {
98 dbprintf(_("no current type\n"));
104 dbprintf(_("no handler function for type %s, fuzz unsupported.\n"),
109 while ((c
= getopt(argc
, argv
, "cd")) != EOF
) {
118 dbprintf(_("bad option for fuzz command\n"));
123 if (corrupt
&& invalid_data
) {
124 dbprintf(_("Cannot specify both -c and -d options\n"));
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"));
139 * If the buffer has no verifier or we are using standard verifier
140 * paths, then just fuzz it and return
142 if (!iocur_top
->bp
->b_ops
||
143 !(corrupt
|| invalid_data
)) {
144 (*pf
)(DB_FUZZ
, cur_typ
->fields
, argc
, argv
);
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
;
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"));
167 (*pf
)(DB_FUZZ
, cur_typ
->fields
, argc
, argv
);
169 iocur_top
->bp
->b_ops
= stashed_ops
;
174 /* Write zeroes to the field */
184 if (bitoff
% NBBY
|| nbits
% NBBY
) {
185 for (bit
= 0; bit
< nbits
; bit
++)
186 setbit_l(out
, bit
+ bitoff
, 0);
188 memset(out
+ byteize(bitoff
), 0, byteize(nbits
));
192 /* Write ones to the field */
202 if (bitoff
% NBBY
|| nbits
% NBBY
) {
203 for (bit
= 0; bit
< nbits
; bit
++)
204 setbit_l(out
, bit
+ bitoff
, 1);
206 memset(out
+ byteize(bitoff
), 0xFF, byteize(nbits
));
210 /* Flip the high bit in the (presumably big-endian) field */
217 setbit_l((char *)buf
, bitoff
, !getbit_l((char *)buf
, bitoff
));
221 /* Flip the low bit in the (presumably big-endian) field */
228 setbit_l((char *)buf
, bitoff
+ nbits
- 1,
229 !getbit_l((char *)buf
, bitoff
+ nbits
- 1));
233 /* Flip the middle bit in the (presumably big-endian) field */
240 setbit_l((char *)buf
, bitoff
+ nbits
/ 2,
241 !getbit_l((char *)buf
, bitoff
+ nbits
/ 2));
245 /* Format and shift a number into a buffer for setbitval. */
253 char *rbuf
= (char *)out
;
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:
261 * val |----|----|----|----|----|--MM|mmmm|llll|
263 * val |----|----|----|----|----|MMmm|mmll|ll00|
265 offset
= bit_length
% NBBY
;
267 val
<<= (NBBY
- offset
);
270 * convert to big endian and copy into the array
271 * rbuf |----|----|----|----|----|MMmm|mmll|ll00|
273 *out
= cpu_to_be64(val
);
276 * Align the array to point to the field in the array.
277 * rbuf = |MMmm|mmll|ll00|
279 offset
= sizeof(__be64
) - 1 - ((bit_length
- 1) / sizeof(__be64
));
280 return rbuf
+ offset
;
283 /* Increase the value by some small prime number. */
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
);
305 /* Decrease the value by some small prime number. */
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
);
327 /* Randomize the field. */
337 bytes
= byteize_up(nbits
);
338 rbuf
= b
= malloc(bytes
);
340 perror("fuzz_random");
344 for (i
= 0; i
< bytes
; i
++)
345 *b
++ = (char)lrand48();
347 setbitval(buf
, bitoff
, nbits
, rbuf
);
355 bool (*fn
)(void *buf
, int bitoff
, int nbits
);
358 /* Keep these verbs in sync with enum fuzzcmds. */
359 static struct fuzzcmd fuzzverbs
[] = {
360 {"zeroes", fuzz_zeroes
},
362 {"firstbit", fuzz_firstbit
},
363 {"middlebit", fuzz_middlebit
},
364 {"lastbit", fuzz_lastbit
},
367 {"random", fuzz_random
},
374 const field_t
*fields
,
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
);
395 fl
= flist_scan(argv
[0]);
397 dbprintf(_("unable to parse '%s'.\n"), argv
[0]);
401 /* Find our fuzz verb */
402 for (fc
= fuzzverbs
; fc
->verb
!= NULL
; fc
++)
403 if (!strcmp(fc
->verb
, argv
[1]))
405 if (fc
->fn
== NULL
) {
406 dbprintf(_("Unknown fuzz command '%s'.\n"), argv
[1]);
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
);
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"));
426 parentoffset
= sfl
->offset
;
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.
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).
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;
447 bit_length
*= fcount(sfl
->fld
, iocur_top
->data
, parentoffset
);
450 success
= fc
->fn(iocur_top
->data
, sfl
->offset
, bit_length
);
452 dbprintf(_("unable to fuzz field '%s'\n"), argv
[0]);
456 /* Write the fuzzed value back */