Linux PCI subsystem development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Zhi Wang" <zhiw@nvidia.com>
Cc: linux-pci@vger.kernel.org, ojeda@kernel.org
Subject: Re: [PATCH v4 1/1] rust: pci: add extended capability and SR-IOV support
Date: Tue, 14 Jul 2026 17:20:03 +0000	[thread overview]
Message-ID: <20260714172004.306FA1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260714165827.2937960-2-zhiw@nvidia.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Critical] The `ConfigSpace` abstraction cannot store a capability offset, causing extended capability reads/writes to silently target the standard PCI header.
- [High] Data structure layout bug conflates `func_dep_link` with a reserved hardware register.
- [High] Double-fetch of the VF BAR register in `read_vf_bar64` introduces a Time-of-Check to Time-of-Use (TOCTOU) vulnerability with untrusted hardware.
--

--- Patch [1]: [PATCH v4 1/1] rust: pci: add extended capability and SR-IOV support ---
commit 12bd7c1ee008a72446697b452c0ce723422b22e0
Author: Zhi Wang <zhiw@nvidia.com>

rust: pci: add extended capability and SR-IOV support

Rust PCI drivers have no typed interface for locating and accessing PCIe
extended capabilities. The SR-IOV extended capability describes VF topology
and VF BARs. Expose this information through the Rust PCI abstraction so
drivers can use the existing typed configuration-space accessors.

Link: https://lore.kernel.org/rust-for-linux/20260409185254.3869808-1-zhiw@nvidia.com/

> diff --git a/rust/kernel/pci/cap.rs b/rust/kernel/pci/cap.rs
> new file mode 100644
> index 0000000000000..a37c96d3d5395
> --- /dev/null
> +++ b/rust/kernel/pci/cap.rs
> @@ -0,0 +1,209 @@

[ ... ]

> +    pub fn find_ext_capability<C: ExtCapability>(&self) -> Result<ConfigSpace<'a, C>> {
> +        let offset = usize::from(
> +            // SAFETY: `self.pdev` is valid by the type invariant of `ConfigSpace`.
> +            unsafe {
> +                bindings::pci_find_ext_capability(self.pdev.as_raw(), i32::from(C::ID.as_raw()))
> +            },
> +        );
> +
> +        if offset == 0 {
> +            return Err(ENODEV);
> +        }
> +
> +        let size = self.calculate_ext_cap_size(offset);
> +
> +        let base = ConfigSpaceBackend::as_ptr(*self)
> +            .cast::<u8>()
> +            .wrapping_add(offset);
> +        let ptr = Region::<0>::ptr_try_from_raw_parts_mut(base, size)?;
> +
> +        // SAFETY: `offset` was returned by `pci_find_ext_capability`, and
> +        // `calculate_ext_cap_size` bounds `ptr` at the next capability or the end of the extended
> +        // configuration space. `ptr_try_from_raw_parts_mut` verified the region layout.
> +        let capability = unsafe { ConfigSpaceBackend::project_view(*self, ptr) };
> +
> +        capability.try_cast::<C>()
> +    }

[Severity: Critical]
Does the ConfigSpace abstraction have a way to store the runtime capability
base offset?

It looks like ConfigSpace only holds a pdev reference and a PhantomData marker,
physically lacking a field to store the runtime capability base offset.

If the Io trait implementation for ConfigSpace returns 0 for the address,
wouldn't reading or writing to this view silently target the standard PCI
configuration registers at offset 0, ignoring the capability's actual offset?

[ ... ]

> +/// SR-IOV register layout per PCIe spec (64 bytes starting at cap offset).
> +#[repr(C)]
> +#[derive(FromBytes, IntoBytes)]
> +pub struct ExtSriovRegs {
> +    /// Extended capability header.
> +    pub header: u32,
> +    /// SR-IOV capabilities.
> +    pub cap: u32,
> +    /// SR-IOV control.
> +    pub ctrl: u16,
> +    /// SR-IOV status.
> +    pub status: u16,
> +    /// Initial VFs.
> +    pub initial_vfs: u16,
> +    /// Total VFs.
> +    pub total_vfs: u16,
> +    /// Number of VFs.
> +    pub num_vfs: u16,
> +    /// Function dependency link.
> +    pub func_dep_link: u16,

[Severity: High]
Is it safe to declare the 8-bit Function Dependency Link register as a
16-bit field?

This conflates it with the adjacent reserved byte at offset 0x13. On
little-endian architectures, reading this might pull non-zero reserved bits
into the upper 8 bits. Furthermore, writing it could overwrite the reserved
bits, potentially corrupting hardware state.

> +    /// First VF offset.
> +    pub vf_offset: u16,
> +    /// VF stride.
> +    pub vf_stride: u16,

[ ... ]

> +impl ConfigSpace<'_, ExtSriovRegs> {
> +    /// Returns `true` if the VF BAR at `bar_index` is a 64-bit memory BAR.
> +    #[inline]
> +    pub fn is_vf_bar_64bit(&self, bar_index: usize) -> Result<bool> {
> +        if bar_index >= NUM_VF_BARS {
> +            return Err(EINVAL);
> +        }
> +
> +        let bar = crate::io_read!(*self, .vf_bar[try: bar_index]);
> +
> +        Ok(bar & bindings::PCI_BASE_ADDRESS_SPACE == 0
> +            && bar & bindings::PCI_BASE_ADDRESS_MEM_TYPE_MASK
> +                == bindings::PCI_BASE_ADDRESS_MEM_TYPE_64)
> +    }
> +
> +    /// Reads a 64-bit VF BAR from two consecutive 32-bit slots.
> +    #[inline]
> +    pub fn read_vf_bar64(&self, bar_index: usize) -> Result<u64> {
> +        if !self.is_vf_bar_64bit(bar_index)? {

[Severity: High]
Does this introduce a Time-of-Check to Time-of-Use (TOCTOU) issue when
dealing with untrusted hardware?

is_vf_bar_64bit() performs a PCI config space read to check if the BAR is
a 64-bit memory type. Immediately after, read_vf_bar64() performs a second
read on the exact same register to extract the lower 32 bits:

> +            return Err(EINVAL);
> +        }
> +
> +        let high_index = bar_index.checked_add(1).ok_or(EINVAL)?;
> +        if high_index >= NUM_VF_BARS {
> +            return Err(EINVAL);
> +        }
> +
> +        let low = crate::io_read!(*self, .vf_bar[try: bar_index]);

Could a malicious device return a 64-bit indicator during the first read,
but a different 32-bit payload during the second read? It might be safer
to read the register exactly once, validate the read value, and then use
that same value.

> +        let high = crate::io_read!(*self, .vf_bar[try: high_index]);
> +        Ok((u64::from(high) << 32) | u64::from(low))
> +    }
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260714165827.2937960-2-zhiw@nvidia.com?part=1

      reply	other threads:[~2026-07-14 17:20 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-14 16:58 [PATCH v4 0/1] Rust PCI capability infrastructure and SR-IOV support Zhi Wang
2026-07-14 16:58 ` [PATCH v4 1/1] rust: pci: add extended capability " Zhi Wang
2026-07-14 17:20   ` sashiko-bot [this message]

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=20260714172004.306FA1F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=ojeda@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=zhiw@nvidia.com \
    /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