From nobody Sun Oct 11 01:07:15 2026 X-Original-To: dev-commits-src-branches@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4j2Mrg2k35z6w8HZ for ; Sun, 11 Oct 2026 01:07:15 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR2" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4j2Mrg1dMnz3Hkf for ; Sun, 11 Oct 2026 01:07:15 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1791680835; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=h3amUTpVjxpZoWo+Pvl/j1tfiDrwu1yguaV3OtiTHgE=; b=Y5pw7hK4GkoOvkgACWNb8rzNqzHqutw8UwgmAQpQexrFwbzpVWox9qclnM2WS9pAXlgD0+ 16/wAxqDNaMOV9+xK5A0fR2eUw7D8FzGfqi/vv5VauhTaCciu9ZSplYfylwDS7mCeK5CkP w0xen4Dv+7+xahZW7xU0IddhMRdQP8fcTzMYy4ct9AXe9imzsU6EiZlOjeoZ0c3UJUD11u 5YAbCs1H4mgEGH2Nmy5hQ9OgiT6vPT9l7EfsvpontfO7PuhvdHZ6bLt8Dscf3kiO3j4Qwt iLJ5Cs3jaKr19qxYRrjH7QKyE9JN/qNL9XgaYw8YUBSoKs/owxpKRxwhJ8n8Fg== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1791680835; b=HKqqvB4mB+vEQQvJezP6tfCDNpSbiQLxUIQ2pfg3oPfXQkvRI06UGKcDxCeV7WCGcH7J1W tv0TBMPhNBXVDUHxg5t4pIDh1gwYNA890yqsPNTaxPyZH+/TCQqWbVWTsbnZJIpEDHYa4y TzLjD3mEaUwdHX23y3WRVE1kVRWU+H0ilWJQ4XzhtI3kky9Z0fxd+NjLrf9KDK0BRxpyAW 7mNRZ4eso0d+OnEjyMjD3iQ1sSYMTYYUYOMlV8TkUq6d/Yj5arhEdCazWquLCoZV3tJQv2 GIFqeGBsQ3fqqE+U2P25rBrufdEvgTaGNHu2iFxijpza3RvAzBKV0utmLDsFwQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1791680835; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=h3amUTpVjxpZoWo+Pvl/j1tfiDrwu1yguaV3OtiTHgE=; b=rJYWXhwJqGVnyH0+YFujxLpDau1wy5kWZu33+z72DvYA/fk4Ehrl4Ysi0OYRMKUI9ekHIK nh/9JgIQwDff4ncOiau8sRnQ8HLir6q058XLF4uxj+zYeSlJERA9bLUWXFXYNx5DoMRNFZ WXRdxy4Q6deJzWhaxWJEF8nQ5cG5fXcBx+D2QdPoDwUVH2M3X5Te0bu8M4Jcexgn6Jax/l DCRHVj6GDFVegxEkgZaqBbpcw0JSZTECljuypzMzcTopJ+A5oWSyrHWbSOl33PR38R+9qV mbIh0xJeobWo2Yyv3iF9jb4IikpiXXQZE8LQatMxF+AuyTAtuM28FdznkvQtnQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4j2Mrg0hGczJg7 for ; Sun, 11 Oct 2026 01:07:15 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 311ff by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sun, 11 Oct 2026 01:07:15 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Kevin Bowling Subject: git: 0a843d2b14c9 - stable/15 - tpm20: Harden the common device lifecycle List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@FreeBSD.org List-Id: Commits to the stable branches of the FreeBSD src repository List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kbowling X-Git-Repository: src X-Git-Refname: refs/heads/stable/15 X-Git-Reftype: branch X-Git-Commit: 0a843d2b14c9cd32e850d18bb7c133adc4564d44 Auto-Submitted: auto-generated Date: Sun, 11 Oct 2026 01:07:15 +0000 Message-Id: <6acae143.311ff.10f6775@gitrepo.freebsd.org> The branch stable/15 has been updated by kbowling: URL: https://cgit.FreeBSD.org/src/commit/?id=0a843d2b14c9cd32e850d18bb7c133adc4564d44 commit 0a843d2b14c9cd32e850d18bb7c133adc4564d44 Author: Kevin Bowling AuthorDate: 2026-08-27 12:42:28 +0000 Commit: Kevin Bowling CommitDate: 2026-10-11 01:07:04 +0000 tpm20: Harden the common device lifecycle Mark the device as dying before teardown and destroy the character device before freeing its private state or lock. This prevents cdev methods from entering with a freed internal buffer or a destroyed sx. Check the teardown state in command paths, honor failures from the cdev private data interface, and publish teardown before waiting for the lifecycle lock. Keep that lock across TPM retry delays so commands cannot interpose and private state remains pinned, but abort before the next retry once teardown begins. Block new cdev operations after a successful Shutdown(STATE). Keep the suspend gate and the TPM command under the same lock so a userspace command cannot invalidate the saved state before S3 entry. Clear the gate only after Startup(STATE) succeeds. Keep entropy harvesting scheduled after a transient command or suspend failure, but stop it while suspended or once teardown begins. Queue the next timeout while holding the lifecycle lock so release cannot miss a concurrent requeue. Validated on two TPM 2.0 FIFO systems. Each completed five device suspend/resume cycles, rejected both new and already-open cdev operations with EBUSY while suspended, completed 200 concurrent PCR reads, and detached cleanly while four PCR readers were active. A ThinkPad P51 also completed a full S3 cycle with PCR 0 unchanged and 50 successful reads after resume. Reviewed by: kevans Sponsored by: BBOX.io Differential Revision: https://reviews.freebsd.org/D59240 (cherry picked from commit a3daa1e8f5988045e8042fb3fe376384a43b85ee) --- sys/dev/tpm/tpm20.c | 168 ++++++++++++++++++++++++++++++++++++++-------------- sys/dev/tpm/tpm20.h | 2 + 2 files changed, 125 insertions(+), 45 deletions(-) diff --git a/sys/dev/tpm/tpm20.c b/sys/dev/tpm/tpm20.c index 20521b2ed361..bebf0af400fa 100644 --- a/sys/dev/tpm/tpm20.c +++ b/sys/dev/tpm/tpm20.c @@ -28,6 +28,8 @@ #include #include +#include + #include "tpm20.h" #define TPM_HARVEST_SIZE 16 @@ -84,12 +86,22 @@ tpm20_read(struct cdev *dev, struct uio *uio, int flags) struct tpm_priv *priv; size_t bytes_to_transfer; size_t offset; - int result = 0; + int result; sc = (struct tpm_sc *)dev->si_drv1; - devfs_get_cdevpriv((void **)&priv); + result = devfs_get_cdevpriv((void **)&priv); + if (result != 0) + return (result); sx_xlock(&sc->dev_lock); + if (atomic_load_bool(&sc->dying)) { + result = ENXIO; + goto out; + } + if (sc->suspended) { + result = EBUSY; + goto out; + } offset = priv->offset; bytes_to_transfer = MIN(priv->len, uio->uio_resid); if (bytes_to_transfer > 0) { @@ -100,8 +112,8 @@ tpm20_read(struct cdev *dev, struct uio *uio, int flags) result = 0; } +out: sx_xunlock(&sc->dev_lock); - return (result); } @@ -111,10 +123,12 @@ tpm20_write(struct cdev *dev, struct uio *uio, int flags) struct tpm_sc *sc; struct tpm_priv *priv; size_t byte_count; - int result = 0; + int result; sc = (struct tpm_sc *)dev->si_drv1; - devfs_get_cdevpriv((void **)&priv); + result = devfs_get_cdevpriv((void **)&priv); + if (result != 0) + return (result); byte_count = uio->uio_resid; if (byte_count < TPM_HEADER_SIZE) { @@ -130,15 +144,22 @@ tpm20_write(struct cdev *dev, struct uio *uio, int flags) } sx_xlock(&sc->dev_lock); + if (atomic_load_bool(&sc->dying)) { + result = ENXIO; + goto out; + } + if (sc->suspended) { + result = EBUSY; + goto out; + } result = uiomove(priv->buf, byte_count, uio); - if (result != 0) { - sx_xunlock(&sc->dev_lock); - return (result); - } + if (result != 0) + goto out; result = TPM_TRANSMIT(sc->dev, priv, byte_count); +out: sx_xunlock(&sc->dev_lock); return (result); } @@ -163,12 +184,28 @@ tpm20_priv_dtor(void *data) int tpm20_open(struct cdev *dev, int flag, int mode, struct thread *td) { + struct tpm_sc *sc; struct tpm_priv *priv; + int error; + sc = (struct tpm_sc *)dev->si_drv1; + sx_xlock(&sc->dev_lock); + if (atomic_load_bool(&sc->dying)) { + error = ENXIO; + goto out; + } + if (sc->suspended) { + error = EBUSY; + goto out; + } priv = tpm20_priv_alloc(); - devfs_set_cdevpriv(priv, tpm20_priv_dtor); + error = devfs_set_cdevpriv(priv, tpm20_priv_dtor); + if (error != 0) + tpm20_priv_dtor(priv); - return (0); +out: + sx_xunlock(&sc->dev_lock); + return (error); } int @@ -199,6 +236,8 @@ tpm20_init(struct tpm_sc *sc) struct make_dev_args args; int result; + atomic_store_bool(&sc->dying, false); + sc->suspended = false; sc->internal_priv = tpm20_priv_alloc(); make_dev_args_init(&args); @@ -227,11 +266,20 @@ void tpm20_release(struct tpm_sc *sc) { + /* Publish teardown so a retrying command stops using the device. */ + atomic_store_bool(&sc->dying, true); + sx_xlock(&sc->dev_lock); + sx_xunlock(&sc->dev_lock); + + /* Stop and drain character-device methods before freeing their state. */ + if (sc->sc_cdev != NULL) { + destroy_dev(sc->sc_cdev); + sc->sc_cdev = NULL; + } if (!sc->common_initialized) goto out; #if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM - if (device_is_attached(sc->dev)) - taskqueue_drain_timeout(taskqueue_thread, &sc->harvest_task); + taskqueue_drain_timeout(taskqueue_thread, &sc->harvest_task); random_source_deregister(&random_tpm); #endif sc->common_initialized = false; @@ -241,25 +289,29 @@ out: sc->internal_priv = NULL; } sx_destroy(&sc->dev_lock); - if (sc->sc_cdev != NULL) { - destroy_dev(sc->sc_cdev); - sc->sc_cdev = NULL; - } } int tpm20_resume(device_t dev) { + struct tpm_sc *sc; int error; + sc = device_get_softc(dev); + sx_xlock(&sc->dev_lock); + if (atomic_load_bool(&sc->dying)) { + error = ENXIO; + goto out; + } error = tpm20_restart(dev, false); + if (error == 0) + sc->suspended = false; +out: + sx_xunlock(&sc->dev_lock); if (error != 0) return (error); #if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM - struct tpm_sc *sc; - - sc = device_get_softc(dev); taskqueue_enqueue_timeout(taskqueue_thread, &sc->harvest_task, hz * TPM_HARVEST_INTERVAL); #endif @@ -269,15 +321,27 @@ tpm20_resume(device_t dev) int tpm20_suspend(device_t dev) { - int error; - -#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM struct tpm_sc *sc; + int error; sc = device_get_softc(dev); +#if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM taskqueue_drain_timeout(taskqueue_thread, &sc->harvest_task); #endif + sx_xlock(&sc->dev_lock); + if (atomic_load_bool(&sc->dying)) { + error = ENXIO; + goto out; + } + if (sc->suspended) { + error = 0; + goto out; + } error = tpm20_save_state(dev, true); + if (error == 0) + sc->suspended = true; +out: + sx_xunlock(&sc->dev_lock); #if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM if (error != 0) taskqueue_enqueue_timeout(taskqueue_thread, &sc->harvest_task, @@ -289,7 +353,17 @@ tpm20_suspend(device_t dev) int tpm20_shutdown(device_t dev) { - return (tpm20_save_state(dev, false)); + struct tpm_sc *sc; + int error; + + sc = device_get_softc(dev); + sx_xlock(&sc->dev_lock); + if (atomic_load_bool(&sc->dying)) + error = ENXIO; + else + error = tpm20_save_state(dev, false); + sx_xunlock(&sc->dev_lock); + return (error); } #if defined TPM_HARVEST || defined RANDOM_ENABLE_TPM @@ -314,31 +388,32 @@ tpm20_harvest(void *arg, int unused) sc = arg; sx_xlock(&sc->dev_lock); + if (atomic_load_bool(&sc->dying) || sc->suspended) { + sx_xunlock(&sc->dev_lock); + return; + } priv = sc->internal_priv; memcpy(priv->buf, cmd, sizeof(cmd)); + entropy_size = 0; result = TPM_TRANSMIT(sc->dev, priv, sizeof(cmd)); - if (result != 0) { - sx_xunlock(&sc->dev_lock); - return; - } - - /* The number of random bytes we got is placed right after the header */ - entropy_size = (uint16_t) priv->buf[TPM_HEADER_SIZE + 1]; - if (entropy_size > 0) { - entropy_size = MIN(entropy_size, TPM_HARVEST_SIZE); - memcpy(entropy, - priv->buf + TPM_HEADER_SIZE + sizeof(uint16_t), - entropy_size); + if (result == 0) { + /* The byte count is placed immediately after the header. */ + entropy_size = (uint16_t)priv->buf[TPM_HEADER_SIZE + 1]; + if (entropy_size > 0) { + entropy_size = MIN(entropy_size, TPM_HARVEST_SIZE); + memcpy(entropy, + priv->buf + TPM_HEADER_SIZE + sizeof(uint16_t), + entropy_size); + } } + taskqueue_enqueue_timeout(taskqueue_thread, &sc->harvest_task, + hz * TPM_HARVEST_INTERVAL); sx_xunlock(&sc->dev_lock); if (entropy_size > 0) random_harvest_queue(entropy, entropy_size, RANDOM_PURE_TPM); - - taskqueue_enqueue_timeout(taskqueue_thread, &sc->harvest_task, - hz * TPM_HARVEST_INTERVAL); } #endif /* TPM_HARVEST */ @@ -359,15 +434,20 @@ tpm20_command(device_t dev, uint32_t command, uint16_t parameter, if (sc == NULL) return (ENXIO); + sx_assert(&sc->dev_lock, SA_XLOCKED); + if (atomic_load_bool(&sc->dying)) + return (ENXIO); + priv = sc->internal_priv; + be16enc(cmd, TPM2_ST_NO_SESSIONS); be32enc(cmd + 2, sizeof(cmd)); be32enc(cmd + 6, command); be16enc(cmd + 10, parameter); - sx_xlock(&sc->dev_lock); - priv = sc->internal_priv; delay_ms = TPM2_RETRY_INITIAL_MS; for (;;) { + if (atomic_load_bool(&sc->dying)) + return (ENXIO); memcpy(priv->buf, cmd, sizeof(cmd)); error = TPM_TRANSMIT(sc->dev, priv, sizeof(cmd)); if (error != 0) @@ -388,15 +468,13 @@ tpm20_command(device_t dev, uint32_t command, uint16_t parameter, if (response_rc != TPM2_RC_RETRY && response_rc != TPM2_RC_TESTING) break; + if (atomic_load_bool(&sc->dying)) + return (ENXIO); if (delay_ms > TPM2_RETRY_MAX_MS) break; - sx_xunlock(&sc->dev_lock); pause("tpm2retry", MAX(hz * delay_ms / 1000, 1)); - sx_xlock(&sc->dev_lock); delay_ms *= 2; } - sx_xunlock(&sc->dev_lock); - if (error != 0) { device_printf(dev, "%s command failed: %d\n", name, error); return (error); diff --git a/sys/dev/tpm/tpm20.h b/sys/dev/tpm/tpm20.h index 3bf59112bed8..2aa23318b5e5 100644 --- a/sys/dev/tpm/tpm20.h +++ b/sys/dev/tpm/tpm20.h @@ -130,6 +130,8 @@ struct tpm_sc { uint32_t intr_mask; /* Saved TIS interrupt configuration */ bool interrupts; bool common_initialized; + bool dying; + bool suspended; struct tpm_priv *internal_priv;