]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_db: introduce fuzz command
authorDarrick J. Wong <darrick.wong@oracle.com>
Fri, 4 Aug 2017 21:33:52 +0000 (16:33 -0500)
committerEric Sandeen <sandeen@redhat.com>
Fri, 4 Aug 2017 21:33:52 +0000 (16:33 -0500)
Introduce a new 'fuzz' command to write creative values into
disk structure fields.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
[sandeen: tweak words in help a bit for consistency]
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
db/Makefile
db/bit.c
db/bit.h
db/command.c
db/fuzz.c [new file with mode: 0644]
db/fuzz.h [new file with mode: 0644]
db/type.c
db/type.h
man/man8/xfs_db.8

index 6618bff2dd1ae451691bade9709c9f36eb8d5cbf..8111bf154554790d32da039e300d2b47bd20c01b 100644 (file)
@@ -12,7 +12,8 @@ HFILES = addr.h agf.h agfl.h agi.h attr.h attrshort.h bit.h block.h bmap.h \
        dir2.h dir2sf.h dquot.h echo.h faddr.h field.h \
        flist.h fprint.h frag.h freesp.h hash.h help.h init.h inode.h input.h \
        io.h logformat.h malloc.h metadump.h output.h print.h quit.h sb.h \
-        sig.h strvec.h text.h type.h write.h attrset.h symlink.h fsmap.h
+       sig.h strvec.h text.h type.h write.h attrset.h symlink.h fsmap.h \
+       fuzz.h
 CFILES = $(HFILES:.h=.c) btdump.c
 LSRCFILES = xfs_admin.sh xfs_ncheck.sh xfs_metadump.sh
 
index f5ebf681a7fac78dd83c6d1669a5c9d8ccaeb9da..a20b6ba5f5a9b77d0afd3f642c25cbaa6c00627a 100644 (file)
--- a/db/bit.c
+++ b/db/bit.c
 #include "libxfs.h"
 #include "bit.h"
 
-#undef setbit  /* defined in param.h on Linux */
-
-static int     getbit(char *ptr, int bit);
-static void    setbit(char *ptr, int bit, int val);
-
-static int
-getbit(
+int
+getbit_l(
        char    *ptr,
        int     bit)
 {
@@ -39,8 +34,8 @@ getbit(
        return (*ptr & mask) >> shift;
 }
 
-static void
-setbit(
+void
+setbit_l(
        char *ptr,
        int  bit,
        int  val)
@@ -106,7 +101,7 @@ getbitval(
 
 
        for (i = 0, rval = 0LL; i < nbits; i++) {
-               if (getbit(p, bit + i)) {
+               if (getbit_l(p, bit + i)) {
                        /* If the last bit is on and we care about sign
                         * bits and we don't have a full 64 bit
                         * container, turn all bits on between the
@@ -162,7 +157,7 @@ setbitval(
 
        if (bitoff % NBBY || nbits % NBBY) {
                for (bit = 0; bit < nbits; bit++)
-                       setbit(out, bit + bitoff, getbit(in, bit));
+                       setbit_l(out, bit + bitoff, getbit_l(in, bit));
        } else
                memcpy(out + byteize(bitoff), in, byteize(nbits));
 }
index 9fd71f4b8b80ece9e22ab610f14652bfebcb0c50..78fcd05a25eb9f795b576a7c05af5259469650af 100644 (file)
--- a/db/bit.h
+++ b/db/bit.h
 #define        bitszof(x,y)    bitize(szof(x,y))
 #define        byteize(s)      ((s) / NBBY)
 #define        bitoffs(s)      ((s) % NBBY)
+#define        byteize_up(s)   (((s) + NBBY - 1) / NBBY)
 
 #define        BVUNSIGNED      0
 #define        BVSIGNED        1
 
 extern int64_t         getbitval(void *obj, int bitoff, int nbits, int flags);
-extern void             setbitval(void *obuf, int bitoff, int nbits, void *ibuf);
+extern void            setbitval(void *obuf, int bitoff, int nbits, void *ibuf);
+extern int             getbit_l(char *ptr, int bit);
+extern void            setbit_l(char *ptr, int bit, int val);
index c90c85c560ca3e84a2746bc66c0cc5fdde2f8e5c..5ff3c4f0b40a0536e94c34593c67ad9893e4336b 100644 (file)
@@ -51,6 +51,7 @@
 #include "dquot.h"
 #include "fsmap.h"
 #include "crc.h"
+#include "fuzz.h"
 
 cmdinfo_t      *cmdtab;
 int            ncmds;
@@ -147,4 +148,5 @@ init_commands(void)
        type_init();
        write_init();
        dquot_init();
+       fuzz_init();
 }
diff --git a/db/fuzz.c b/db/fuzz.c
new file mode 100644 (file)
index 0000000..e1c2045
--- /dev/null
+++ b/db/fuzz.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "libxfs.h"
+#include <ctype.h>
+#include <time.h>
+#include "bit.h"
+#include "block.h"
+#include "command.h"
+#include "type.h"
+#include "faddr.h"
+#include "fprint.h"
+#include "field.h"
+#include "flist.h"
+#include "io.h"
+#include "init.h"
+#include "output.h"
+#include "print.h"
+#include "write.h"
+#include "malloc.h"
+
+static int     fuzz_f(int argc, char **argv);
+static void     fuzz_help(void);
+
+static const cmdinfo_t fuzz_cmd =
+       { "fuzz", NULL, fuzz_f, 0, -1, 0, N_("[-c] [-d] field fuzzcmd..."),
+         N_("fuzz values on disk"), fuzz_help };
+
+void
+fuzz_init(void)
+{
+       if (!expert_mode)
+               return;
+
+       add_command(&fuzz_cmd);
+       srand48(clock());
+}
+
+static void
+fuzz_help(void)
+{
+       dbprintf(_(
+"\n"
+" The 'fuzz' command fuzzes fields in any on-disk data structure.  For\n"
+" block fuzzing, see the 'blocktrash' or 'write' commands."
+"\n"
+" Examples:\n"
+"  Struct mode: 'fuzz core.uid zeroes'    - set an inode uid field to 0.\n"
+"               'fuzz crc ones'           - set a crc filed to all ones.\n"
+"               'fuzz bno[11] firstbit'   - set the high bit of a block array.\n"
+"               'fuzz keys[5].startblock add'    - increase a btree key value.\n"
+"               'fuzz uuid random'        - randomize the superblock uuid.\n"
+"\n"
+" Type 'fuzz' by itself for a list of specific commands.\n\n"
+" Specifying the -c option will allow writes of invalid (corrupt) data with\n"
+" an invalid CRC. Specifying the -d option will allow writes of invalid data,\n"
+" but still recalculate the CRC so we are forced to check and detect the\n"
+" invalid data appropriately.\n\n"
+));
+
+}
+
+static int
+fuzz_f(
+       int             argc,
+       char            **argv)
+{
+       pfunc_t pf;
+       extern char *progname;
+       int c;
+       bool corrupt = false;   /* Allow write of bad data w/ invalid CRC */
+       bool invalid_data = false; /* Allow write of bad data w/ valid CRC */
+       struct xfs_buf_ops local_ops;
+       const struct xfs_buf_ops *stashed_ops = NULL;
+
+       if (x.isreadonly & LIBXFS_ISREADONLY) {
+               dbprintf(_("%s started in read only mode, fuzzing disabled\n"),
+                       progname);
+               return 0;
+       }
+
+       if (cur_typ == NULL) {
+               dbprintf(_("no current type\n"));
+               return 0;
+       }
+
+       pf = cur_typ->pfunc;
+       if (pf == NULL) {
+               dbprintf(_("no handler function for type %s, fuzz unsupported.\n"),
+                        cur_typ->name);
+               return 0;
+       }
+
+       while ((c = getopt(argc, argv, "cd")) != EOF) {
+               switch (c) {
+               case 'c':
+                       corrupt = true;
+                       break;
+               case 'd':
+                       invalid_data = true;
+                       break;
+               default:
+                       dbprintf(_("bad option for fuzz command\n"));
+                       return 0;
+               }
+       }
+
+       if (corrupt && invalid_data) {
+               dbprintf(_("Cannot specify both -c and -d options\n"));
+               return 0;
+       }
+
+       if (invalid_data && iocur_top->typ->crc_off == TYP_F_NO_CRC_OFF &&
+                       !iocur_top->ino_buf && !iocur_top->dquot_buf) {
+               dbprintf(_("Cannot recalculate CRCs on this type of object\n"));
+               return 0;
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       /*
+        * If the buffer has no verifier or we are using standard verifier
+        * paths, then just fuzz it and return
+        */
+       if (!iocur_top->bp->b_ops ||
+           !(corrupt || invalid_data)) {
+               (*pf)(DB_FUZZ, cur_typ->fields, argc, argv);
+               return 0;
+       }
+
+
+       /* Temporarily remove write verifier to write bad data */
+       stashed_ops = iocur_top->bp->b_ops;
+       local_ops.verify_read = stashed_ops->verify_read;
+       iocur_top->bp->b_ops = &local_ops;
+
+       if (corrupt) {
+               local_ops.verify_write = xfs_dummy_verify;
+               dbprintf(_("Allowing fuzz of corrupted data and bad CRC\n"));
+       } else if (iocur_top->ino_buf) {
+               local_ops.verify_write = xfs_verify_recalc_inode_crc;
+               dbprintf(_("Allowing fuzz of corrupted inode with good CRC\n"));
+       } else if (iocur_top->dquot_buf) {
+               local_ops.verify_write = xfs_verify_recalc_dquot_crc;
+               dbprintf(_("Allowing fuzz of corrupted dquot with good CRC\n"));
+       } else if (iocur_top->typ->crc_off == TYP_F_CRC_FUNC) {
+               local_ops.verify_write = iocur_top->typ->set_crc;
+               dbprintf(_("Allowing fuzz of corrupted data with good CRC\n"));
+       } else { /* invalid data */
+               local_ops.verify_write = xfs_verify_recalc_crc;
+               dbprintf(_("Allowing fuzz of corrupted data with good CRC\n"));
+       }
+
+       (*pf)(DB_FUZZ, cur_typ->fields, argc, argv);
+
+       iocur_top->bp->b_ops = stashed_ops;
+
+       return 0;
+}
+
+/* Write zeroes to the field */
+static bool
+fuzz_zeroes(
+       void            *buf,
+       int             bitoff,
+       int             nbits)
+{
+       char            *out = buf;
+       int             bit;
+
+       if (bitoff % NBBY || nbits % NBBY) {
+               for (bit = 0; bit < nbits; bit++)
+                       setbit_l(out, bit + bitoff, 0);
+       } else
+               memset(out + byteize(bitoff), 0, byteize(nbits));
+       return true;
+}
+
+/* Write ones to the field */
+static bool
+fuzz_ones(
+       void            *buf,
+       int             bitoff,
+       int             nbits)
+{
+       char            *out = buf;
+       int             bit;
+
+       if (bitoff % NBBY || nbits % NBBY) {
+               for (bit = 0; bit < nbits; bit++)
+                       setbit_l(out, bit + bitoff, 1);
+       } else
+               memset(out + byteize(bitoff), 0xFF, byteize(nbits));
+       return true;
+}
+
+/* Flip the high bit in the (presumably big-endian) field */
+static bool
+fuzz_firstbit(
+       void            *buf,
+       int             bitoff,
+       int             nbits)
+{
+       setbit_l((char *)buf, bitoff, !getbit_l((char *)buf, bitoff));
+       return true;
+}
+
+/* Flip the low bit in the (presumably big-endian) field */
+static bool
+fuzz_lastbit(
+       void            *buf,
+       int             bitoff,
+       int             nbits)
+{
+       setbit_l((char *)buf, bitoff + nbits - 1,
+                       !getbit_l((char *)buf, bitoff));
+       return true;
+}
+
+/* Flip the middle bit in the (presumably big-endian) field */
+static bool
+fuzz_middlebit(
+       void            *buf,
+       int             bitoff,
+       int             nbits)
+{
+       setbit_l((char *)buf, bitoff + nbits / 2,
+                       !getbit_l((char *)buf, bitoff));
+       return true;
+}
+
+/* Format and shift a number into a buffer for setbitval. */
+static char *
+format_number(
+       uint64_t        val,
+       __be64          *out,
+       int             bit_length)
+{
+       int             offset;
+       char            *rbuf = (char *)out;
+
+       /*
+        * If the length of the field is not a multiple of a byte, push
+        * the bits up in the field, so the most signicant field bit is
+        * the most significant bit in the byte:
+        *
+        * before:
+        * val  |----|----|----|----|----|--MM|mmmm|llll|
+        * after
+        * val  |----|----|----|----|----|MMmm|mmll|ll00|
+        */
+       offset = bit_length % NBBY;
+       if (offset)
+               val <<= (NBBY - offset);
+
+       /*
+        * convert to big endian and copy into the array
+        * rbuf |----|----|----|----|----|MMmm|mmll|ll00|
+        */
+       *out = cpu_to_be64(val);
+
+       /*
+        * Align the array to point to the field in the array.
+        *  rbuf  = |MMmm|mmll|ll00|
+        */
+       offset = sizeof(__be64) - 1 - ((bit_length - 1) / sizeof(__be64));
+       return rbuf + offset;
+}
+
+/* Increase the value by some small prime number. */
+static bool
+fuzz_add(
+       void            *buf,
+       int             bitoff,
+       int             nbits)
+{
+       uint64_t        val;
+       __be64          out;
+       char            *b;
+
+       if (nbits > 64)
+               return false;
+
+       val = getbitval(buf, bitoff, nbits, BVUNSIGNED);
+       val += (nbits > 8 ? 2017 : 137);
+       b = format_number(val, &out, nbits);
+       setbitval(buf, bitoff, nbits, b);
+
+       return true;
+}
+
+/* Decrease the value by some small prime number. */
+static bool
+fuzz_sub(
+       void            *buf,
+       int             bitoff,
+       int             nbits)
+{
+       uint64_t        val;
+       __be64          out;
+       char            *b;
+
+       if (nbits > 64)
+               return false;
+
+       val = getbitval(buf, bitoff, nbits, BVUNSIGNED);
+       val -= (nbits > 8 ? 2017 : 137);
+       b = format_number(val, &out, nbits);
+       setbitval(buf, bitoff, nbits, b);
+
+       return true;
+}
+
+/* Randomize the field. */
+static bool
+fuzz_random(
+       void            *buf,
+       int             bitoff,
+       int             nbits)
+{
+       int             i, bytes;
+       char            *b, *rbuf;
+
+       bytes = byteize_up(nbits);
+       rbuf = b = malloc(bytes);
+       if (!b) {
+               perror("fuzz_random");
+               return false;
+       }
+
+       for (i = 0; i < bytes; i++)
+               *b++ = (char)lrand48();
+
+       setbitval(buf, bitoff, nbits, rbuf);
+       free(rbuf);
+
+       return true;
+}
+
+struct fuzzcmd {
+       const char      *verb;
+       bool            (*fn)(void *buf, int bitoff, int nbits);
+};
+
+/* Keep these verbs in sync with enum fuzzcmds. */
+static struct fuzzcmd fuzzverbs[] = {
+       {"zeroes",              fuzz_zeroes},
+       {"ones",                fuzz_ones},
+       {"firstbit",            fuzz_firstbit},
+       {"middlebit",           fuzz_middlebit},
+       {"lastbit",             fuzz_lastbit},
+       {"add",                 fuzz_add},
+       {"sub",                 fuzz_sub},
+       {"random",              fuzz_random},
+       {NULL,                  NULL},
+};
+
+/* ARGSUSED */
+void
+fuzz_struct(
+       const field_t   *fields,
+       int             argc,
+       char            **argv)
+{
+       const ftattr_t  *fa;
+       flist_t         *fl;
+       flist_t         *sfl;
+       int             bit_length;
+       struct fuzzcmd  *fc;
+       bool            success;
+       int             parentoffset;
+
+       if (argc != 2) {
+               dbprintf(_("Usage: fuzz fieldname fuzzcmd\n"));
+               dbprintf("Fuzz commands: %s", fuzzverbs->verb);
+               for (fc = fuzzverbs + 1; fc->verb != NULL; fc++)
+                       dbprintf(", %s", fc->verb);
+               dbprintf(".\n");
+               return;
+       }
+
+       fl = flist_scan(argv[0]);
+       if (!fl) {
+               dbprintf(_("unable to parse '%s'.\n"), argv[0]);
+               return;
+       }
+
+       /* Find our fuzz verb */
+       for (fc = fuzzverbs; fc->verb != NULL; fc++)
+               if (!strcmp(fc->verb, argv[1]))
+                       break;
+       if (fc->fn == NULL) {
+               dbprintf(_("Unknown fuzz command '%s'.\n"), argv[1]);
+               return;
+       }
+
+       /* if we're a root field type, go down 1 layer to get field list */
+       if (fields->name[0] == '\0') {
+               fa = &ftattrtab[fields->ftyp];
+               ASSERT(fa->ftyp == fields->ftyp);
+               fields = fa->subfld;
+       }
+
+       /* run down the field list and set offsets into the data */
+       if (!flist_parse(fields, fl, iocur_top->data, 0)) {
+               flist_free(fl);
+               dbprintf(_("parsing error\n"));
+               return;
+       }
+
+       sfl = fl;
+       parentoffset = 0;
+       while (sfl->child) {
+               parentoffset = sfl->offset;
+               sfl = sfl->child;
+       }
+
+       /*
+        * For structures, fsize * fcount tells us the size of the region we are
+        * modifying, which is usually a single structure member and is pointed
+        * to by the last child in the list.
+        *
+        * However, if the base structure is an array and we have a direct index
+        * into the array (e.g. write bno[5]) then we are returned a single
+        * flist object with the offset pointing directly at the location we
+        * need to modify. The length of the object we are modifying is then
+        * determined by the size of the individual array entry (fsize) and the
+        * indexes defined in the object, not the overall size of the array
+        * (which is what fcount returns).
+        */
+       bit_length = fsize(sfl->fld, iocur_top->data, parentoffset, 0);
+       if (sfl->fld->flags & FLD_ARRAY)
+               bit_length *= sfl->high - sfl->low + 1;
+       else
+               bit_length *= fcount(sfl->fld, iocur_top->data, parentoffset);
+
+       /* Fuzz the value */
+       success = fc->fn(iocur_top->data, sfl->offset, bit_length);
+       if (!success) {
+               dbprintf(_("unable to fuzz field '%s'\n"), argv[0]);
+               flist_free(fl);
+               return;
+       }
+
+       /* Write the fuzzed value back */
+       write_cur();
+
+       flist_print(fl);
+       print_flist(fl);
+       flist_free(fl);
+}
diff --git a/db/fuzz.h b/db/fuzz.h
new file mode 100644 (file)
index 0000000..c203eb5
--- /dev/null
+++ b/db/fuzz.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 Oracle.  All Rights Reserved.
+ *
+ * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it would be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write the Free Software Foundation,
+ * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+extern void    fuzz_init(void);
+extern void    fuzz_struct(const field_t *fields, int argc, char **argv);
index bf31e0419e0cfb4d0d4012e9e47b4fd6b279cce4..740adc01423eb1b1f40426db98e688c6c4ef1473 100644 (file)
--- a/db/type.c
+++ b/db/type.c
@@ -39,6 +39,7 @@
 #include "dir2.h"
 #include "text.h"
 #include "symlink.h"
+#include "fuzz.h"
 
 static const typ_t     *findtyp(char *name);
 static int             type_f(int argc, char **argv);
@@ -254,10 +255,17 @@ handle_struct(
        int           argc,
        char          **argv)
 {
-       if (action == DB_WRITE)
+       switch (action) {
+       case DB_FUZZ:
+               fuzz_struct(fields, argc, argv);
+               break;
+       case DB_WRITE:
                write_struct(fields, argc, argv);
-       else
+               break;
+       case DB_READ:
                print_struct(fields, argc, argv);
+               break;
+       }
 }
 
 void
@@ -267,10 +275,17 @@ handle_string(
        int           argc,
        char          **argv)
 {
-       if (action == DB_WRITE)
+       switch (action) {
+       case DB_WRITE:
                write_string(fields, argc, argv);
-       else
+               break;
+       case DB_READ:
                print_string(fields, argc, argv);
+               break;
+       case DB_FUZZ:
+               dbprintf(_("string fuzzing not supported.\n"));
+               break;
+       }
 }
 
 void
@@ -280,10 +295,17 @@ handle_block(
        int           argc,
        char          **argv)
 {
-       if (action == DB_WRITE)
+       switch (action) {
+       case DB_WRITE:
                write_block(fields, argc, argv);
-       else
+               break;
+       case DB_READ:
                print_block(fields, argc, argv);
+               break;
+       case DB_FUZZ:
+               dbprintf(_("use 'blocktrash' or 'write' to fuzz a block.\n"));
+               break;
+       }
 }
 
 void
@@ -293,6 +315,14 @@ handle_text(
        int           argc,
        char          **argv)
 {
-       if (action != DB_WRITE)
+       switch (action) {
+       case DB_FUZZ:
+               /* fall through */
+       case DB_WRITE:
+               dbprintf(_("text writing/fuzzing not supported.\n"));
+               break;
+       case DB_READ:
                print_text(fields, argc, argv);
+               break;
+       }
 }
index 85d89c74eedfa6b010aac6ffa0e8e0895a3e2c41..39719754e0c709fe11484f55076377126c5c1bc8 100644 (file)
--- a/db/type.h
+++ b/db/type.h
@@ -30,6 +30,7 @@ typedef enum typnm
        TYP_TEXT, TYP_FINOBT, TYP_NONE
 } typnm_t;
 
+#define DB_FUZZ  2
 #define DB_WRITE 1
 #define DB_READ  0
 
index 043985e629c19efd88424e33a66bf32addb2e0a7..37018a7a37a23aec2a233e8173a8cf8419407c14 100644 (file)
@@ -613,6 +613,55 @@ in units of 512-byte blocks, no matter what the filesystem's block size is.
 .BI "The optional " start " and " end " arguments can be used to constrain
 the output to a particular range of disk blocks.
 .TP
+.BI "fuzz [\-c] [\-d] " "field action"
+Write garbage into a specific structure field on disk.
+Expert mode must be enabled to use this command.
+The operation happens immediately; there is no buffering.
+.IP
+The fuzz command can take the following
+.IR action "s"
+against a field:
+.RS 1.0i
+.TP 0.4i
+.B zeroes
+Clears all bits in the field.
+.TP 0.4i
+.B ones
+Sets all bits in the field.
+.TP 0.4i
+.B firstbit
+Flips the first bit in the field.
+For a scalar value, this is the highest bit.
+.TP 0.4i
+.B middlebit
+Flips the middle bit in the field.
+.TP 0.4i
+.B lastbit
+Flips the last bit in the field.
+For a scalar value, this is the lowest bit.
+.TP 0.4i
+.B add
+Adds a small value to a scalar field.
+.TP 0.4i
+.B sub
+Subtracts a small value from a scalar field.
+.TP 0.4i
+.B random
+Randomizes the contents of the field.
+.RE
+.IP
+The following switches affect the write behavior:
+.RS 1.0i
+.TP 0.4i
+.B \-c
+Skip write verifiers and CRC recalculation; allows invalid data to be written
+to disk.
+.TP 0.4i
+.B \-d
+Skip write verifiers but perform CRC recalculation; allows invalid data to be
+written to disk to test detection of invalid data.
+.RE
+.TP
 .BI hash " string
 Prints the hash value of
 .I string