From nobody Thu Apr 2 17:18:53 2026 Received: from mail-pf1-f182.google.com (mail-pf1-f182.google.com [209.85.210.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EF4C634A784 for ; Wed, 11 Feb 2026 03:32:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.182 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780765; cv=none; b=m3aJAK4RNLk4tUyooWN+MfMeQBc3o/vpSHH5+kOSCZbUJoO8hpCTEh0Jl5If3TLAXc9fUSupuERgt9HFJFhQRITVtlTu4+mtFQezuCRPu3nix2xL05WB3i7jt0kZILLUWt5SbKnmHieFDfcWCJVy9hjsAIWs3Rob0tKFU2nHxAs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770780765; c=relaxed/simple; bh=kxAaH/R88X1dnCESSe1LeIaJnXEvtoow32TWakEjvNk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PZ6zGN1+ijEX2wk9WYCkcOE5ShYVqrDpLOaXPi6891efVIEKdwFXNzqTsCdob7Dxlb43IPTIHr3PqobecIyoXxttU9sPW5NY38si2XgnzQXwX6rdXZQJEvSrqm2BqahRXG9KUvgOO7EA1hJwkZ2mAWcacmqsfsrDvwfn9bJxEYU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=UGomaO7o; arc=none smtp.client-ip=209.85.210.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="UGomaO7o" Received: by mail-pf1-f182.google.com with SMTP id d2e1a72fcca58-8249aca0affso180462b3a.3 for ; Tue, 10 Feb 2026 19:32:43 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770780763; x=1771385563; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=uQmEdlkVMUMCwxAKuTSKI45sx1LLRiTRlNgCvCrwMvE=; b=UGomaO7oVQbLwjxr4hlWHnUKLe8OFZBqhBRAkJIGxpaKWMsVYocc2sNegQk2zuJFuh ker0TZBtHULEw6oAlHL8KD0SYqaQU4BqSPoymOX3GQJ/s/l0SC4GJ0e3nj4zQkuIsO6Y 1nK/IzAMirt3bqeSdzAQvqa3dAN5dNGbpztLMQjqSyiqtEMVkSwo6dysg5uGPfSAkb3X ge10dxxUB2hXGswAYdXONM2p386VQ6X/jcrGzLlRz6VCHLndGlXEQUqyEHsACaHCE15N h6J1SClQHC5zapOYyAaTS8W3uGR4ksK1DqB+ym7T07FxEIATkHnS7AlSRCWfSsEyKMwW wBGg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770780763; x=1771385563; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=uQmEdlkVMUMCwxAKuTSKI45sx1LLRiTRlNgCvCrwMvE=; b=kKdiDq/UlWVdV3++Qo/W01SAb3rNa+3vGBkld92E6eIjKpuYZKNgxgR8SY7GfSMBuy xnVf9kQ4lTX+Ai8gVfWibAKg2gOv/pfWuFZnjlhAcYAnA+LDjhT3bXdMlJ6glWwyMhWo A4PuvEns22hg9mle3E5itZovwKhH+uwkP1GJt5UEXl+nmzDn92CpoQXFy45pLi9fDwqo uG1+p+nB3dxbMfGmJ7HTOf++Ar2ddhcmx1nMXe+zjmRFG0lPpG19cZzAUnbNm+G4lexf Cp/k45CKVM2/8OJ7nkgG+AHWBWrCV3dACneMoc/qvB7UmN5nLTOutm367fNPfZtYsyaq lnlg== X-Forwarded-Encrypted: i=1; AJvYcCUTLvMzAcdYe61HLG7mlT4eRF8YJPLcaY/0IGKhoHDM9W5fU/rprCHFeM96gOF32lFZdGG22slF4ASlNIA=@vger.kernel.org X-Gm-Message-State: AOJu0YwSo15J+LNwwP6o/LemCS3CNvYAnmXPgdDgRK2d9vPbET4QQ4wB ELsdINpt04aGK5QEx9Nv4xtJ8IQSWfotq2VuRg+2volkqd/DLoLPirnY X-Gm-Gg: AZuq6aJxGSiLLwn98Querkb8fUbT8EDYCClCY25TsAmR5sUH/jHhjBwx6bwJ5/3Hp7B tX+TfLnYj2n9xeyaCgnNQ5DK+Odwx+f+F/VujTyR7s81/HouoABBeAclsR2S6A74iLFEcZLBFYk UCcPWbnQ8jRc+2B1p2y53jvlDOmBww9wQcITHsmtGGh4Fwg7TWWl0dHTpBIc1/NRa8Qe0t/9ZdP rNjXGguCRf5KPlKToHjmf3ea8lAD+8XcwxqWiyie6jFXaPm/PcfGJaDwtQlzdenjXJ8HvvaDZUM kA7jfUXtBuwL+HgLiQa5/L/9DvHyS2YcexhwGonpk4BW4LO5njJOJ0hl8haWoziD9XmtyohfwSC oCCES1DT5hHDdQU0nWhE7d6GeyJdhnq6bF6TJupCToD3L9/1ooWLurIjfj45trWNzacbVsq0U6e jqCmjY6ytQ+CYFTWVN+J0brhBMeFtGlHGiBjeGCJpXow== X-Received: by 2002:a05:6a21:b91:b0:37e:4319:d7c8 with SMTP id adf61e73a8af0-3942e6d5e66mr1179185637.75.1770780763333; Tue, 10 Feb 2026 19:32:43 -0800 (PST) Received: from toolbx.alistair23.me ([2403:581e:fdf9:0:6209:4521:6813:45b7]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-c6e197d63c9sm464856a12.20.2026.02.10.19.32.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Feb 2026 19:32:42 -0800 (PST) From: alistair23@gmail.com X-Google-Original-From: alistair.francis@wdc.com To: bhelgaas@google.com, lukas@wunner.de, rust-for-linux@vger.kernel.org, akpm@linux-foundation.org, linux-pci@vger.kernel.org, Jonathan.Cameron@huawei.com, linux-cxl@vger.kernel.org, linux-kernel@vger.kernel.org Cc: alex.gaynor@gmail.com, benno.lossin@proton.me, boqun.feng@gmail.com, a.hindborg@kernel.org, gary@garyguo.net, bjorn3_gh@protonmail.com, tmgross@umich.edu, alistair23@gmail.com, ojeda@kernel.org, wilfred.mallawa@wdc.com, aliceryhl@google.com, Alistair Francis Subject: [RFC v3 25/27] PCI/CMA: Expose in sysfs whether devices are authenticated Date: Wed, 11 Feb 2026 13:29:32 +1000 Message-ID: <20260211032935.2705841-26-alistair.francis@wdc.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260211032935.2705841-1-alistair.francis@wdc.com> References: <20260211032935.2705841-1-alistair.francis@wdc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Alistair Francis From: Lukas Wunner The PCI core has just been amended to authenticate CMA-capable devices on enumeration and store the result in an "authenticated" bit in struct pci_dev->spdm_state. Expose the bit to user space through an eponymous sysfs attribute. Allow user space to trigger reauthentication (e.g. after it has updated the CMA keyring) by writing to the sysfs attribute. Implement the attribute in the SPDM library so that other bus types besides PCI may take advantage of it. They just need to add spdm_attr_group to the attribute groups of their devices and amend the dev_to_spdm_state() helper which retrieves the spdm_state for a given device. The helper may return an ERR_PTR if it couldn't be determined whether SPDM is supported by the device. The sysfs attribute is visible in that case but returns an error on access. This prevents downgrade attacks where an attacker disturbs memory allocation or DOE communication in order to create the appearance that SPDM is unsupported. Subject to further discussion, a future commit might add a user-defined policy to forbid driver binding to devices which failed authentication, similar to the "authorized" attribute for USB. Alternatively, authentication success might be signaled to user space through a uevent, whereupon it may bind a (blacklisted) driver. A uevent signaling authentication failure might similarly cause user space to unbind or outright remove the potentially malicious device. Traffic from devices which failed authentication could also be filtered through ACS I/O Request Blocking Enable (PCIe r6.2 sec 7.7.11.3) or through Link Disable (PCIe r6.2 sec 7.5.3.7). Unlike an IOMMU, that will not only protect the host, but also prevent malicious peer-to-peer traffic to other devices. Signed-off-by: Lukas Wunner [ Changes by AF: - Drop lib/spdm changes and replace with Rust implementation ] Signed-off-by: Alistair Francis --- Documentation/ABI/testing/sysfs-devices-spdm | 31 +++++++ MAINTAINERS | 3 +- drivers/pci/cma.c | 12 ++- drivers/pci/doe.c | 2 + drivers/pci/pci-sysfs.c | 3 + drivers/pci/pci.h | 5 + include/linux/pci.h | 12 +++ lib/rspdm/Makefile | 1 + lib/rspdm/lib.rs | 1 + lib/rspdm/req-sysfs.c | 98 ++++++++++++++++++++ lib/rspdm/spdm.h | 17 ++++ lib/rspdm/sysfs.rs | 38 ++++++++ 12 files changed, 218 insertions(+), 5 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-devices-spdm create mode 100644 lib/rspdm/req-sysfs.c create mode 100644 lib/rspdm/spdm.h create mode 100644 lib/rspdm/sysfs.rs diff --git a/Documentation/ABI/testing/sysfs-devices-spdm b/Documentation/A= BI/testing/sysfs-devices-spdm new file mode 100644 index 000000000000..018acde950ff --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-spdm @@ -0,0 +1,31 @@ +What: /sys/devices/.../authenticated +Date: June 2025 +Contact: Lukas Wunner +Description: + This file contains 1 if the device authenticated successfully + with SPDM (Security Protocol and Data Model). It contains 0 if + the device failed authentication (and may thus be malicious). + + Writing "re" to this file causes reauthentication. + That may be opportune after updating the device keyring. + The device keyring of the PCI bus is named ".cma" + (Component Measurement and Authentication). + + Reauthentication may also be necessary after device identity + has mutated, e.g. after downloading firmware to an FPGA device. + + The file is not visible if authentication is unsupported + by the device. + + If the kernel could not determine whether authentication is + supported because memory was low or communication with the + device was not working, the file is visible but accessing it + fails with error code ENOTTY. + + This prevents downgrade attacks where an attacker consumes + memory or disturbs communication in order to create the + appearance that a device does not support authentication. + + The reason why authentication support could not be determined + is apparent from "dmesg". To re-probe authentication support + of PCI devices, exercise the "remove" and "rescan" attributes. diff --git a/MAINTAINERS b/MAINTAINERS index 58898260fde8..eae4722e40bf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23702,7 +23702,8 @@ L: linux-cxl@vger.kernel.org L: linux-pci@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/devsec/spdm.git -F: drivers/pci/cma.c +F: Documentation/ABI/testing/sysfs-devices-spdm +F: drivers/pci/cma* F: include/linux/spdm.h F: lib/rspdm/ =20 diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c index 8d64008594e2..55b8fe6d3121 100644 --- a/drivers/pci/cma.c +++ b/drivers/pci/cma.c @@ -172,8 +172,10 @@ void pci_cma_init(struct pci_dev *pdev) { struct pci_doe_mb *doe; =20 - if (IS_ERR(pci_cma_keyring)) + if (IS_ERR(pci_cma_keyring)) { + pdev->spdm_state =3D ERR_PTR(-ENOTTY); return; + } =20 if (!pci_is_pcie(pdev)) return; @@ -186,8 +188,10 @@ void pci_cma_init(struct pci_dev *pdev) pdev->spdm_state =3D spdm_create(&pdev->dev, pci_doe_transport, doe, PCI_DOE_MAX_PAYLOAD, pci_cma_keyring, pci_cma_validate); - if (!pdev->spdm_state) + if (!pdev->spdm_state) { + pdev->spdm_state =3D ERR_PTR(-ENOTTY); return; + } =20 /* * Keep spdm_state allocated even if initial authentication fails @@ -205,7 +209,7 @@ void pci_cma_init(struct pci_dev *pdev) */ void pci_cma_reauthenticate(struct pci_dev *pdev) { - if (!pdev->spdm_state) + if (IS_ERR_OR_NULL(pdev->spdm_state)) return; =20 spdm_authenticate(pdev->spdm_state); @@ -213,7 +217,7 @@ void pci_cma_reauthenticate(struct pci_dev *pdev) =20 void pci_cma_destroy(struct pci_dev *pdev) { - if (!pdev->spdm_state) + if (IS_ERR_OR_NULL(pdev->spdm_state)) return; =20 spdm_destroy(pdev->spdm_state); diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c index 344e01496a8e..96ca77ecba90 100644 --- a/drivers/pci/doe.c +++ b/drivers/pci/doe.c @@ -857,6 +857,7 @@ void pci_doe_init(struct pci_dev *pdev) if (IS_ERR(doe_mb)) { pci_err(pdev, "[%x] failed to create mailbox: %ld\n", offset, PTR_ERR(doe_mb)); + pci_cma_disable(pdev); continue; } =20 @@ -865,6 +866,7 @@ void pci_doe_init(struct pci_dev *pdev) pci_err(pdev, "[%x] failed to insert mailbox: %d\n", offset, rc); pci_doe_destroy_mb(doe_mb); + pci_cma_disable(pdev); } } } diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index c2df915ad2d2..b2b2096f8162 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1853,6 +1853,9 @@ const struct attribute_group *pci_dev_attr_groups[] = =3D { #ifdef CONFIG_PCIEASPM &aspm_ctrl_attr_group, #endif +#ifdef CONFIG_PCI_CMA + &spdm_attr_group, +#endif #ifdef CONFIG_PCI_DOE &pci_doe_sysfs_group, #endif diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index f87a4b7c92b8..80bd998e77e4 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -603,10 +603,15 @@ static inline void pci_doe_disconnected(struct pci_de= v *pdev) { } void pci_cma_init(struct pci_dev *pdev); void pci_cma_destroy(struct pci_dev *pdev); void pci_cma_reauthenticate(struct pci_dev *pdev); +static inline void pci_cma_disable(struct pci_dev *pdev) +{ + pdev->spdm_state =3D ERR_PTR(-ENOTTY); +} #else static inline void pci_cma_init(struct pci_dev *pdev) { } static inline void pci_cma_destroy(struct pci_dev *pdev) { } static inline void pci_cma_reauthenticate(struct pci_dev *pdev) { } +static inline void pci_cma_disable(struct pci_dev *pdev) { } #endif =20 #ifdef CONFIG_PCI_NPEM diff --git a/include/linux/pci.h b/include/linux/pci.h index 7384846ade19..44e7bd6f2076 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2827,6 +2827,18 @@ static inline bool pci_is_thunderbolt_attached(struc= t pci_dev *pdev) void pci_uevent_ers(struct pci_dev *pdev, enum pci_ers_result err_type); #endif =20 +#ifdef CONFIG_PCI_CMA +static inline struct spdm_state *pci_dev_to_spdm_state(struct pci_dev *pde= v) +{ + return pdev->spdm_state; +} +#else +static inline struct spdm_state *pci_dev_to_spdm_state(struct pci_dev *pde= v) +{ + return NULL; +} +#endif + #include =20 #define pci_emerg(pdev, fmt, arg...) dev_emerg(&(pdev)->dev, fmt, ##arg) diff --git a/lib/rspdm/Makefile b/lib/rspdm/Makefile index 1f62ee2a882d..f15b1437196b 100644 --- a/lib/rspdm/Makefile +++ b/lib/rspdm/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_RSPDM) +=3D spdm.o =20 spdm-y :=3D lib.o +spdm-$(CONFIG_SYSFS) +=3D req-sysfs.o diff --git a/lib/rspdm/lib.rs b/lib/rspdm/lib.rs index 05fa471bb1e2..c0e3e039c14f 100644 --- a/lib/rspdm/lib.rs +++ b/lib/rspdm/lib.rs @@ -24,6 +24,7 @@ =20 mod consts; mod state; +pub mod sysfs; mod validator; =20 /// spdm_create() - Allocate SPDM session diff --git a/lib/rspdm/req-sysfs.c b/lib/rspdm/req-sysfs.c new file mode 100644 index 000000000000..7971be291627 --- /dev/null +++ b/lib/rspdm/req-sysfs.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Rust implementation of the DMTF Security Protocol and Data Model (SPDM) + * https://www.dmtf.org/dsp/DSP0274 + * + * Requester role: sysfs interface + * + * Copyright (C) 2023-24 Intel Corporation + * Copyright (C) 2024 Western Digital + */ + +#include +#include "spdm.h" + +int rust_authenticated_show(void *spdm_state, char *buf); + +/** + * dev_to_spdm_state() - Retrieve SPDM session state for given device + * + * @dev: Responder device + * + * Returns a pointer to the device's SPDM session state, + * %NULL if the device doesn't have one or + * %ERR_PTR if it couldn't be determined whether SPDM is supported. + * + * In the %ERR_PTR case, attributes are visible but return an error on acc= ess. + * This prevents downgrade attacks where an attacker disturbs memory alloc= ation + * or communication with the device in order to create the appearance that= SPDM + * is unsupported. E.g. with PCI devices, the attacker may foil CMA or DOE + * initialization by simply hogging memory. + */ +static void *dev_to_spdm_state(struct device *dev) +{ + if (dev_is_pci(dev)) + return pci_dev_to_spdm_state(to_pci_dev(dev)); + + /* Insert mappers for further bus types here. */ + + return NULL; +} + +/* authenticated attribute */ + +static umode_t spdm_attrs_are_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev =3D kobj_to_dev(kobj); + void *spdm_state =3D dev_to_spdm_state(dev); + + if (IS_ERR_OR_NULL(spdm_state)) + return SYSFS_GROUP_INVISIBLE; + + return a->mode; +} + +static ssize_t authenticated_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + void *spdm_state =3D dev_to_spdm_state(dev); + int rc; + + if (IS_ERR_OR_NULL(spdm_state)) + return PTR_ERR(spdm_state); + + if (sysfs_streq(buf, "re")) { + rc =3D spdm_chall(spdm_state); + if (rc) + return rc; + } else { + return -EINVAL; + } + + return count; +} + +static ssize_t authenticated_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + void *spdm_state =3D dev_to_spdm_state(dev); + + if (IS_ERR_OR_NULL(spdm_state)) + return PTR_ERR(spdm_state); + + return rust_authenticated_show(spdm_state, buf); +} +static DEVICE_ATTR_RW(authenticated); + +static struct attribute *spdm_attrs[] =3D { + &dev_attr_authenticated.attr, + NULL +}; + +const struct attribute_group spdm_attr_group =3D { + .attrs =3D spdm_attrs, + .is_visible =3D spdm_attrs_are_visible, +}; diff --git a/lib/rspdm/spdm.h b/lib/rspdm/spdm.h new file mode 100644 index 000000000000..43ef56a073c0 --- /dev/null +++ b/lib/rspdm/spdm.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * DMTF Security Protocol and Data Model (SPDM) + * https://www.dmtf.org/dsp/DSP0274 + * + * Copyright (C) 2021-22 Huawei + * Jonathan Cameron + * + * Copyright (C) 2022-25 Intel Corporation + */ + +#ifndef _LIB_SPDM_H_ +#define _LIB_SPDM_H_ + +int spdm_chall(struct spdm_state *spdm_state); + +#endif /* _LIB_SPDM_H_ */ diff --git a/lib/rspdm/sysfs.rs b/lib/rspdm/sysfs.rs new file mode 100644 index 000000000000..d0e7f6b3de40 --- /dev/null +++ b/lib/rspdm/sysfs.rs @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 + +// Copyright (C) 2024 Western Digital + +//! Rust sysfs helper functions +//! +//! Rust implementation of the DMTF Security Protocol and Data Model (SPDM) +//! + +use crate::SpdmState; +use kernel::prelude::*; +use kernel::{bindings, str::CString}; + +/// Helper function for the sysfs `authenticated_show()`. +#[no_mangle] +pub extern "C" fn rust_authenticated_show(spdm_state: *mut SpdmState, buf:= *mut u8) -> isize { + // SAFETY: The opaque pointer will be directly from the `spdm_create()` + // function, so we can safely reconstruct it. + let state =3D unsafe { KBox::from_raw(spdm_state) }; + + let fmt =3D match CString::try_from_fmt(fmt!("{}\n", state.authenticat= ed)) { + Ok(f) =3D> f, + Err(_e) =3D> return 0, + }; + + // SAFETY: Calling a kernel C function with valid arguments + unsafe { bindings::sysfs_emit(buf, fmt.as_char_ptr()) as isize } +} + +/// Helper function to trigger a SPDM challenge +#[no_mangle] +pub unsafe extern "C" fn spdm_chall(state: &'static mut SpdmState) -> c_in= t { + if let Err(e) =3D state.challenge(state.provisioned_slots.trailing_zer= os() as u8, false) { + return e.to_errno() as c_int; + } + + 0 +} --=20 2.52.0