DF-5007 / hangsim.c
/* * DF-5007 reproduction module. * * Reuses the STOCK sys/dev/crypto/tpm/tpm20.c (verbatim #include below) so * that tpm20_read / tpm20_write / tpm20_discard_buffer / the discard-callout * arming+stopping / the dev_lock + buf_cv protocol are the real shipped code. * Only the TPM MMIO transport is stubbed: ok_transmit() mimics the successful * epilogue of tpmcrb_transmit() (tpm_crb.c:384-402): write a canned response * header + body into sc->buf and set sc->pending_data_length. * * Bug under test (tpm20.c:78-83): * tpm20_read() calls callout_stop(&sc->discard_buffer_callout) BEFORE * validating sc->owner_tid. A read() that FAILS the ownership check with * EPERM still permanently disarms the only automatic recovery (0.5 s * discard timer) that clears pending_data_length. Every later write(2) * then blocks forever in the non-interruptible cv_wait loop at * tpm20.c:126-127 (DF cv_wait has no PCATCH, kern_condvar.c:23-25,41). */ #include <sys/module.h> #include <sys/sysctl.h> #include <sys/sysproto.h> /* SYSCTL_HANDLER_ARGS glue on DF */ #include "tpm20.c" /* STOCK, verbatim */ static struct tpm_sc *gsc; static int ok_transmit(struct tpm_sc *sc, size_t length) { /* canned TPM2 GetCapability-style success response: * tag 0x8001, size 0x0000000c, rc 0, then payload padding */ uint8_t resp[32]; memset(resp, 0, sizeof(resp)); resp[0] = 0x80; resp[1] = 0x01; resp[2] = 0x00; resp[3] = 0x00; resp[4] = 0x00; resp[5] = 0x0c; memcpy(sc->buf, resp, sizeof(resp)); /* tpm_crb.c:402 epilogue */ sc->pending_data_length = length; kprintf("hangsim: transmit(len=%zu) ok -> pending=%zu\n", length, sc->pending_data_length); return (0); } static int hangsim_modevent(module_t mod, int type, void *arg) { struct tpm_sc *sc; switch (type) { case MOD_LOAD: gsc = kmalloc(sizeof(struct tpm_sc), M_DEVBUF, M_WAITOK | M_ZERO); sc = gsc; /* verbatim tpm20_init (tpm20.c:190-207) minus device_get_unit. * sc->dev must be a valid device_t: the stock discard callout * ends with device_printf(sc->dev, ...) (tpm20.c:164); the * real backends set it at attach. Borrow the root bus. */ sc->dev = root_bus; sc->buf = kmalloc(TPM_BUFSIZE, M_TPM20, M_WAITOK); lockinit(&sc->dev_lock, "TPM driver lock", 0, LK_CANRECURSE); cv_init(&sc->buf_cv, "TPM buffer cv"); callout_init_mp(&sc->discard_buffer_callout); sc->pending_data_length = 0; sc->sc_cdev = make_dev(&tpm20_ops, 0, UID_ROOT, GID_WHEEL, 0600, "hangsim0"); sc->sc_cdev->si_drv1 = sc; sc->transmit = ok_transmit; kprintf("hangsim: loaded; /dev/hangsim0 is stock tpm20.c\n"); return (0); case MOD_UNLOAD: return (EBUSY); /* never unload: victim threads may be stuck */ } return (0); } DEV_MODULE(hangsim, hangsim_modevent, NULL); |