From: Jonathan McDowell <[email protected]> The locking on /dev/tpm<n> that dates back to at least 2013, but it only prevents multiple accesses via *that* interface. It is perfectly possible for userspace to use /dev/tpmrm<n>, or the kernel to use the internal interfaces, to access the TPM. For tooling expecting exclusive access, such as firmware updates, this can cause issues.
Close the userspace loophole by including /dev/tpmrm<n> in the read/write mutex, as a reader. That way it gets factored in when a client wants exclusive access to /dev/tpm<n> but otherwise operates as usual. Signed-off-by: Jonathan McDowell <[email protected]> --- v2: Change error path label to err instead of out. Rework commit message. v3: Rework based on O_EXCL patch being moved earlier in the series. drivers/char/tpm/tpmrm-dev.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/tpmrm-dev.c b/drivers/char/tpm/tpmrm-dev.c index c25df7ea064e..780a17454088 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); + + /* + * A client can choose to have exclusive access to the TPM by opening + * /dev/tpm0 with O_EXCL set, in which case we treat it as a write + * lock. All other opens get 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 err; rc = tpm2_init_space(&priv->space, TPM2_SPACE_BUFFER_SIZE); if (rc) { kfree(priv); - return -ENOMEM; + goto err; } tpm_common_open(file, chip, &priv->priv, &priv->space); return 0; + +err: + 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; } -- 2.51.0
