Linux CXL
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: alistair23@gmail.com
Cc: ojeda@kernel.org, linux-cxl@vger.kernel.org, linux-pci@vger.kernel.org
Subject: Re: [PATCH v2 18/21] lib: rspdm: Support SPDM get_certificate
Date: Tue, 23 Jun 2026 05:20:24 +0000	[thread overview]
Message-ID: <20260623052024.5FD6B1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260623045406.2589547-19-alistair.francis@wdc.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

New issues:
- [High] Integer overflow on the `offset` accumulator allows a malicious SPDM responder to force an infinite memory allocation loop or kernel panic.
- [High] The `Validate` implementation for `&GetCertificateRsp` and `get_cert_exchange` use elided lifetimes, creating a memory safety soundness hole.
- [High] The `request.length` field is cast to `usize` for buffer resizing after being converted to little-endian, severely truncating responses on big-endian architectures.

Pre-existing issues:
- [High] Stack-allocated request buffers are passed to a generic transport callback, violating kernel stack DMA rules.
--

commit d1fb5590c1a4a5cf4a63bb011d83ed5975824d75
Author: Alistair Francis <alistair@alistair23.me>

lib: rspdm: Support SPDM get_certificate

This commit adds support for the GET_CERTIFICATE SPDM command. The kernel
sends requests to the responder and iterates over the returned certificates,
with validation planned for a future patch.

> diff --git a/lib/rspdm/state.rs b/lib/rspdm/state.rs
> --- a/lib/rspdm/state.rs
> +++ b/lib/rspdm/state.rs
[ ... ]
> @@ -686,4 +699,136 @@ pub(crate) fn get_digests(&mut self) -> Result<(), Error> {
[ ... ]
> +    fn get_cert_exchange(
> +        &mut self,
> +        request_buf: &mut [u8],
> +        response_vec: &mut KVec<u8>,
> +    ) -> Result<&GetCertificateRsp, Error> {

[Severity: High]
Does the elided lifetime here tie the returned reference to self rather than
the response_vec buffer?

Standard Rust method lifetime elision binds this detached lifetime to the
mutable self reference. This completely disables borrow checking for the
underlying response_vec, allowing safe callers to resize, mutate, or drop
the heap allocation while retaining a pointer to it, which could trigger a
use-after-free.

> +        let len = self.spdm_exchange(request_buf, response_vec.as_mut_slice())? as usize;
[ ... ]
> +    pub(crate) fn get_certificate(&mut self, slot: u8) -> Result<(), Error> {
> +        let mut request = GetCertificateReq::default();
> +        request.version = self.version;
> +        request.param1 = slot;
> +
> +        let req_sz = core::mem::size_of::<GetCertificateReq>();
> +        let rsp_sz = (core::mem::size_of::<GetCertificateRsp>() as u32 + u16::MAX as u32)
> +            .min(self.transport_sz) as usize;
> +
> +        request.offset = 0;
> +        request.length = ((rsp_sz - core::mem::size_of::<GetCertificateRsp>()) as u16).to_le();
> +
> +        // 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) };

[Severity: High]
This is a pre-existing issue, but does passing stack memory to a generic
transport callback violate kernel stack DMA rules?

The GetCertificateReq structure is allocated on the kernel stack and a
mutable slice pointing to this memory is passed to spdm_exchange(). Using
stack memory for hardware I/O is problematic because stacks may be virtually
mapped and lack physical contiguity or proper cacheline alignment.
Attempting to map this stack memory for DMA will cause mapping errors or
memory corruption.

> +
> +        let mut response_vec: KVec<u8> = KVec::from_elem(0u8, rsp_sz, GFP_KERNEL)?;
> +
> +        let response = self.get_cert_exchange(request_buf, &mut response_vec)?;
[ ... ]
> +        let mut offset: u16 = u16::from_le(response.portion_length);
> +        let mut remainder_length = u16::from_le(response.remainder_length) as usize;
> +
> +        while remainder_length > 0 {
> +            request.offset = offset.to_le();
> +            request.length =
> +                ((remainder_length.min(rsp_sz - core::mem::size_of::<GetCertificateRsp>())) as u16)
> +                    .to_le();
> +
> +            let request_buf =
> +                unsafe { from_raw_parts_mut(&mut request as *mut _ as *mut u8, req_sz) };
> +
> +            response_vec.resize(
> +                request.length as usize + core::mem::size_of::<GetCertificateRsp>(),

[Severity: High]
Will this resize operation truncate the response buffer on big-endian
architectures?

The request.length field is converted to little-endian using .to_le(), but
then immediately cast to usize to calculate the new size for resize().
On big-endian architectures, casting this byte-swapped value back to a
native usize yields a drastically incorrect size. This will truncate the
incoming response to the undersized buffer and reliably fail certificate
validation.

> +                0,
> +                GFP_KERNEL,
> +            )?;
> +
> +            let response = self.get_cert_exchange(request_buf, &mut response_vec)?;
[ ... ]
> +            certs_buf.extend_from_slice(
> +                &response_vec[8..(8 + u16::from_le(response.portion_length) as usize)],
> +                GFP_KERNEL,
> +            )?;
> +            offset += u16::from_le(response.portion_length);

[Severity: High]
Could this offset calculation overflow if a responder provides crafted
portion_length and remainder_length values?

The offset variable is explicitly typed as a u16. If an untrusted SPDM
responder manipulates the response lengths, offset += portion_length can
overflow. On systems with overflow checks enabled, this immediately triggers
a panic. Without overflow checks, offset wraps around, allowing the loop:

while remainder_length > 0 {
    ...
}

to run infinitely, continuously appending untrusted data to certs_buf until
all kernel memory is exhausted.

> +            remainder_length = u16::from_le(response.remainder_length) as usize;
> +        }

> diff --git a/lib/rspdm/validator.rs b/lib/rspdm/validator.rs
> --- a/lib/rspdm/validator.rs
> +++ b/lib/rspdm/validator.rs
[ ... ]
> @@ -426,3 +427,58 @@ fn validate(unvalidated: &[u8]) -> Result<Self, Self::Err> {
[ ... ]
> +impl Validate<Untrusted<&[u8]>> for &GetCertificateRsp {

[Severity: High]
Does this Validate implementation fail to link the input slice's lifetime
to the output reference's lifetime?

Because explicit lifetimes are missing, the output reference is detached
from the input slice. Inside the validate() function, an unsafe block
dereferences a pointer into this unconstrained reference, breaking Rust's
safety guarantees.

> +    type Err = Error;
> +
> +    fn validate(unvalidated: &[u8]) -> Result<Self, Self::Err> {
> +        if unvalidated.len() < mem::size_of::<GetCertificateRsp>() {
> +            return Err(EINVAL);
> +        }
> +
> +        let ptr = unvalidated.as_ptr();
> +        // CAST: `GetCertificateRsp` only contains integers and has `repr(C)`.
> +        let ptr = ptr.cast::<GetCertificateRsp>();
> +        // SAFETY: `ptr` came from a reference and the cast above is valid.
> +        let rsp: &GetCertificateRsp = unsafe { &*ptr };
> +
> +        Ok(rsp)
> +    }
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260623045406.2589547-1-alistair.francis@wdc.com?part=18

  reply	other threads:[~2026-06-23  5:20 UTC|newest]

Thread overview: 43+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-23  4:53 [PATCH v2 00/21] lib: Rust implementation of SPDM alistair23
2026-06-23  4:53 ` [PATCH v2 01/21] rust: transmute: add `cast_slice[_mut]` functions alistair23
2026-06-23  5:05   ` sashiko-bot
2026-06-23  4:53 ` [PATCH v2 02/21] rust: create basic untrusted data API alistair23
2026-06-23  5:09   ` sashiko-bot
2026-06-23  4:53 ` [PATCH v2 03/21] rust: validate: add `Validate` trait alistair23
2026-06-23  5:10   ` sashiko-bot
2026-06-23  4:53 ` [PATCH v2 04/21] X.509: Make certificate parser public alistair23
2026-06-23  5:03   ` sashiko-bot
2026-06-23  4:53 ` [PATCH v2 05/21] X.509: Parse Subject Alternative Name in certificates alistair23
2026-06-23  5:07   ` sashiko-bot
2026-06-23  4:53 ` [PATCH v2 06/21] X.509: Move certificate length retrieval into new helper alistair23
2026-06-23  5:02   ` sashiko-bot
2026-06-23  4:53 ` [PATCH v2 07/21] rust: add bindings for hash.h alistair23
2026-06-23  7:01   ` sashiko-bot
2026-06-23  4:53 ` [PATCH v2 08/21] rust: error: impl From<FromBytesWithNulError> for Kernel Error alistair23
2026-06-23  5:01   ` sashiko-bot
2026-06-23  4:53 ` [PATCH v2 09/21] lib: rspdm: Initial commit of Rust SPDM alistair23
2026-06-23  5:09   ` sashiko-bot
2026-06-23  4:53 ` [PATCH v2 10/21] PCI/TSM: Rename pf0 to host alistair23
2026-06-23  5:12   ` sashiko-bot
2026-06-23  4:53 ` [PATCH v2 11/21] PCI/TSM: Support connecting to PCIe CMA devices alistair23
2026-06-23  5:16   ` sashiko-bot
2026-06-23  4:53 ` [PATCH v2 12/21] PCI/CMA: Add a PCI TSM CMA driver using SPDM alistair23
2026-06-23  5:07   ` sashiko-bot
2026-06-23  4:53 ` [PATCH v2 13/21] PCI/CMA: Validate Subject Alternative Name in certificates alistair23
2026-06-23  5:07   ` sashiko-bot
2026-06-23  4:53 ` [PATCH v2 14/21] lib: rspdm: Support SPDM get_version alistair23
2026-06-23  5:10   ` sashiko-bot
2026-06-23  4:54 ` [PATCH v2 15/21] lib: rspdm: Support SPDM get_capabilities alistair23
2026-06-23  5:09   ` sashiko-bot
2026-06-23  4:54 ` [PATCH v2 16/21] lib: rspdm: Support SPDM negotiate_algorithms alistair23
2026-06-23  5:17   ` sashiko-bot
2026-06-23  4:54 ` [PATCH v2 17/21] lib: rspdm: Support SPDM get_digests alistair23
2026-06-23  5:17   ` sashiko-bot
2026-06-23  4:54 ` [PATCH v2 18/21] lib: rspdm: Support SPDM get_certificate alistair23
2026-06-23  5:20   ` sashiko-bot [this message]
2026-06-23  4:54 ` [PATCH v2 19/21] lib: rspdm: Support SPDM certificate validation alistair23
2026-06-23  5:19   ` sashiko-bot
2026-06-23  4:54 ` [PATCH v2 20/21] rust: allow extracting the buffer from a CString alistair23
2026-06-23  5:13   ` sashiko-bot
2026-06-23  4:54 ` [PATCH v2 21/21] lib: rspdm: Support SPDM challenge alistair23
2026-06-23  5:21   ` 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=20260623052024.5FD6B1F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=alistair23@gmail.com \
    --cc=linux-cxl@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=ojeda@kernel.org \
    --cc=sashiko-reviews@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