1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2013 Damian Bogel. All rights reserved.
14 */
15
16 /*
17 * Filesystem disturber pseudo-device driver.
18 */
19
20 #include <sys/conf.h>
21 #include <sys/ddi.h>
22 #include <sys/file.h>
23 #include <sys/fsd.h>
24 #include <sys/fsh.h>
25 #include <sys/kmem.h>
26 #include <sys/ksynch.h>
27 #include <sys/list.h>
28 #include <sys/mkdev.h>
29 #include <sys/stat.h>
30 #include <sys/sunddi.h>
31 #include <sys/sysmacros.h>
32 #include <sys/types.h>
33
34 /*
35 * fsd - filesystem disturber
36 *
37 * 1. Abstract
38 * Filesystem disturber is a pseudo-device driver used to inject pathological
39 * behaviour into vfs calls. It is NOT a fuzzer. That kind of behaviour
40 * should be expected and correctly handled by software. A simple example of
41 * such behaviour is read() reading less bytes than it was requested. It's
42 * well documented and every read() caller should check the return value of
43 * this function before proceeding.
44 *
45 * 2. Features
46 * * per-vfs injections
47 * * injection installing on every newly mounted vfs
48 *
49 * 3. Usage
50 * A library (libfsd) is provided to drive fsd.
51 *
52 * 4. Internals
53 * fsd_enable() does the necessary fsh callbacks installing.
54 * These callbacks are used for both installing injections on newly mounted
55 * vfs' and cleaning up when a vfs is destroyed.
56 *
57 * The list of currently installed hooks is kept in fsd_slist.
58 *
59 * 5. Locking
60 * The locking is fairly simple. Every modification of fsd_enable, fsd_hooks,
61 * fsd_new_stat and fsd_slist is protected by fsd_lock. Hooks use only the
62 * elements of fsd_slist, nothing else. Before an element of fsd_slist
63 * is destroyed, a hook which uses it is removed.
64 *
65 * Every fsd_slist element is protected by it's own lock.
66 */
67
68 static dev_info_t *fsd_devi;
69
70 /* procets: fsd_enabled, fsd_hooks, fsd_new_stat, fsd_slist */
71 static kmutex_t fsd_lock;
72
73 /* fsd_stat for new filesystems. Don't do anything if NULL. */
74 static fsd_stat_t *fsd_new_stat;
75 static int fsd_enabled;
76 static fsh_t fsd_hooks;
77
78 /*
79 * The fsd_slist exist for two purposes.
80 * 1. It's an argument for hooks.
81 * 2. For every vfs on which fsd installed a set of hooks there exist exactly
82 * one entry in fsd_slist.
83 */
84 static list_t fsd_slist;
85
86 /* No need for lock here. It's unchanged between fsd_attach()/fsd_detach(). */
87 static fsh_callback_t fsd_callbacks;
88
89 /*
90 * Although it's safe to use this kind of pseudo-random number generator,
91 * it behaves very regular when it comes to parity. Every fsd_rand() call
92 * changes the parity of the seed. That's why when a range of width 2 is set
93 * as a parameter, it's highly possible that the random value will always be
94 * the same, because fsd_rand() could be called the same number of times in a
95 * hook.
96 */
97 static long fsd_rand_seed;
98
99 static int
100 fsd_rand()
101 {
102 fsd_rand_seed = fsd_rand_seed * 1103515245L + 12345;
103 return (fsd_rand_seed & 0x7ffffffff);
104 }
105
106 /* vnode hooks */
107 /*
108 * A pointer to a given fsd_stat_int_t is valid always inside fsh_hook_xxx()
109 * call, because it's valid until the hooks associated with it are removed.
110 * If a hook is removed, it cannot be executing.
111 */
112 static int
113 fsd_hook_read(fsh_int_t *fshi, void *arg, vnode_t *vp, uio_t *uiop,
114 int ioflag, cred_t *cr, caller_context_t *ct)
115 {
116 fsd_stat_int_t *si;
117 uint64_t cnt;
118 uint64_t less;
119 uint64_t less_chance;
120
121 /*
122 * It is used to keep an odd number of fsd_rand() calls in every
123 * fsd_hook_read() call. That is desired because when a range of width
124 * 2 is set as a parameter, we don't want to make it a constant.
125 * The pseudo-random number generator returns a number with different
126 * parity with every call. If this function is called in every
127 * fsd_hook_read() execution even number of times, it would always be
128 * the same % 2.
129 */
130 (void) fsd_rand();
131
132 si = (fsd_stat_int_t *)arg;
133 ASSERT(vp->v_vfsp == si->fsdsi_vfsp);
134
135 rw_enter(&si->fsdsi_lock, RW_READER);
136 less_chance = si->fsdsi_stat.fsds_read_less_chance;
137 less = (uint64_t)fsd_rand() %
138 (si->fsdsi_stat.fsds_read_less_r[1] + 1 -
139 si->fsdsi_stat.fsds_read_less_r[0]) +
140 si->fsdsi_stat.fsds_read_less_r[0];
141
142 rw_exit(&si->fsdsi_lock);
143
144 cnt = uiop->uio_iov->iov_len;
145 if ((uint64_t)fsd_rand() % 100 < less_chance) {
146 extern size_t copyout_max_cached;
147 int ret;
148
149 if (cnt > less)
150 cnt -= less;
151 else
152 less = 0;
153
154 uiop->uio_iov->iov_len = cnt;
155 uiop->uio_resid = cnt;
156 if (cnt <= copyout_max_cached)
157 uiop->uio_extflg = UIO_COPY_CACHED;
158 else
159 uiop->uio_extflg = UIO_COPY_DEFAULT;
160
161 ret = fsh_next_read(fshi, vp, uiop, ioflag, cr, ct);
162 uiop->uio_resid += less;
163 return (ret);
164 }
165
166 return (fsh_next_read(fshi, vp, uiop, ioflag, cr, ct));
167 }
168
169 /*
170 * Finds and (optionally) locks a fsd_stat_int_t structure of a given vfs.
171 */
172 static int
173 fsd_get_stati(vfs_t *vfsp, fsd_stat_int_t **si, int lock, int rwmode)
174 {
175 fsd_stat_int_t *stati;
176
177 ASSERT(MUTEX_HELD(&fsd_lock));
178
179 for (stati = list_head(&fsd_slist); stati;
180 stati = list_next(&fsd_slist, stati))
181 if (stati->fsdsi_vfsp == vfsp) {
182 if (lock)
183 rw_enter(&stati->fsdsi_lock, rwmode);
184 *si = stati;
185 return (0);
186 }
187 return (FSD_ENTRY_NOT_FOUND);
188
189 }
190
191 static void
192 fsd_install_disturber(vfs_t *vfsp, fsd_stat_t *stat)
193 {
194 fsd_stat_int_t *si;
195
196 ASSERT(MUTEX_HELD(&fsd_lock));
197
198 if (fsd_get_stati(vfsp, &si, 1, RW_WRITER) == FSD_ENTRY_NOT_FOUND) {
199 si = kmem_alloc(sizeof (*si), KM_SLEEP);
200 si->fsdsi_vfsp = vfsp;
201 (void) memcpy(&si->fsdsi_stat, stat, sizeof (si->fsdsi_stat));
202 rw_init(&si->fsdsi_lock, NULL, RW_DRIVER, NULL);
203 list_insert_head(&fsd_slist, si);
204
205 fsd_hooks.arg = si;
206 fsh_hook_install(vfsp, &fsd_hooks);
207 } else {
208 (void) memcpy(&si->fsdsi_stat, stat, sizeof (si->fsdsi_stat));
209 rw_exit(&si->fsdsi_lock);
210 }
211 }
212
213 static int
214 fsd_remove_disturber(vfs_t *vfsp)
215 {
216 fsd_stat_int_t *si;
217 int error;
218
219 ASSERT(MUTEX_HELD(&fsd_lock));
220
221 error = fsd_get_stati(vfsp, &si, 0, 0);
222 if (error == FSD_ENTRY_NOT_FOUND)
223 return (error);
224
225 list_remove(&fsd_slist, si);
226 fsd_hooks.arg = si;
227 fsh_hook_remove(vfsp, &fsd_hooks);
228
229 rw_destroy(&si->fsdsi_lock);
230 kmem_free(si, sizeof (*si));
231
232 return (0);
233 }
234
235 static void
236 fsd_callback_mount(vfs_t *vfsp, void *arg)
237 {
238 _NOTE(ARGUNUSED(arg));
239
240 mutex_enter(&fsd_lock);
241 if (fsd_new_stat != NULL)
242 fsd_install_disturber(vfsp, fsd_new_stat);
243 mutex_exit(&fsd_lock);
244 }
245
246 static void
247 fsd_callback_free(vfs_t *vfsp, void *arg)
248 {
249 _NOTE(ARGUNUSED(arg));
250
251 mutex_enter(&fsd_lock);
252 (void) fsd_remove_disturber(vfsp);
253 mutex_exit(&fsd_lock);
254 }
255
256 static void
257 fsd_enable()
258 {
259 mutex_enter(&fsd_lock);
260 if (!fsd_enabled)
261 fsh_callback_install(&fsd_callbacks);
262 fsd_enabled = 1;
263 mutex_exit(&fsd_lock);
264 }
265
266 static void
267 fsd_disable()
268 {
269 fsd_stat_int_t *si;
270
271 mutex_enter(&fsd_lock);
272 if (fsd_enabled) {
273 fsd_enabled = 0;
274
275 fsh_callback_remove(&fsd_callbacks);
276
277 /* Removing all hooks and fsd_stat_int_t entries. */
278 while (!list_is_empty(&fsd_slist)) {
279 si = list_remove_head(&fsd_slist);
280
281 /*
282 * After a fsh_hook_remove, we don't have to worry
283 * about the fsd_stat_int_t. If we hold the fsd_lock,
284 * nobody would try to use it. It's safe to destroy it.
285 */
286 fsd_hooks.arg = si;
287 fsh_hook_remove(si->fsdsi_vfsp, &fsd_hooks);
288
289 rw_destroy(&si->fsdsi_lock);
290 kmem_free(si, sizeof (*si));
291 }
292
293 if (fsd_new_stat != NULL) {
294 kmem_free(fsd_new_stat, sizeof (*fsd_new_stat));
295 fsd_new_stat = NULL;
296 }
297 }
298 mutex_exit(&fsd_lock);
299 }
300
301 static int
302 fsd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
303 {
304 minor_t instance;
305
306 if (cmd != DDI_ATTACH)
307 return (DDI_FAILURE);
308
309 if (fsd_devi != NULL)
310 return (DDI_FAILURE);
311
312 instance = ddi_get_instance(dip);
313 if (ddi_create_minor_node(dip, "fsd", S_IFCHR, instance,
314 DDI_PSEUDO, 0) == DDI_FAILURE)
315 return (DDI_FAILURE);
316 fsd_devi = dip;
317 ddi_report_dev(fsd_devi);
318
319 /* Setting hooks */
320 fsd_hooks.hook_read = fsd_hook_read;
321
322 list_create(&fsd_slist, sizeof (fsd_stat_int_t),
323 offsetof(fsd_stat_int_t, fsdsi_next));
324
325 fsd_rand_seed = gethrtime();
326
327 /* Setting callbacks */
328 fsd_callbacks.fshc_mount = fsd_callback_mount;
329 fsd_callbacks.fshc_free = fsd_callback_free;
330
331 mutex_init(&fsd_lock, NULL, MUTEX_DRIVER, NULL);
332
333 return (DDI_SUCCESS);
334 }
335
336 /*
337 * If fsd_enable() was called and there was no subsequent fsd_disable() call,
338 * detach will fail.
339 */
340 static int
341 fsd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
342 {
343 if (cmd != DDI_DETACH)
344 return (DDI_FAILURE);
345
346 ASSERT(dip == fsd_devi);
347
348 /* No need to hold fsd_lock here */
349 if (fsd_enabled)
350 return (DDI_FAILURE);
351
352 ddi_remove_minor_node(dip, NULL);
353 fsd_devi = NULL;
354
355 list_destroy(&fsd_slist);
356 mutex_destroy(&fsd_lock);
357
358 return (DDI_SUCCESS);
359 }
360
361 static int
362 fsd_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **resultp)
363 {
364 _NOTE(ARGUNUSED(dip));
365
366 switch (infocmd) {
367 case DDI_INFO_DEVT2DEVINFO:
368 *resultp = fsd_devi;
369 return (DDI_SUCCESS);
370 case DDI_INFO_DEVT2INSTANCE:
371 *resultp = (void *)(uintptr_t)getminor((dev_t)arg);
372 return (DDI_SUCCESS);
373 default:
374 return (DDI_FAILURE);
375 }
376 }
377
378 /* TODO: what policy should be checked here? */
379 static int
380 fsd_open(dev_t *devp, int oflag, int sflag, cred_t *cred_p)
381 {
382 _NOTE(ARGUNUSED(devp));
383 _NOTE(ARGUNUSED(oflag));
384 _NOTE(ARGUNUSED(sflag));
385 _NOTE(ARGUNUSED(cred_p));
386
387 return (0);
388 }
389
390 static int
391 fsd_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
392 {
393 _NOTE(ARGUNUSED(dev));
394 _NOTE(ARGUNUSED(flag));
395 _NOTE(ARGUNUSED(otyp));
396 _NOTE(ARGUNUSED(cred_p));
397
398 return (0);
399 }
400
401 static int
402 fsd_check_stat(fsd_stat_t *stat)
403 {
404 if (stat->fsds_read_less_chance > 100 ||
405 stat->fsds_read_less_r[0] > stat->fsds_read_less_r[1])
406 return (FSD_BAD_STAT);
407 return (0);
408 }
409
410 static int
411 fsd_ioctl_disturb(void *arg, int mode, int *rvalp)
412 {
413 file_t *file;
414 fsd_ioc_stat_t iocs;
415 int rv;
416
417 if (ddi_copyin(arg, &iocs, sizeof (iocs), mode))
418 return (EFAULT);
419
420 if ((rv = fsd_check_stat(&iocs.fsdis_stat))) {
421 *rvalp = rv;
422 return (0);
423 }
424
425 if ((file = getf((int)iocs.fsdis_mnt)) == NULL) {
426 *rvalp = FSD_BAD_FD;
427 return (0);
428 }
429
430 mutex_enter(&fsd_lock);
431 fsd_install_disturber(file->f_vnode->v_vfsp, &iocs.fsdis_stat);
432 mutex_exit(&fsd_lock);
433
434 releasef((int)iocs.fsdis_mnt);
435
436 *rvalp = 0;
437 return (0);
438 }
439
440 static int
441 fsd_ioctl_get_status(void *arg, int mode, int *rvalp)
442 {
443 file_t *file;
444 fsd_stat_int_t *si;
445 int error;
446 int64_t fd;
447
448 if (ddi_copyin(arg, &fd, sizeof (fd), mode))
449 return (EFAULT);
450
451 if ((file = getf((int)fd)) == NULL) {
452 *rvalp = FSD_BAD_FD;
453 return (0);
454 }
455
456 mutex_enter(&fsd_lock);
457 error = fsd_get_stati(file->f_vnode->v_vfsp, &si, 1, RW_READER);
458 mutex_exit(&fsd_lock);
459
460 releasef((int)fd);
461
462 if (error == FSD_ENTRY_NOT_FOUND) {
463 *rvalp = FSD_ENTRY_NOT_FOUND;
464 return (0);
465 }
466
467 error = ddi_copyout(&si->fsdsi_stat, (void *)arg,
468 sizeof (si->fsdsi_stat), mode);
469 rw_exit(&si->fsdsi_lock);
470 if (error)
471 return (EFAULT);
472
473 *rvalp = 0;
474 return (0);
475 }
476
477 static int
478 fsd_ioctl_nodisturb(void *arg, int mode, int *rvalp)
479 {
480 file_t *file;
481 int64_t fd;
482
483 if (ddi_copyin((void *)arg, &fd, sizeof (fd), mode))
484 return (EFAULT);
485
486 if ((file = getf((int)fd)) == NULL) {
487 *rvalp = FSD_BAD_FD;
488 return (0);
489 }
490
491 mutex_enter(&fsd_lock);
492 *rvalp = fsd_remove_disturber(file->f_vnode->v_vfsp);
493 releasef((int)fd);
494 mutex_exit(&fsd_lock);
495
496 return (0);
497 }
498
499 static int
500 fsd_ioctl_disturb_new(void *arg, int mode, int *rvalp)
501 {
502 fsd_stat_t stat;
503 int rv;
504
505 if (ddi_copyin(arg, &stat, sizeof (stat), mode))
506 return (EFAULT);
507
508 if ((rv = fsd_check_stat(&stat))) {
509 *rvalp = rv;
510 return (0);
511 }
512
513 mutex_enter(&fsd_lock);
514 if (fsd_new_stat == NULL)
515 fsd_new_stat = (fsd_stat_t *)kmem_alloc(sizeof (*fsd_new_stat),
516 KM_SLEEP);
517 (void) memcpy(fsd_new_stat, &stat, sizeof (*fsd_new_stat));
518 mutex_exit(&fsd_lock);
519
520 *rvalp = 0;
521 return (0);
522 }
523
524
525 static int
526 fsd_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
527 int *rvalp)
528 {
529 _NOTE(ARGUNUSED(dev));
530 _NOTE(ARGUNUSED(credp));
531
532 if (!fsd_enabled && cmd != FSD_ENABLE) {
533 *rvalp = FSD_NOT_ENABLED;
534 return (0);
535 }
536
537 switch (cmd) {
538 case FSD_ENABLE:
539 fsd_enable();
540 *rvalp = 0;
541 return (0);
542
543 case FSD_DISABLE:
544 fsd_disable();
545 *rvalp = 0;
546 return (0);
547
548 case FSD_GET_STATUS:
549 return (fsd_ioctl_get_status((void *)arg, mode, rvalp));
550
551 case FSD_DISTURB:
552 return (fsd_ioctl_disturb((void *)arg, mode, rvalp));
553
554 case FSD_NODISTURB:
555 return (fsd_ioctl_nodisturb((void *)arg, mode, rvalp));
556
557 case FSD_DISTURB_NEW:
558 return (fsd_ioctl_disturb_new((void *)arg, mode, rvalp));
559
560 case FSD_NODISTURB_NEW:
561 mutex_enter(&fsd_lock);
562 if (fsd_new_stat != NULL)
563 kmem_free(fsd_new_stat, sizeof (*fsd_new_stat));
564 mutex_exit(&fsd_lock);
565
566 *rvalp = 0;
567 return (0);
568
569 default:
570 return (EINVAL);
571 }
572 }
573
574 static struct cb_ops cb_ops = {
575 fsd_open, /* open(9E) */
576 fsd_close, /* close(9E) */
577 nodev, /* strategy(9E) */
578 nodev, /* print(9E) */
579 nodev, /* dump(9E) */
580 nodev, /* read(9E) */
581 nodev, /* write(9E) */
582 fsd_ioctl, /* ioctl(9E) */
583 nodev, /* devmap(9E) */
584 nodev, /* mmap(9E) */
585 nodev, /* segmap(9E) */
586 nochpoll, /* chpoll(9E) */
587 ddi_prop_op, /* prop_op(9E) */
588 NULL, /* streamtab(9E) */
589 D_MP | D_64BIT, /* cb_flag(9E) */
590 CB_REV, /* cb_rev(9E) */
591 nodev, /* aread(9E) */
592 nodev, /* awrite(9E) */
593 };
594
595 static struct dev_ops dev_ops = {
596 DEVO_REV, /* driver build version */
597 0, /* reference count */
598 fsd_getinfo, /* getinfo */
599 nulldev,
600 nulldev, /* probe */
601 fsd_attach, /* attach */
602 fsd_detach, /* detach */
603 nodev,
604 &cb_ops, /* cb_ops */
605 NULL, /* bus_ops */
606 NULL, /* power */
607 ddi_quiesce_not_needed, /* quiesce */
608 };
609
610 static struct modldrv modldrv = {
611 &mod_driverops, "Filesystem disturber", &dev_ops
612 };
613
614 static struct modlinkage modlinkage = {
615 MODREV_1, &modldrv, NULL
616 };
617
618 int
619 _init(void)
620 {
621 return (mod_install(&modlinkage));
622 }
623
624 int
625 _info(struct modinfo *modinfop)
626 {
627 return (mod_info(&modlinkage, modinfop));
628 }
629
630 int
631 _fini(void)
632 {
633 return (mod_remove(&modlinkage));
634 }