[PATCH v3] rust: pci: reject out-of-bounds IRQ vector indices

Sophon Z via B4 Relay posted 1 patch 3 weeks, 5 days ago
rust/kernel/pci/irq.rs | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
[PATCH v3] rust: pci: reject out-of-bounds IRQ vector indices
Posted by Sophon Z via B4 Relay 3 weeks, 5 days ago
From: Sophon Z <aiqubits@hotmail.com>

IrqVectorRegistration::index() accepts a usize and documents that
out-of-bounds indices return EINVAL, while pci_irq_vector() takes an
unsigned int.

Casting an index larger than u32::MAX wraps it before the PCI core can
validate it. In particular, u32::MAX + 1 becomes zero and can resolve to
the first allocated vector. Values that fit in u32 but exceed
MSI_MAX_INDEX can also reach msi_domain_get_virq() and trigger
WARN_ON_ONCE.

Check the index against the registration length before entering the C
API, and keep the usize-to-u32 conversion checked so the ABI boundary
does not rely on an unchecked cast.

Fixes: 2fb7755b0a7e ("rust: pci: resolve IRQ in index() and embed IrqRequest in IrqVector")

Signed-off-by: Sophon Z <aiqubits@hotmail.com>
---
Changes in v3:
- Check the index against the allocated vector count before entering the C API.
- Keep the checked usize-to-u32 conversion and document the C-side warning.
- Link to v2: https://patch.msgid.link/20260831-fix-pci-irq-vector-index-truncation-v2-1-4030ea7746a9@hotmail.com

Changes in v2:
- No code changes.
- Link to v1: https://patch.msgid.link/20260831-fix-pci-irq-vector-index-truncation-v1-1-d63217d99b67@hotmail.com
---
 rust/kernel/pci/irq.rs | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/pci/irq.rs b/rust/kernel/pci/irq.rs
index 6741046ec1c0..dfab323f26f7 100644
--- a/rust/kernel/pci/irq.rs
+++ b/rust/kernel/pci/irq.rs
@@ -151,8 +151,14 @@ pub fn irq_type(&self) -> IrqType {
     /// [`Self::len()`].
     #[inline]
     pub fn index(&self, index: usize) -> Result<IrqVector<'_>> {
+        if index >= self.len.get() {
+            return Err(EINVAL);
+        }
+
+        let index = u32::try_from(index).map_err(|_| EINVAL)?;
+
         // SAFETY: `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`.
-        let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), index as u32) };
+        let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), index) };
         if irq < 0 {
             return Err(Error::from_errno(irq));
         }

---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20260831-fix-pci-irq-vector-index-truncation-6752f3751a0d

Best regards,
--  
Sophon Z <aiqubits@hotmail.com>
Re: [PATCH v3] rust: pci: reject out-of-bounds IRQ vector indices
Posted by Gary Guo 3 weeks, 5 days ago
On Mon Aug 31, 2026 at 8:17 AM BST, Sophon Z via B4 Relay wrote:
> From: Sophon Z <aiqubits@hotmail.com>
>
> IrqVectorRegistration::index() accepts a usize and documents that
> out-of-bounds indices return EINVAL, while pci_irq_vector() takes an
> unsigned int.
>
> Casting an index larger than u32::MAX wraps it before the PCI core can
> validate it. In particular, u32::MAX + 1 becomes zero and can resolve to
> the first allocated vector. Values that fit in u32 but exceed
> MSI_MAX_INDEX can also reach msi_domain_get_virq() and trigger
> WARN_ON_ONCE.
>
> Check the index against the registration length before entering the C
> API, and keep the usize-to-u32 conversion checked so the ABI boundary
> does not rely on an unchecked cast.
>
> Fixes: 2fb7755b0a7e ("rust: pci: resolve IRQ in index() and embed IrqRequest in IrqVector")
>
> Signed-off-by: Sophon Z <aiqubits@hotmail.com>
> ---
> Changes in v3:
> - Check the index against the allocated vector count before entering the C API.
> - Keep the checked usize-to-u32 conversion and document the C-side warning.
> - Link to v2: https://patch.msgid.link/20260831-fix-pci-irq-vector-index-truncation-v2-1-4030ea7746a9@hotmail.com
>
> Changes in v2:
> - No code changes.
> - Link to v1: https://patch.msgid.link/20260831-fix-pci-irq-vector-index-truncation-v1-1-d63217d99b67@hotmail.com
> ---
>  rust/kernel/pci/irq.rs | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/rust/kernel/pci/irq.rs b/rust/kernel/pci/irq.rs
> index 6741046ec1c0..dfab323f26f7 100644
> --- a/rust/kernel/pci/irq.rs
> +++ b/rust/kernel/pci/irq.rs
> @@ -151,8 +151,14 @@ pub fn irq_type(&self) -> IrqType {
>      /// [`Self::len()`].
>      #[inline]
>      pub fn index(&self, index: usize) -> Result<IrqVector<'_>> {
> +        if index >= self.len.get() {
> +            return Err(EINVAL);
> +        }
> +
> +        let index = u32::try_from(index).map_err(|_| EINVAL)?;

Just having this line should be fine, no need for the length check above.

Alternatively, just have the check above, and do the cast, while documenting
that `len` fits u32 as invariant (which is always true given the length is
handed out by PCI core.

Best,
Gary

> +
>          // SAFETY: `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`.
> -        let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), index as u32) };
> +        let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), index) };
>          if irq < 0 {
>              return Err(Error::from_errno(irq));
>          }
>
> ---
> base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
> change-id: 20260831-fix-pci-irq-vector-index-truncation-6752f3751a0d
>
> Best regards,
> --  
> Sophon Z <aiqubits@hotmail.com>
Re: [PATCH v3] rust: pci: reject out-of-bounds IRQ vector indices
Posted by Miguel Ojeda 3 weeks, 5 days ago
On Mon, Aug 31, 2026 at 9:17 AM Sophon Z via B4 Relay
<devnull+aiqubits.hotmail.com@kernel.org> wrote:
>
> Fixes: 2fb7755b0a7e ("rust: pci: resolve IRQ in index() and embed IrqRequest in IrqVector")

The tag looks good, and there is no need for Cc: stable since this is
from yesterday's -rc1, so that is good.

Nit: we usually don't do newlines between tags.

> Signed-off-by: Sophon Z <aiqubits@hotmail.com>

Is this a known identity? Please see:

  https://docs.kernel.org/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin

> +        let index = u32::try_from(index).map_err(|_| EINVAL)?;

Do we need the `.map_err()`?

Thanks!

Cheers,
Miguel