Howard McLauchlan | ef4154b | 2018-03-16 16:50:26 -0700 | [diff] [blame] | 1 | Some examples for inject |
| 2 | |
| 3 | inject guarantees the appropriate erroneous return of the specified injection |
| 4 | mode(kmalloc,bio,etc) given a call chain and an optional set of predicates. You |
| 5 | can also optionally print out the generated BPF program for |
| 6 | modification/debugging purposes. |
| 7 | |
| 8 | For example, suppose you want to fail kmalloc() from mount_subtree() when called |
| 9 | from btrfs_mount(): |
| 10 | |
| 11 | # ./inject.py kmalloc -I 'linux/mm.h' -I 'linux/fs.h' -v '(true)<- |
| 12 | mount_subtree(struct vfsmount *mnt, const char *name) (true) <- |
| 13 | btrfs_mount(struct file_system_type *fs_type, int flags, const char |
| 14 | *device_name, void *data)' |
| 15 | |
| 16 | The first argument indicates the mode(or what to fail). Appropriate headers |
| 17 | are specified. The verbosity flag prints the generated program. |
| 18 | |
| 19 | Note that btrfs_mount() has no accompanying predicate. In such cases the program |
| 20 | defaults to (true). |
| 21 | |
| 22 | Next, lets say we want to hit one of the BUG_ONs in fs/btrfs. As of 4.16-rc3, |
| 23 | there is a BUG_ON in btrfs_prepare_close_one_device() at fs/btrfs/volumes.c:1002 |
| 24 | |
| 25 | To hit this, we can use the following: |
| 26 | |
| 27 | # ./inject.py kmalloc -v -I 'linux/mm.h' '(true)<- btrfs_alloc_device(struct |
| 28 | btrfs_fs_info *fs_info, const u64 *ded, const u8 *uuid)(true)<- |
| 29 | btrfs_close_devices(struct btrfs_fs_devices *fs_devices)(true)' |
| 30 | |
| 31 | While the script was executing, I mounted and unmounted btrfs, causing a |
| 32 | segfault on umount(since that satisfied the call path indicated). A look at |
| 33 | dmesg will confirm we successfully hit that BUG_ON and caused a panic. |
| 34 | |
| 35 | In general, it's worth noting that the required specificity of the call chain is |
| 36 | dependent on how much granularity you need. The example above might have |
| 37 | performed as expected without the intermediate btrfs_alloc_device, but might |
| 38 | have also done something unexpected(an earlier kmalloc could have failed before |
| 39 | the one we were targetting). |
| 40 | |
| 41 | For hot paths, the approach outlined above isn't enough. If a path is traversed |
| 42 | very often, we can distinguish distinct calls with function arguments. Let's say |
| 43 | we want to fail the dentry allocation of a file creatively named 'bananas'. We |
| 44 | can do the following: |
| 45 | |
| 46 | # ./inject.py kmalloc -v -I 'linux/fs.h' '(true) <- d_alloc_parallel(struct |
| 47 | dentry *parent, const struct qstr *name, wait_queue_head_t *wq) |
| 48 | (STRCMP(name->name, 'bananas'))' |
| 49 | |
| 50 | While this script is executing, any operation that would cause a dentry |
| 51 | allocation where the name is 'bananas' fails, as expected. |
| 52 | |
| 53 | To note, STRCMP is a workaround for some rewriter issues. It will take input of |
| 54 | the form (x->...->z, 'literal'), and generate some equivalent code that the |
| 55 | verifier is more friendly about. It's not horribly robust, but works for the |
| 56 | purposes of making string comparisons a bit easier. |
| 57 | |
| 58 | Finally, we briefly demonstrate how to inject bio failures. The mechanism is |
| 59 | identical, so any information from above will apply. |
| 60 | |
| 61 | Let's say we want to fail bio requests when the request is to some specific |
| 62 | sector. An example use case would be to fail superblock writes in btrfs. For |
| 63 | btrfs, we know that there must be a superblock at 65536 bytes, or sector 128. |
| 64 | This allows us to run the following: |
| 65 | |
| 66 | # ./inject.py bio -v -I 'linux/blkdev.h' '(({struct gendisk *d = bio->bi_disk; |
| 67 | struct disk_part_tbl *tbl = d->part_tbl; struct hd_struct **parts = (void *)tbl + |
| 68 | sizeof(struct disk_part_tbl); struct hd_struct **partp = parts + bio->bi_partno; |
| 69 | struct hd_struct *p = *partp; dev_t disk = p->__dev.devt; disk == |
| 70 | MKDEV(254,16);}) && bio->bi_iter.bi_sector == 128)' |
| 71 | |
| 72 | The predicate in the command above has two parts. The first is a compound |
| 73 | statement which shortens to "only if the system is btrfs", but is long due |
| 74 | to rewriter/verifier shenanigans. The major/minor information can be found |
| 75 | however; I used Python. The second part simply checks the starting |
| 76 | address of bi_iter. While executing, this script effectively fails superblock |
| 77 | writes to the superblock at sector 128 without affecting other filesystems. |
| 78 | |
| 79 | As an extension to the above, one could easily fail all btrfs superblock writes |
| 80 | (we only fail the primary) by calculating the sector number of the mirrors and |
| 81 | amending the predicate accordingly. |