From: Jarkko Sakkinen <jarkko@kernel.org>
To: Jonathan McDowell <noodles@earth.li>
Cc: Peter Huewe <peterhuewe@gmx.de>, Jason Gunthorpe <jgg@ziepe.ca>,
linux-integrity@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [RFC PATCH 1/4] tpm: Ensure exclusive userspace access when using /dev/tpm<n>
Date: Wed, 3 Sep 2025 22:22:56 +0300 [thread overview]
Message-ID: <aLiVkKbcCoLqcLtG@kernel.org> (raw)
In-Reply-To: <d2d7c8105a73e43866f23c88b188ee6e83562726.1756833527.git.noodles@meta.com>
On Tue, Sep 02, 2025 at 06:26:49PM +0100, Jonathan McDowell wrote:
> From: Jonathan McDowell <noodles@meta.com>
>
> There is an is_open lock on /dev/tpm<n> that dates back to at least
> 2013, but it only prevents multiple accesses via *this* interface. It is
> perfectly possible for userspace to use /dev/tpmrm<n>, or the kernel to
> use the internal interfaces, to access the TPM.
>
> This can cause problems with userspace expecting exclusive access and
> something changing state underneath it, for example while performing a
> TPM firmware upgrade.
>
> Close the userspace loophole by changing the simple bit lock to a full
> read/write mutex. Direct /dev/tpm<n> access needs an exclusive write
> lock, the resource broker continues to allow concurrent access *except*
> when /dev/tpm<n> is open.
>
> Signed-off-by: Jonathan McDowell <noodles@meta.com>
I think the rationale makes sense to me as they are different view port
for the exact same hardware instance, and /dev/tpmrm0 scales only within
its own virtual universum.
I don't know what would be the best write up but basically I'd cut the
story shorter a bit and explicitly enumerate these anchoring "hard
reasons". Problems in user space is something that I can imagine that
there is a variety problem but it is more abstract side of this
issue. When you have a smoking gun just point your finger to it
exactly.
> ---
> drivers/char/tpm/tpm-chip.c | 1 +
> drivers/char/tpm/tpm-dev.c | 14 ++++++++------
> drivers/char/tpm/tpmrm-dev.c | 20 ++++++++++++++++++--
> include/linux/tpm.h | 3 ++-
> 4 files changed, 29 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c
> index e25daf2396d3..8c8e9054762a 100644
> --- a/drivers/char/tpm/tpm-chip.c
> +++ b/drivers/char/tpm/tpm-chip.c
> @@ -338,6 +338,7 @@ struct tpm_chip *tpm_chip_alloc(struct device *pdev,
>
> mutex_init(&chip->tpm_mutex);
> init_rwsem(&chip->ops_sem);
> + init_rwsem(&chip->open_lock);
>
> chip->ops = ops;
>
> diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c
> index 97c94b5e9340..80c4b3f3ad18 100644
> --- a/drivers/char/tpm/tpm-dev.c
> +++ b/drivers/char/tpm/tpm-dev.c
> @@ -22,10 +22,12 @@ static int tpm_open(struct inode *inode, struct file *file)
>
> chip = container_of(inode->i_cdev, struct tpm_chip, cdev);
>
> - /* It's assured that the chip will be opened just once,
> - * by the check of is_open variable, which is protected
> - * by driver_lock. */
> - if (test_and_set_bit(0, &chip->is_open)) {
> + /*
> + * Only one client is allowed to have /dev/tpm0 open at a time, so we
> + * treat it as a write lock. The shared /dev/tpmrm0 is treated as a
> + * read lock.
> + */
> + if (!down_write_trylock(&chip->open_lock)) {
> dev_dbg(&chip->dev, "Another process owns this TPM\n");
> return -EBUSY;
> }
> @@ -39,7 +41,7 @@ static int tpm_open(struct inode *inode, struct file *file)
> return 0;
>
> out:
> - clear_bit(0, &chip->is_open);
> + up_write(&chip->open_lock);
> return -ENOMEM;
> }
>
> @@ -51,7 +53,7 @@ static int tpm_release(struct inode *inode, struct file *file)
> struct file_priv *priv = file->private_data;
>
> tpm_common_release(file, priv);
> - clear_bit(0, &priv->chip->is_open);
> + up_write(&priv->chip->open_lock);
> kfree(priv);
>
> return 0;
> diff --git a/drivers/char/tpm/tpmrm-dev.c b/drivers/char/tpm/tpmrm-dev.c
> index c25df7ea064e..40c139a080b6 100644
> --- a/drivers/char/tpm/tpmrm-dev.c
> +++ b/drivers/char/tpm/tpmrm-dev.c
> @@ -17,19 +17,34 @@ static int tpmrm_open(struct inode *inode, struct file *file)
> int rc;
>
> chip = container_of(inode->i_cdev, struct tpm_chip, cdevs);
> +
> + /*
> + * Only one client is allowed to have /dev/tpm0 open at a time, so we
> + * treat it as a write lock. The shared /dev/tpmrm0 is treated as a
> + * read lock.
> + */
> + if (!down_read_trylock(&chip->open_lock)) {
> + dev_dbg(&chip->dev, "Another process owns this TPM\n");
> + return -EBUSY;
> + }
> +
> priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> if (priv == NULL)
> - return -ENOMEM;
> + goto out;
>
> rc = tpm2_init_space(&priv->space, TPM2_SPACE_BUFFER_SIZE);
> if (rc) {
> kfree(priv);
> - return -ENOMEM;
> + goto out;
> }
>
> tpm_common_open(file, chip, &priv->priv, &priv->space);
>
> return 0;
> +
> +out:
nit
err:
as it is purely for error propagation
> + up_read(&chip->open_lock);
> + return -ENOMEM;
> }
>
> static int tpmrm_release(struct inode *inode, struct file *file)
> @@ -40,6 +55,7 @@ static int tpmrm_release(struct inode *inode, struct file *file)
> tpm_common_release(file, fpriv);
> tpm2_del_space(fpriv->chip, &priv->space);
> kfree(priv);
> + up_read(&fpriv->chip->open_lock);
>
> return 0;
> }
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index b0e9eb5ef022..548362d20b32 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -22,6 +22,7 @@
> #include <linux/cdev.h>
> #include <linux/fs.h>
> #include <linux/highmem.h>
> +#include <linux/rwsem.h>
> #include <crypto/hash_info.h>
> #include <crypto/aes.h>
>
> @@ -168,7 +169,7 @@ struct tpm_chip {
> unsigned int flags;
>
> int dev_num; /* /dev/tpm# */
> - unsigned long is_open; /* only one allowed */
> + struct rw_semaphore open_lock;
>
> char hwrng_name[64];
> struct hwrng hwrng;
> --
> 2.51.0
>
BR, Jarkko
next prev parent reply other threads:[~2025-09-03 19:23 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-02 17:26 [RFC PATCH 0/4] Re-establish ability for exclusive TPM access to userspace Jonathan McDowell
2025-09-02 17:26 ` [RFC PATCH 1/4] tpm: Ensure exclusive userspace access when using /dev/tpm<n> Jonathan McDowell
2025-09-03 19:22 ` Jarkko Sakkinen [this message]
2025-09-02 17:27 ` [RFC PATCH 2/4] tpm: Remove tpm_find_get_ops Jonathan McDowell
2025-09-10 16:54 ` Jarkko Sakkinen
2025-09-02 17:27 ` [RFC PATCH 3/4] tpm: Allow for exclusive TPM access when using /dev/tpm<n> Jonathan McDowell
2025-09-10 17:04 ` Jarkko Sakkinen
2025-09-02 17:27 ` [RFC PATCH 4/4] tpm: Require O_EXCL for exclusive /dev/tpm access Jonathan McDowell
2025-09-10 17:06 ` Jarkko Sakkinen
2025-09-23 17:09 ` [PATCH v2 0/4] Re-establish ability for exclusive TPM access to userspace Jonathan McDowell
2025-09-23 17:10 ` [PATCH v2 1/4] tpm: Ensure exclusive userspace access when using /dev/tpm<n> Jonathan McDowell
2025-09-24 1:14 ` Jarkko Sakkinen
2025-09-23 17:10 ` [PATCH v2 2/4] tpm: Remove tpm_find_get_ops Jonathan McDowell
2025-09-24 1:19 ` Jarkko Sakkinen
2025-09-23 17:10 ` [PATCH v2 3/4] tpm: Allow for exclusive TPM access when using /dev/tpm<n> Jonathan McDowell
2025-09-24 1:22 ` Jarkko Sakkinen
2025-09-23 17:10 ` [PATCH v2 4/4] tpm: Require O_EXCL for exclusive /dev/tpm access Jonathan McDowell
2025-09-24 1:23 ` Jarkko Sakkinen
2025-10-20 11:30 ` [PATCH v3 0/4] pm: Ensure exclusive userspace access when using /dev/tpm<n> Jonathan McDowell
2025-10-20 11:30 ` [PATCH v3 1/4] tpm: Remove tpm_find_get_ops Jonathan McDowell
2025-10-20 11:30 ` [PATCH v3 2/4] tpm: Add O_EXCL for exclusive /dev/tpm access Jonathan McDowell
2025-10-20 11:30 ` [PATCH v3 3/4] tpm: Include /dev/tpmrm<n> when checking exclusive userspace TPM access Jonathan McDowell
2025-10-20 11:31 ` [PATCH v3 4/4] tpm: Allow for exclusive TPM access when using /dev/tpm<n> Jonathan McDowell
2025-10-20 11:53 ` Roberto Sassu
2025-10-23 14:24 ` Jonathan McDowell
2025-10-27 19:38 ` Jarkko Sakkinen
2025-10-27 20:09 ` James Bottomley
2025-10-27 20:18 ` Jarkko Sakkinen
2025-11-03 18:38 ` Jonathan McDowell
2025-11-09 4:34 ` Jarkko Sakkinen
2025-10-24 18:55 ` [PATCH v3 0/4] pm: Ensure exclusive userspace " Jarkko Sakkinen
2025-10-27 11:50 ` Mimi Zohar
2025-10-27 19:41 ` Jarkko Sakkinen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aLiVkKbcCoLqcLtG@kernel.org \
--to=jarkko@kernel.org \
--cc=jgg@ziepe.ca \
--cc=linux-integrity@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=noodles@earth.li \
--cc=peterhuewe@gmx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.