From nobody Sat Feb 7 17:55:39 2026 Received: from the.earth.li (the.earth.li [93.93.131.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 583C324A051; Mon, 20 Oct 2025 11:43:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=93.93.131.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760960585; cv=none; b=VYBxek+ExdUCNQeL32nUzrALfENOkD7O8mD+tmBEtGYp3yzbVy/uwVP940+dwwltpcKdxfS+JzBxWVJqxYnFvmAda+9zm9s1vrCVGJ3mn7Db2CjkH/2Sa7xVQmCYFWn9nbWD53QoJHmfoREBbd+sIgoTPB/cmoW4QkJsh3vSmZI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760960585; c=relaxed/simple; bh=GGhYb32zVcEY/nN0x4vMpsh70d+/Vp6kzB++g286hpY=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=N3mzPZymoCDMmmLFHanR9w0eNvfjP8ofLdvfYJCazLbHT04vg2JFIjeB54+mSdlmHCeLBvo657fuF21vE5iadSZltxqVOn/1ZnDH2aqBMlnaWYlfgGLEG02aYe21OX+aowQaeg7fjLnLT8xLknSZn7oK4lMrl7S6flD18rUFkEo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=earth.li; spf=pass smtp.mailfrom=earth.li; dkim=pass (2048-bit key) header.d=earth.li header.i=@earth.li header.b=S5M725Hs; arc=none smtp.client-ip=93.93.131.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=earth.li Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=earth.li Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=earth.li header.i=@earth.li header.b="S5M725Hs" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=earth.li; s=the; h=In-Reply-To:Content-Type:MIME-Version:References:Message-ID:Subject: Cc:To:From:Date:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=kJknCH3swuMVmZ2WVheIS69zgrozTmIHgxve2G9qddE=; b=S5M725Hsun+a05Acy1ROIsbmGL uKSQ1J09CiBPiOOUcpz1xuaODFYj6MSehlK1765j2LeY7a8e1XXMKBh3xqa+/CpkYmIszluP/SEoG 6MTr09gvIiwVCUwQ7+BEMo0dL5FnGceyf6RHRHZDtu6gSAkWCGEDxCIxYVeIRffxrWA4krEeYNqXm n+5WYmLyLqYD4Rn8SuWcaCJF5H6vX2tnAdwszToEXZASU4WgUFP/V1mVzIrzpnQgoMByxwyC2Bsnp sztddQ5lL0lyGIfNg6ntPoX5gn0CfNPG8/bKmvOccDUSuIYfLSfsyKLl2rUzqBr7LnUIdzeEemqDM FlVF2YxA==; Received: from noodles by the.earth.li with local (Exim 4.96) (envelope-from ) id 1vAo64-00184u-1g; Mon, 20 Oct 2025 12:30:40 +0100 Date: Mon, 20 Oct 2025 12:30:40 +0100 From: Jonathan McDowell To: Peter Huewe , Jarkko Sakkinen , Jason Gunthorpe Cc: Linus Torvalds , James Bottomley , linux-integrity@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 1/4] tpm: Remove tpm_find_get_ops Message-ID: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Jonathan McDowell tpm_find_get_ops() looks for the first valid TPM if the caller passes in NULL. All internal users have been converted to either associate themselves with a TPM directly, or call tpm_default_chip() as part of their setup. Remove the no longer necessary tpm_find_get_ops(). Signed-off-by: Jonathan McDowell --- v2: No changes. v3: Move to start of patch series drivers/char/tpm/tpm-chip.c | 36 -------------------------------- drivers/char/tpm/tpm-interface.c | 20 ++++++++++++++---- drivers/char/tpm/tpm.h | 1 - drivers/char/tpm/tpm_tis_core.c | 3 +-- 4 files changed, 17 insertions(+), 43 deletions(-) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index e25daf2396d3..30d00219f9f3 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -230,42 +230,6 @@ struct tpm_chip *tpm_default_chip(void) } EXPORT_SYMBOL_GPL(tpm_default_chip); =20 -/** - * tpm_find_get_ops() - find and reserve a TPM chip - * @chip: a &struct tpm_chip instance, %NULL for the default chip - * - * Finds a TPM chip and reserves its class device and operations. The chip= must - * be released with tpm_put_ops() after use. - * This function is for internal use only. It supports existing TPM callers - * by accepting NULL, but those callers should be converted to pass in a c= hip - * directly. - * - * Return: - * A reserved &struct tpm_chip instance. - * %NULL if a chip is not found. - * %NULL if the chip is not available. - */ -struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip) -{ - int rc; - - if (chip) { - if (!tpm_try_get_ops(chip)) - return chip; - return NULL; - } - - chip =3D tpm_default_chip(); - if (!chip) - return NULL; - rc =3D tpm_try_get_ops(chip); - /* release additional reference we got from tpm_default_chip() */ - put_device(&chip->dev); - if (rc) - return NULL; - return chip; -} - /** * tpm_dev_release() - free chip memory and the device number * @dev: the character device for the TPM chip diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interf= ace.c index c9f173001d0e..f745a098908b 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -313,10 +313,13 @@ int tpm_is_tpm2(struct tpm_chip *chip) { int rc; =20 - chip =3D tpm_find_get_ops(chip); if (!chip) return -ENODEV; =20 + rc =3D tpm_try_get_ops(chip); + if (rc) + return rc; + rc =3D (chip->flags & TPM_CHIP_FLAG_TPM2) !=3D 0; =20 tpm_put_ops(chip); @@ -338,10 +341,13 @@ int tpm_pcr_read(struct tpm_chip *chip, u32 pcr_idx, { int rc; =20 - chip =3D tpm_find_get_ops(chip); if (!chip) return -ENODEV; =20 + rc =3D tpm_try_get_ops(chip); + if (rc) + return rc; + if (chip->flags & TPM_CHIP_FLAG_TPM2) rc =3D tpm2_pcr_read(chip, pcr_idx, digest, NULL); else @@ -369,10 +375,13 @@ int tpm_pcr_extend(struct tpm_chip *chip, u32 pcr_idx, int rc; int i; =20 - chip =3D tpm_find_get_ops(chip); if (!chip) return -ENODEV; =20 + rc =3D tpm_try_get_ops(chip); + if (rc) + return rc; + for (i =3D 0; i < chip->nr_allocated_banks; i++) { if (digests[i].alg_id !=3D chip->allocated_banks[i].alg_id) { rc =3D -EINVAL; @@ -492,10 +501,13 @@ int tpm_get_random(struct tpm_chip *chip, u8 *out, si= ze_t max) if (!out || max > TPM_MAX_RNG_DATA) return -EINVAL; =20 - chip =3D tpm_find_get_ops(chip); if (!chip) return -ENODEV; =20 + rc =3D tpm_try_get_ops(chip); + if (rc) + return rc; + if (chip->flags & TPM_CHIP_FLAG_TPM2) rc =3D tpm2_get_random(chip, out, max); else diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 2726bd38e5ac..02c07fef41ba 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -267,7 +267,6 @@ static inline void tpm_msleep(unsigned int delay_msec) int tpm_chip_bootstrap(struct tpm_chip *chip); int tpm_chip_start(struct tpm_chip *chip); void tpm_chip_stop(struct tpm_chip *chip); -struct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip); =20 struct tpm_chip *tpm_chip_alloc(struct device *dev, const struct tpm_class_ops *ops); diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_cor= e.c index 8954a8660ffc..e2a1769081b1 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -265,8 +265,7 @@ static u8 tpm_tis_status(struct tpm_chip *chip) =20 /* * Dump stack for forensics, as invalid TPM_STS.x could be - * potentially triggered by impaired tpm_try_get_ops() or - * tpm_find_get_ops(). + * potentially triggered by impaired tpm_try_get_ops(). */ dump_stack(); } --=20 2.51.0 From nobody Sat Feb 7 17:55:39 2026 Received: from the.earth.li (the.earth.li [93.93.131.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7E81C1A23AC; Mon, 20 Oct 2025 11:30:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=93.93.131.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760959855; cv=none; b=WuHVdXPnaicpKg09ttkxMo6yoAdL4DaVthBMnSmyRcHlGzFJRNPJ9eq6AUaqL2l7vBuaqLFU9uQCOBm4A5b95+iiC4nYceKAw59A4V0EYfhQknjaoPZmVdTBoPGES87sLV43/pnurDDtyenu1DT90t/lPdd6oRqBM6Ef35yJqiE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760959855; c=relaxed/simple; bh=h/gMN2udSHr2DLLD0ZPi9OPx7y3lFehRWe69LWD6akI=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=Y2icaGba9MjMu5VvmM1Kr3eLvhzJQCuC/D+F2Bq+IXTam/2M61693qRPHmAclLxgqweY9htuC9Aj0rW6//10Ce2rbfHgmaA4VQ8we6sFRsAwvrREzM49WNt7/Bf0didTh8HNuBe+z0wJYEOA7MLntBi8BoiY+rudcEkTJmT6t38= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=earth.li; spf=pass smtp.mailfrom=earth.li; dkim=pass (2048-bit key) header.d=earth.li header.i=@earth.li header.b=p+kFuQzK; arc=none smtp.client-ip=93.93.131.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=earth.li Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=earth.li Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=earth.li header.i=@earth.li header.b="p+kFuQzK" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=earth.li; s=the; h=In-Reply-To:Content-Type:MIME-Version:References:Message-ID:Subject: Cc:To:From:Date:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=sUhX2FuGfYS8QgSzitcQ5P61LZZZqlq70/6Q/h1wOsU=; b=p+kFuQzK9RAQsBayOmD6/S9ar2 F1/OXgOT8iJ1Gy7j1zpbAsddBb8zuZuJubDfuYU2baQx/q/9upsyp2m0D5kX+JqnNuksyFMTPGOA4 LOHdmULmfK7qbMJyON/78MK8tsuhI1FvxtNDActh5/qASinOPcUttCPZStDTr/H5EMY+zBpWPropu W3n5/DjIkMvBNF6aMQNec5aCIGJUgBVhTe/RAajpyltN3Q3rhGrtrfUenmb9Irj94Dwl3K3CrB2dh 4KBgzYS9TSXO4dpuvPot6lfBE3fU5VdZM4CLcQnflJ+9lJR74aFbeZtrUeKY/pHdRnnFcV2WFAAor 61WAILrg==; Received: from noodles by the.earth.li with local (Exim 4.96) (envelope-from ) id 1vAo6D-0018GC-0e; Mon, 20 Oct 2025 12:30:49 +0100 Date: Mon, 20 Oct 2025 12:30:49 +0100 From: Jonathan McDowell To: Peter Huewe , Jarkko Sakkinen , Jason Gunthorpe Cc: Linus Torvalds , James Bottomley , linux-integrity@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 2/4] tpm: Add O_EXCL for exclusive /dev/tpm access Message-ID: <336248d0ab90787d209906526bdbfd5238902509.1760958898.git.noodles@meta.com> References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Jonathan McDowell 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 --- 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, =20 mutex_init(&chip->tpm_mutex); init_rwsem(&chip->ops_sem); + init_rwsem(&chip->open_lock); =20 chip->ops =3D ops; =20 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 *f= ile) { struct tpm_chip *chip; struct file_priv *priv; + int rc; =20 chip =3D container_of(inode->i_cdev, struct tpm_chip, cdev); =20 - /* 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; } =20 + /* + * 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)) =3D=3D (O_RDWR|O_EXCL)) + rc =3D down_write_trylock(&chip->open_lock); + else + rc =3D down_read_trylock(&chip->open_lock); + + if (!rc) { + dev_dbg(&chip->dev, "Another process owns this TPM\n"); + return -EBUSY; + } + priv =3D kzalloc(sizeof(*priv), GFP_KERNEL); if (priv =3D=3D NULL) goto out; + priv->exclusive =3D (file->f_flags & O_EXCL); =20 tpm_common_open(file, chip, priv, NULL); =20 @@ -40,6 +61,10 @@ static int tpm_open(struct inode *inode, struct file *fi= le) =20 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; } =20 @@ -52,6 +77,10 @@ static int tpm_release(struct inode *inode, struct file = *file) =20 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); =20 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; =20 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 #include #include +#include #include #include =20 @@ -168,7 +169,8 @@ struct tpm_chip { unsigned int flags; =20 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; =20 char hwrng_name[64]; struct hwrng hwrng; --=20 2.51.0 From nobody Sat Feb 7 17:55:39 2026 Received: from the.earth.li (the.earth.li [93.93.131.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 62EAA1A23AC; Mon, 20 Oct 2025 11:31:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=93.93.131.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760959864; cv=none; b=M0I7mFm1TUY3ozf9pYak21nJXLPDBI+HllHx/chORbjntSIEMxsymmNx5xIl0yNVDhQ1ibOclZEq8SsU6VReUhSrCNSluj/R4navrDS0Gjvbvg1kahUFNMgKgSDEBgOsnQvqj0YbhsTV7cpAzkws+Xuxb57LJ5uGZwn+2vkbkOQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760959864; c=relaxed/simple; bh=Y6DVARd5TLUUMVptZLcNrFDp38SCHQt6VzjbeB6jwQs=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=jxiGpxYWiZGSYEpt+k9yMF4aPJGJfGptpmRQpATU7Q/aqYQUNH8DwAdTPgeqXG0fMNTDgdMK8zvWzprLlt/05bAd7vIlXRW6VYsWFKg2DgEu8rPFcdm5qOFwd4qzyX8VJErhQ+USQQzWgPrIy8tH70Xzf4UB6nsec9fOxt3rQV4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=earth.li; spf=pass smtp.mailfrom=earth.li; dkim=pass (2048-bit key) header.d=earth.li header.i=@earth.li header.b=CwxXRUEz; arc=none smtp.client-ip=93.93.131.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=earth.li Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=earth.li Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=earth.li header.i=@earth.li header.b="CwxXRUEz" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=earth.li; s=the; h=In-Reply-To:Content-Type:MIME-Version:References:Message-ID:Subject: Cc:To:From:Date:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=ZWln54Xn4JgTYr3X6k8FCnK+46730tKxOqtam13CWHA=; b=CwxXRUEzF6J2Zbo8ht1LBlQ37g Vffw+qK4ss4k42mwlPN/dRlmekhJ2oPXDxVi2QWP+Dc4ZCbba9s5nCO19lSsKR4SuzTDebvgcPFFb xlSVTrq7C3eLK/+0PdPH91/VzAfWWIuMatsUJ1ztnTCn6K6ysdFtJ0MWe6dZD+TPpU6AXG1ySJpdQ e0p+qNEZh1mXubD24DRdydu6nWs/ZGWBx+aWyPCOo1r7ZesIPg/CBm8IG6C+MaKjdUvDLBlybXpZV ovFcEm4T2FeqF75xXEdVLQZw3gZLfjJEVG7qgEc2EZ4l4gQCHCguPbysXv19xZeux22oIIJSfXsTN os9Vm6Eg==; Received: from noodles by the.earth.li with local (Exim 4.96) (envelope-from ) id 1vAo6N-0018SR-00; Mon, 20 Oct 2025 12:30:59 +0100 Date: Mon, 20 Oct 2025 12:30:58 +0100 From: Jonathan McDowell To: Peter Huewe , Jarkko Sakkinen , Jason Gunthorpe Cc: Linus Torvalds , James Bottomley , linux-integrity@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 3/4] tpm: Include /dev/tpmrm when checking exclusive userspace TPM access Message-ID: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Jonathan McDowell The locking on /dev/tpm 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, 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 in the read/write mutex, as a reader. That way it gets factored in when a client wants exclusive access to /dev/tpm but otherwise operates as usual. Signed-off-by: Jonathan McDowell --- 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; =20 chip =3D 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 =3D kzalloc(sizeof(*priv), GFP_KERNEL); if (priv =3D=3D NULL) - return -ENOMEM; + goto err; =20 rc =3D tpm2_init_space(&priv->space, TPM2_SPACE_BUFFER_SIZE); if (rc) { kfree(priv); - return -ENOMEM; + goto err; } =20 tpm_common_open(file, chip, &priv->priv, &priv->space); =20 return 0; + +err: + up_read(&chip->open_lock); + return -ENOMEM; } =20 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); =20 return 0; } --=20 2.51.0 From nobody Sat Feb 7 17:55:39 2026 Received: from the.earth.li (the.earth.li [93.93.131.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id ACDA3241CA2; Mon, 20 Oct 2025 11:31:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=93.93.131.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760959878; cv=none; b=RXMPM6UAdd9MUrxhKAZAiLjcYbdHexe4LYr3oHNPb0g30o2oZFUi17pDkGGFrs+mYuZpbnjuFrIcdwCfQ/6HBpev7kRO5lV5oo+B3995vK3BkUWsryIV7j6/UMBgbj27Y/uvhuT3T31e7o6kDfvcPDBZ1G1toJoeLA9KSLLTIW0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760959878; c=relaxed/simple; bh=xaBRgqhjD0RsHV1Moml6Ob+3CaY34dxtSbjQGYCKG8k=; h=Date:From:To:Cc:Subject:Message-ID:References:MIME-Version: Content-Type:Content-Disposition:In-Reply-To; b=aDhetJh/me0u94Ef7AA+HB4urgT2+renxk9BnS4JYcZ2hc4N8gtx7v4JEGLG2sm6Bw7LbciVMDafrVnsbxmP6/4HY+5rQfT/84j+UaAaYP5bLS1T0Irqa0eFjYjW3mS4+D2akTGLyhRl9r+7Ctm7ePkhEo0iDDdRRy3/Z8dSikk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=earth.li; spf=pass smtp.mailfrom=earth.li; dkim=pass (2048-bit key) header.d=earth.li header.i=@earth.li header.b=kqJpudP8; arc=none smtp.client-ip=93.93.131.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=earth.li Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=earth.li Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=earth.li header.i=@earth.li header.b="kqJpudP8" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=earth.li; s=the; h=In-Reply-To:Content-Type:MIME-Version:References:Message-ID:Subject: Cc:To:From:Date:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=pm4nmT2XDkE/3z3ULWt/r52c6A4Pu3rHrdw+GZ3Tx1Q=; b=kqJpudP8t27VPg1nw213BtNRSL pDzZ7vNDaj+49EVykNiT9jIEO6B/WvTsbLM14B1mSpI40gzeSQtv3Kel4DJ5tPftopJoWLPkJwwks w+mVbHkXYGO7yLNQWPHhPegwEkj9B0p8DSar4aVjOUf6nKNjI6ubuk4fJNyzdnaAyzHItLUmmwGEX 2QGBkEtFamoFPnSGSBsarVauP+UE+weaqS6IDk2dwACcLtVsUbz9Oljdad+v6O31Tps9ZHip6D52Y jOA/3pYI+MQBQBeUQGUm28VzWDguAUc2QejRRqBMC1gbT+n2MX6SFhu9yVD7vl0bSmBcvsf1zdt/T javRpthQ==; Received: from noodles by the.earth.li with local (Exim 4.96) (envelope-from ) id 1vAo6a-0018jz-18; Mon, 20 Oct 2025 12:31:12 +0100 Date: Mon, 20 Oct 2025 12:31:12 +0100 From: Jonathan McDowell To: Peter Huewe , Jarkko Sakkinen , Jason Gunthorpe Cc: Linus Torvalds , James Bottomley , linux-integrity@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 4/4] tpm: Allow for exclusive TPM access when using /dev/tpm Message-ID: <61049f236fe1eaf72402895cea6892b52ce7e279.1760958898.git.noodles@meta.com> References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Jonathan McDowell There are situations where userspace might reasonably desire exclusive access to the TPM, or the kernel's internal context saving + flushing may cause issues, for example when performing firmware upgrades. Extend the locking already used for avoiding concurrent userspace access to prevent internal users of the TPM when /dev/tpm is in use. The few internal users who already hold the open_lock are changed to use tpm_internal_(try_get|put)_ops, with the old tpm_(try_get|put)_ops functions changing to obtain read access to the open_lock. We return -EBUSY when another user has exclusive access, rather than adding waits. Signed-off-by: Jonathan McDowell --- v2: Switch to _locked instead of _internal_ for function names. v3: Move to end of patch series. drivers/char/tpm/tpm-chip.c | 53 +++++++++++++++++++++++++------ drivers/char/tpm/tpm-dev-common.c | 8 ++--- drivers/char/tpm/tpm.h | 2 ++ drivers/char/tpm/tpm2-space.c | 5 ++- 4 files changed, 52 insertions(+), 16 deletions(-) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index ba906966721a..687f6d8cd601 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -144,7 +144,7 @@ void tpm_chip_stop(struct tpm_chip *chip) EXPORT_SYMBOL_GPL(tpm_chip_stop); =20 /** - * tpm_try_get_ops() - Get a ref to the tpm_chip + * tpm_try_get_ops_locked() - Get a ref to the tpm_chip * @chip: Chip to ref * * The caller must already have some kind of locking to ensure that chip is @@ -154,7 +154,7 @@ EXPORT_SYMBOL_GPL(tpm_chip_stop); * * Returns -ERRNO if the chip could not be got. */ -int tpm_try_get_ops(struct tpm_chip *chip) +int tpm_try_get_ops_locked(struct tpm_chip *chip) { int rc =3D -EIO; =20 @@ -185,22 +185,57 @@ int tpm_try_get_ops(struct tpm_chip *chip) put_device(&chip->dev); return rc; } -EXPORT_SYMBOL_GPL(tpm_try_get_ops); =20 /** - * tpm_put_ops() - Release a ref to the tpm_chip + * tpm_put_ops_locked() - Release a ref to the tpm_chip * @chip: Chip to put * - * This is the opposite pair to tpm_try_get_ops(). After this returns chip= may - * be kfree'd. + * This is the opposite pair to tpm_try_get_ops_locked(). After this retur= ns + * chip may be kfree'd. */ -void tpm_put_ops(struct tpm_chip *chip) +void tpm_put_ops_locked(struct tpm_chip *chip) { tpm_chip_stop(chip); mutex_unlock(&chip->tpm_mutex); up_read(&chip->ops_sem); put_device(&chip->dev); } + +/** + * tpm_try_get_ops() - Get a ref to the tpm_chip + * @chip: Chip to ref + * + * The caller must already have some kind of locking to ensure that chip is + * valid. This function will attempt to get the open_lock for the chip, + * ensuring no other user is expecting exclusive access, before locking the + * chip so that the ops member can be accessed safely. The locking prevents + * tpm_chip_unregister from completing, so it should not be held for long + * periods. + * + * Returns -ERRNO if the chip could not be got. + */ +int tpm_try_get_ops(struct tpm_chip *chip) +{ + if (!down_read_trylock(&chip->open_lock)) + return -EBUSY; + + return tpm_try_get_ops_locked(chip); +} +EXPORT_SYMBOL_GPL(tpm_try_get_ops); + +/** + * tpm_put_ops() - Release a ref to the tpm_chip + * @chip: Chip to put + * + * This is the opposite pair to tpm_try_get_ops(). After this returns + * chip may be kfree'd. + */ +void tpm_put_ops(struct tpm_chip *chip) +{ + tpm_put_ops_locked(chip); + + up_read(&chip->open_lock); +} EXPORT_SYMBOL_GPL(tpm_put_ops); =20 /** @@ -644,10 +679,10 @@ void tpm_chip_unregister(struct tpm_chip *chip) #ifdef CONFIG_TCG_TPM2_HMAC int rc; =20 - rc =3D tpm_try_get_ops(chip); + rc =3D tpm_try_get_ops_locked(chip); if (!rc) { tpm2_end_auth_session(chip); - tpm_put_ops(chip); + tpm_put_ops_locked(chip); } #endif =20 diff --git a/drivers/char/tpm/tpm-dev-common.c b/drivers/char/tpm/tpm-dev-c= ommon.c index f2a5e09257dd..0f5bc63411aa 100644 --- a/drivers/char/tpm/tpm-dev-common.c +++ b/drivers/char/tpm/tpm-dev-common.c @@ -65,7 +65,7 @@ static void tpm_dev_async_work(struct work_struct *work) =20 mutex_lock(&priv->buffer_mutex); priv->command_enqueued =3D false; - ret =3D tpm_try_get_ops(priv->chip); + ret =3D tpm_try_get_ops_locked(priv->chip); if (ret) { priv->response_length =3D ret; goto out; @@ -73,7 +73,7 @@ static void tpm_dev_async_work(struct work_struct *work) =20 ret =3D tpm_dev_transmit(priv->chip, priv->space, priv->data_buffer, sizeof(priv->data_buffer)); - tpm_put_ops(priv->chip); + tpm_put_ops_locked(priv->chip); =20 /* * If ret is > 0 then tpm_dev_transmit returned the size of the @@ -220,14 +220,14 @@ ssize_t tpm_common_write(struct file *file, const cha= r __user *buf, * lock during this period so that the tpm can be unregistered even if * the char dev is held open. */ - if (tpm_try_get_ops(priv->chip)) { + if (tpm_try_get_ops_locked(priv->chip)) { ret =3D -EPIPE; goto out; } =20 ret =3D tpm_dev_transmit(priv->chip, priv->space, priv->data_buffer, sizeof(priv->data_buffer)); - tpm_put_ops(priv->chip); + tpm_put_ops_locked(priv->chip); =20 if (ret > 0) { priv->response_length =3D ret; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 02c07fef41ba..57ef8589f5f5 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -272,6 +272,8 @@ struct tpm_chip *tpm_chip_alloc(struct device *dev, const struct tpm_class_ops *ops); struct tpm_chip *tpmm_chip_alloc(struct device *pdev, const struct tpm_class_ops *ops); +int tpm_try_get_ops_locked(struct tpm_chip *chip); +void tpm_put_ops_locked(struct tpm_chip *chip); int tpm_chip_register(struct tpm_chip *chip); void tpm_chip_unregister(struct tpm_chip *chip); =20 diff --git a/drivers/char/tpm/tpm2-space.c b/drivers/char/tpm/tpm2-space.c index 60354cd53b5c..0ad5e18355e0 100644 --- a/drivers/char/tpm/tpm2-space.c +++ b/drivers/char/tpm/tpm2-space.c @@ -58,10 +58,9 @@ int tpm2_init_space(struct tpm_space *space, unsigned in= t buf_size) =20 void tpm2_del_space(struct tpm_chip *chip, struct tpm_space *space) { - - if (tpm_try_get_ops(chip) =3D=3D 0) { + if (tpm_try_get_ops_locked(chip) =3D=3D 0) { tpm2_flush_sessions(chip, space); - tpm_put_ops(chip); + tpm_put_ops_locked(chip); } =20 kfree(space->context_buf); --=20 2.51.0