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 }