From: Jonathan McDowell <[email protected]> The /dev/tpm interface allows for only a single user, but does not prevent access to the TPM via other paths (either internal kernel interfaces or the /dev/tpmrm interface). Pave the way to being to able request fully exclusive access by supporting the use of the O_EXCL flag on device open.
Signed-off-by: Jonathan McDowell <[email protected]> --- v2: No changes. v3: Move earlier in series to prevent bisection breakage. Check for O_RDWR as well as O_EXCL to guard against clients doing odd things. Retain single /dev/tpm# user even without O_EXCL. drivers/char/tpm/tpm-chip.c | 1 + drivers/char/tpm/tpm-dev.c | 35 ++++++++++++++++++++++++++++++++--- drivers/char/tpm/tpm-dev.h | 1 + include/linux/tpm.h | 4 +++- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 30d00219f9f3..ba906966721a 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -302,6 +302,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..819f3e1546da 100644 --- a/drivers/char/tpm/tpm-dev.c +++ b/drivers/char/tpm/tpm-dev.c @@ -19,20 +19,41 @@ static int tpm_open(struct inode *inode, struct file *file) { struct tpm_chip *chip; struct file_priv *priv; + int rc; 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. */ + /* + * It's assured that the chip will be opened just once via the direct + * /dev/tpm# interface by the check of is_open variable, which is + * protected by driver_lock. + */ if (test_and_set_bit(0, &chip->is_open)) { dev_dbg(&chip->dev, "Another process owns this TPM\n"); return -EBUSY; } + /* + * If a client uses the O_EXCL flag then it expects to be the only TPM + * user across all access paths, so we treat it as a write lock. + * Otherwise we use a read lock, allowing for concurrent openers. + * We make sure the client is opening the device for reading + writing; + * opening for exclusive access doesn't make sense if not. + */ + if ((file->f_flags & (O_ACCMODE|O_EXCL)) == (O_RDWR|O_EXCL)) + rc = down_write_trylock(&chip->open_lock); + else + rc = down_read_trylock(&chip->open_lock); + + if (!rc) { + dev_dbg(&chip->dev, "Another process owns this TPM\n"); + return -EBUSY; + } + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (priv == NULL) goto out; + priv->exclusive = (file->f_flags & O_EXCL); tpm_common_open(file, chip, priv, NULL); @@ -40,6 +61,10 @@ static int tpm_open(struct inode *inode, struct file *file) out: clear_bit(0, &chip->is_open); + if (file->f_flags & O_EXCL) + up_write(&chip->open_lock); + else + up_read(&chip->open_lock); return -ENOMEM; } @@ -52,6 +77,10 @@ static int tpm_release(struct inode *inode, struct file *file) tpm_common_release(file, priv); clear_bit(0, &priv->chip->is_open); + if (priv->exclusive) + up_write(&priv->chip->open_lock); + else + up_read(&priv->chip->open_lock); kfree(priv); return 0; diff --git a/drivers/char/tpm/tpm-dev.h b/drivers/char/tpm/tpm-dev.h index f3742bcc73e3..0ad8504c73e4 100644 --- a/drivers/char/tpm/tpm-dev.h +++ b/drivers/char/tpm/tpm-dev.h @@ -17,6 +17,7 @@ struct file_priv { ssize_t response_length; bool response_read; bool command_enqueued; + bool exclusive; u8 data_buffer[TPM_BUFSIZE]; }; diff --git a/include/linux/tpm.h b/include/linux/tpm.h index dc0338a783f3..3bd3da5cb97e 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,8 @@ struct tpm_chip { unsigned int flags; int dev_num; /* /dev/tpm# */ - unsigned long is_open; /* only one allowed */ + unsigned long is_open; /* only one tpm# allowed */ + struct rw_semaphore open_lock; char hwrng_name[64]; struct hwrng hwrng; -- 2.51.0
