From: sashiko-bot@kernel.org
To: alistair23@gmail.com
Cc: ojeda@kernel.org, linux-pci@vger.kernel.org
Subject: Re: [PATCH 12/18] lib: rspdm: Support SPDM get_capabilities
Date: Fri, 08 May 2026 04:05:32 +0000 [thread overview]
Message-ID: <20260508040533.59B12C2BCB0@smtp.kernel.org> (raw)
In-Reply-To: <20260508031710.514574-13-alistair.francis@wdc.com>
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Incorrect request and response buffer sizes for SPDM 1.0 and 1.1 GET_CAPABILITIES exchanges.
- [High] Missing little-endian to native-endian conversion for `response.data_transfer_size`.
- [High] `GetCapabilitiesRsp::validate` unconditionally rejects valid SPDM 1.0 responses.
- [High] Creating a mutable slice (`&mut [u8]`) from uninitialized memory causes Undefined Behavior.
- [Medium] Vector length bounds check does not prevent out-of-bounds capacity overflow if transport returns a larger length.
--
commit 1fee665b22607bae77947f31f9d54552e86f77d2
Author: Alistair Francis <alistair@alistair23.me>
lib: rspdm: Support SPDM get_capabilities
This commit adds support for the GET_CAPABILITIES SPDM command. It negotiates
responder capabilities and updates the SPDM state.
> diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs
> index 26b90942b298d..e7119ffa9a691 100644
> --- a/lib/rspdm/state.rs
> +++ b/lib/rspdm/state.rs
[ ... ]
> @@ -293,4 +304,67 @@ pub(crate) fn get_version(&mut self) -> Result<(), Error> {
>
> Ok(())
> }
> +
> + /// Obtain the supported capabilities from an SPDM session and store the
> + /// information in the `SpdmState`.
> + pub(crate) fn get_capabilities(&mut self) -> Result<(), Error> {
> + let mut request = GetCapabilitiesReq::default();
> + request.version = self.version;
> +
> + let (req_sz, rsp_sz) = match self.version {
> + SPDM_VER_10 => (4, 8),
> + SPDM_VER_11 => (8, 8),
Are these buffer sizes correct for SPDM 1.0 and 1.1?
According to the SPDM specification, both SPDM 1.0 and 1.1 responses include
a mandatory 4-byte flags field, which would make their true size 12 bytes.
Furthermore, the 1.1 request is also 12 bytes.
Additionally, since GetCapabilitiesRsp::validate() enforces a minimum length
of 12 bytes for SPDM 1.1, wouldn't an 8-byte response unconditionally fail
validation with EINVAL?
> + _ => {
> + request.data_transfer_size = self.transport_sz.to_le();
> + request.max_spdm_msg_size = request.data_transfer_size;
> +
> + (
> + core::mem::size_of::<GetCapabilitiesReq>(),
> + core::mem::size_of::<GetCapabilitiesRsp>(),
> + )
> + }
> + };
> +
> + // SAFETY: `request` is repr(C) and packed, so we can convert it to a slice
> + let request_buf = unsafe { from_raw_parts_mut(&mut request as *mut _ as *mut u8, req_sz) };
> +
> + let mut response_vec: KVec<u8> = KVec::with_capacity(rsp_sz, GFP_KERNEL)?;
> + // SAFETY: `response_vec` is rsp_sz length, initialised, aligned
> + // and won't be mutated
> + let response_buf = unsafe { from_raw_parts_mut(response_vec.as_mut_ptr(), rsp_sz) };
Does creating a mutable slice from this vector trigger undefined behavior?
The safety comment mentions the memory is initialized, but
KVec::with_capacity() allocates uninitialized capacity. Creating a &mut [u8]
reference to uninitialized memory is undefined behavior in Rust.
Should this use something like KVec::resize() or MaybeUninit to ensure the
memory is initialized before slicing?
> +
> + let rc = self.spdm_exchange(request_buf, response_buf)?;
> +
> + if rc < (rsp_sz as i32) {
> + pr_err!("Truncated capabilities response\n");
> + to_result(-(bindings::EIO as i32))?;
> + }
> +
> + // SAFETY: `rc` is the length of data read, which will be smaller
> + // or the same as the capacity of the vector
> + unsafe { response_vec.inc_len(rc as usize) };
The code checks if rc is truncated, but what if a buggy transport driver
returns an rc strictly larger than rsp_sz?
If that happens, response_vec.inc_len() would force the length beyond the
allocated capacity, leading to out-of-bounds memory accesses. Should there
be a check to ensure rc <= rsp_sz?
> +
> + let response: &mut GetCapabilitiesRsp =
> + Untrusted::new_mut(&mut response_vec).validate_mut()?;
> +
> + self.rsp_caps = u32::from_le(response.flags);
> + if (self.rsp_caps & SPDM_RSP_MIN_CAPS) != SPDM_RSP_MIN_CAPS {
> + pr_err!(
> + "{:#x} capabilities are supported, which don't meet required {:#x}",
> + self.rsp_caps,
> + SPDM_RSP_MIN_CAPS
> + );
> + to_result(-(bindings::EPROTONOSUPPORT as i32))?;
> + }
> +
> + if self.version >= SPDM_VER_12 {
> + if response.data_transfer_size < SPDM_MIN_DATA_TRANSFER_SIZE {
Is an endianness conversion missing here?
The SPDM protocol transmits multi-byte fields in little-endian format, but
response.data_transfer_size is read directly here.
On big-endian architectures, could this be read as an artificially massive
integer and bypass the minimum size bounds check? Should this use
u32::from_le() like the flags field above it?
> + pr_err!("Malformed capabilities response\n");
> + to_result(-(bindings::EPROTO as i32))?;
> + }
> + self.transport_sz = self.transport_sz.min(response.data_transfer_size);
> + }
> +
> + Ok(())
> + }
> }
> diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs
> index 8f45bafd4d691..7dc55882c8809 100644
> --- a/lib/rspdm/validator.rs
> +++ b/lib/rspdm/validator.rs
[ ... ]
> @@ -141,3 +145,94 @@ fn validate(unvalidated: &mut Unvalidated<KVec<u8>>) -> Result<Self, Self::Err>
> Ok(rsp)
> }
> }
[ ... ]
> +impl Validate<&mut Unvalidated<KVec<u8>>> for &mut GetCapabilitiesRsp {
> + type Err = Error;
> +
> + fn validate(unvalidated: &mut Unvalidated<KVec<u8>>) -> Result<Self, Self::Err> {
> + let raw = unvalidated.raw_mut();
> +
> + if raw.len() < mem::size_of::<GetCapabilitiesRsp>() {
> + let version = *(raw.get(0).ok_or(ENOMEM))?;
> + let version_1_1_len = mem::size_of::<GetCapabilitiesRsp>()
> + - mem::size_of::<__IncompleteArrayField<__le16>>()
> + - mem::size_of::<u32>()
> + - mem::size_of::<u32>();
> +
> + if version == SPDM_VER_11 && raw.len() == version_1_1_len {
> + // Version 1.1 of the spec doesn't include all of the fields
> + // So let's extend the KVec with 0s so we can cast the
> + // vector to GetCapabilitiesRsp
> +
> + for _i in version_1_1_len..mem::size_of::<GetCapabilitiesRsp>() {
> + raw.push(0, GFP_KERNEL)?;
> + }
> + } else {
> + return Err(EINVAL);
> + }
Does this unintentionally reject valid SPDM 1.0 responses?
If the SPDM session negotiated SPDM 1.0, the response will carry the
SPDM_VER_10 version byte and have a shorter response length. Because this
strictly checks against SPDM_VER_11, won't SPDM 1.0 responses fall through
to the else branch and unconditionally return EINVAL?
> + }
> +
> + let ptr = raw.as_mut_ptr();
> + // CAST: `GetCapabilitiesRsp` only contains integers and has `repr(C)`.
> + let ptr = ptr.cast::<GetCapabilitiesRsp>();
> + // SAFETY: `ptr` came from a reference and the cast above is valid.
> + let rsp: &mut GetCapabilitiesRsp = unsafe { &mut *ptr };
> +
> + Ok(rsp)
> + }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260508031710.514574-1-alistair.francis@wdc.com?part=12
next prev parent reply other threads:[~2026-05-08 4:05 UTC|newest]
Thread overview: 35+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-08 3:16 [PATCH 00/18] lib: Rust implementation of SPDM alistair23
2026-05-08 3:16 ` [PATCH 01/18] rust: add untrusted data abstraction alistair23
2026-05-08 3:52 ` sashiko-bot
2026-05-08 5:17 ` Dirk Behme
2026-05-08 3:16 ` [PATCH 02/18] X.509: Make certificate parser public alistair23
2026-05-08 3:45 ` sashiko-bot
2026-05-08 3:16 ` [PATCH 03/18] X.509: Parse Subject Alternative Name in certificates alistair23
2026-05-08 3:16 ` [PATCH 04/18] X.509: Move certificate length retrieval into new helper alistair23
2026-05-08 3:39 ` sashiko-bot
2026-05-08 3:16 ` [PATCH 05/18] rust: add bindings for hash.h alistair23
2026-05-08 3:43 ` sashiko-bot
2026-05-08 3:16 ` [PATCH 06/18] rust: error: impl From<FromBytesWithNulError> for Kernel Error alistair23
2026-05-08 3:51 ` sashiko-bot
2026-05-08 3:16 ` [PATCH 07/18] lib: rspdm: Initial commit of Rust SPDM alistair23
2026-05-08 3:41 ` sashiko-bot
2026-05-08 3:17 ` [PATCH 08/18] PCI/TSM: Support connecting to PCIe CMA devices alistair23
2026-05-08 3:17 ` [PATCH 09/18] PCI/CMA: Add a PCI TSM CMA driver using SPDM alistair23
2026-05-08 5:02 ` sashiko-bot
2026-05-08 3:17 ` [PATCH 10/18] PCI/CMA: Validate Subject Alternative Name in certificates alistair23
2026-05-08 3:58 ` sashiko-bot
2026-05-08 3:17 ` [PATCH 11/18] lib: rspdm: Support SPDM get_version alistair23
2026-05-08 3:50 ` sashiko-bot
2026-05-08 3:17 ` [PATCH 12/18] lib: rspdm: Support SPDM get_capabilities alistair23
2026-05-08 4:05 ` sashiko-bot [this message]
2026-05-08 3:17 ` [PATCH 13/18] lib: rspdm: Support SPDM negotiate_algorithms alistair23
2026-05-08 4:05 ` sashiko-bot
2026-05-08 3:17 ` [PATCH 14/18] lib: rspdm: Support SPDM get_digests alistair23
2026-05-08 4:06 ` sashiko-bot
2026-05-08 3:17 ` [PATCH 15/18] lib: rspdm: Support SPDM get_certificate alistair23
2026-05-08 4:23 ` sashiko-bot
2026-05-08 3:17 ` [PATCH 16/18] lib: rspdm: Support SPDM certificate validation alistair23
2026-05-08 4:25 ` sashiko-bot
2026-05-08 3:17 ` [PATCH 17/18] rust: allow extracting the buffer from a CString alistair23
2026-05-08 3:17 ` [PATCH 18/18] lib: rspdm: Support SPDM challenge alistair23
2026-05-08 4:19 ` sashiko-bot
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=20260508040533.59B12C2BCB0@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=alistair23@gmail.com \
--cc=linux-pci@vger.kernel.org \
--cc=ojeda@kernel.org \
--cc=sashiko@lists.linux.dev \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox