All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] misc: pci_endpoint_test: Add WARN_ON() to detect broken EPC drivers
@ 2026-07-29 20:28 Niklas Cassel
  2026-07-29 20:38 ` sashiko-bot
  2026-07-29 20:49 ` DWC eDMA weirdness Niklas Cassel
  0 siblings, 2 replies; 3+ messages in thread
From: Niklas Cassel @ 2026-07-29 20:28 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Krzysztof Wilczyński, Bjorn Helgaas
  Cc: linux-pci, Koichiro Den, Niklas Cassel

Getting two IRQs when we are only expecting a single IRQ is a very
serious error, and suggests that something is very wrong in the EPC
driver.

Scream loudly so that EPC driver developers can see the problem,
which might otherwise be very hard to detect.

Signed-off-by: Niklas Cassel <cassel@kernel.org>
---
 drivers/misc/pci_endpoint_test.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c
index 3635741c3e7a..a212e53654c6 100644
--- a/drivers/misc/pci_endpoint_test.c
+++ b/drivers/misc/pci_endpoint_test.c
@@ -178,6 +178,19 @@ static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id)
 	if (reg & STATUS_IRQ_RAISED) {
 		test->last_irq = irq;
 		complete(&test->irq_raised);
+		/*
+		 * The endpoint test driver performs all testing sequentially.
+		 * This means that test->irq_raised.done should never exceed 1.
+		 * If it does, then we received two IRQs in a row, without a
+		 * successful wait_for_completion_timeout() call in between.
+		 *
+		 * While complete() increases test->irq_raised.done by one,
+		 * wait_for_completion_timeout() reduces test->irq_raised.done
+		 * by one on success.
+		 *
+		 * Please debug your EPC driver if you see this warning.
+		 */
+		WARN_ON(test->irq_raised.done > 1);
 	}
 
 	return IRQ_HANDLED;
-- 
2.55.0


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] misc: pci_endpoint_test: Add WARN_ON() to detect broken EPC drivers
  2026-07-29 20:28 [PATCH] misc: pci_endpoint_test: Add WARN_ON() to detect broken EPC drivers Niklas Cassel
@ 2026-07-29 20:38 ` sashiko-bot
  2026-07-29 20:49 ` DWC eDMA weirdness Niklas Cassel
  1 sibling, 0 replies; 3+ messages in thread
From: sashiko-bot @ 2026-07-29 20:38 UTC (permalink / raw)
  To: Niklas Cassel; +Cc: linux-pci

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

New issues:
- [High] The newly added `WARN_ON` can be trivially triggered by shared interrupts or untrusted hardware, leading to a Denial of Service (host panic) if `panic_on_warn` is enabled.
- [Medium] Direct, lockless read of the internal `done` field in `struct completion` causes a data race, violates API encapsulation, and relies on a flawed TOCTOU heuristic.

Pre-existing issues:
- [High] The shared IRQ handler unconditionally returns `IRQ_HANDLED` even when the interrupt was not generated by this device.
- [High] Use-after-free of `struct pci_endpoint_test` when the PCI device is unbound while a file descriptor is still open.
--

--- Patch [1]: [PATCH] misc: pci_endpoint_test: Add WARN_ON() to detect broken EPC drivers ---
commit e4f6d6484e68c6a478412d97f6734cf8590a6f97
Author: Niklas Cassel <cassel@kernel.org>

misc: pci_endpoint_test: Add WARN_ON() to detect broken EPC drivers

This commit adds a WARN_ON check to the IRQ handler in the PCI endpoint
test driver. It aims to detect if the driver receives multiple interrupts
sequentially without a successful wait in between.

> diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c
> index 3635741c3e7a7..a212e53654c68 100644
> --- a/drivers/misc/pci_endpoint_test.c
> +++ b/drivers/misc/pci_endpoint_test.c
> @@ -178,6 +178,19 @@ static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id)
>  	if (reg & STATUS_IRQ_RAISED) {
>  		test->last_irq = irq;
>  		complete(&test->irq_raised);
> +		/*
> +		 * The endpoint test driver performs all testing sequentially.
> +		 * This means that test->irq_raised.done should never exceed 1.
> +		 * If it does, then we received two IRQs in a row, without a
> +		 * successful wait_for_completion_timeout() call in between.
> +		 *
> +		 * While complete() increases test->irq_raised.done by one,
> +		 * wait_for_completion_timeout() reduces test->irq_raised.done
> +		 * by one on success.
> +		 *
> +		 * Please debug your EPC driver if you see this warning.
> +		 */
> +		WARN_ON(test->irq_raised.done > 1);

[Severity: Medium]
Does this direct, lockless read of the completion structure's internal state
introduce a data race?

If wait_for_completion_timeout() executes concurrently on another CPU between
the complete() call and this check, it could decrement done back to 0. Would
this window result in missing the duplicate IRQ this is attempting to detect?

[Severity: High]
Can this warning be trivially triggered by shared interrupts or untrusted
hardware?

If the driver requests its IRQ with IRQF_SHARED, an interrupt from another
device could cause this handler to execute. If it reads a stale
STATUS_IRQ_RAISED bit, could repeated shared interrupts trigger this WARN_ON
and potentially panic the host if panic_on_warn is enabled?

>  	}
>  
>  	return IRQ_HANDLED;

[Severity: High]
This isn't a bug introduced by this patch, but does this function
unconditionally return IRQ_HANDLED even when the interrupt wasn't generated
by this device?

If the status register does not have STATUS_IRQ_RAISED set, bypassing the
internal block, returning IRQ_HANDLED instead of IRQ_NONE could mask
unhandled interrupts from other devices sharing the same IRQ line.

[ ... ]

[Severity: High]
This is a pre-existing issue, but does using devm_kzalloc() for the state
structure expose a use-after-free regression during device unbind?

If the PCI device is unbound, devm_kzalloc() frees the struct
pci_endpoint_test upon return from pci_endpoint_test_remove(). However, if
an application still holds the device node open, could a subsequent
ioctl() access the freed pointer when resolving the private data?

drivers/misc/pci_endpoint_test.c:pci_endpoint_test_ioctl() {
	...
	struct pci_endpoint_test *test = to_endpoint_test(file->private_data);
	struct pci_dev *pdev = test->pdev;
	
	mutex_lock(&test->mutex);
	...
}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260729202821.1153443-2-cassel@kernel.org?part=1

^ permalink raw reply	[flat|nested] 3+ messages in thread

* DWC eDMA weirdness
  2026-07-29 20:28 [PATCH] misc: pci_endpoint_test: Add WARN_ON() to detect broken EPC drivers Niklas Cassel
  2026-07-29 20:38 ` sashiko-bot
@ 2026-07-29 20:49 ` Niklas Cassel
  1 sibling, 0 replies; 3+ messages in thread
From: Niklas Cassel @ 2026-07-29 20:49 UTC (permalink / raw)
  To: Koichiro Den
  Cc: linux-pci, Manivannan Sadhasivam, Krzysztof Wilczyński,
	Bjorn Helgaas

On Wed, Jul 29, 2026 at 10:28:22PM +0200, Niklas Cassel wrote:
> Getting two IRQs when we are only expecting a single IRQ is a very
> serious error, and suggests that something is very wrong in the EPC
> driver.
> 
> Scream loudly so that EPC driver developers can see the problem,
> which might otherwise be very hard to detect.
> 
> Signed-off-by: Niklas Cassel <cassel@kernel.org>
> ---
>  drivers/misc/pci_endpoint_test.c | 13 +++++++++++++
>  1 file changed, 13 insertions(+)
> 
> diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c
> index 3635741c3e7a..a212e53654c6 100644
> --- a/drivers/misc/pci_endpoint_test.c
> +++ b/drivers/misc/pci_endpoint_test.c
> @@ -178,6 +178,19 @@ static irqreturn_t pci_endpoint_test_irqhandler(int irq, void *dev_id)
>  	if (reg & STATUS_IRQ_RAISED) {
>  		test->last_irq = irq;
>  		complete(&test->irq_raised);
> +		/*
> +		 * The endpoint test driver performs all testing sequentially.
> +		 * This means that test->irq_raised.done should never exceed 1.
> +		 * If it does, then we received two IRQs in a row, without a
> +		 * successful wait_for_completion_timeout() call in between.
> +		 *
> +		 * While complete() increases test->irq_raised.done by one,
> +		 * wait_for_completion_timeout() reduces test->irq_raised.done
> +		 * by one on success.
> +		 *
> +		 * Please debug your EPC driver if you see this warning.
> +		 */
> +		WARN_ON(test->irq_raised.done > 1);
>  	}
>  
>  	return IRQ_HANDLED;
> -- 
> 2.55.0
> 

Hello Koichiro,

I created this patch in response to a problem that I saw on v7.2-rc5.

When running the pci_endpoint selftest:
# /pcitest
(snip)
#  RUN           pcie_ep_doorbell.DOORBELL_TEST ...
[  215.256772] pci-endpoint-test 0000:01:00.0: Failed to trigger doorbell in endpoint
#            OK  pcie_ep_doorbell.DOORBELL_TEST
ok 23 pcie_ep_doorbell.DOORBELL_TEST
# FAILED: 19 / 23 tests passed.
# 2 skipped test(s) detected. Consider enabling relevant config options to improve coverage.
# Totals: pass:17 fail:4 xfail:0 xpass:0 skip:2 error:0


I saw the weird error:
"Failed to trigger doorbell in endpoint"

Ignore that the test case leaves things to be desired, since it passes even
though we see this warning.


The problem seems to be that the embedded doorbell IRQ handler is executed twice,
in response to a single writel() which rings the doorbell.

diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
index 4802d4f80f78..7f4360579ffb 100644
--- a/drivers/pci/endpoint/functions/pci-epf-test.c
+++ b/drivers/pci/endpoint/functions/pci-epf-test.c
@@ -714,6 +714,7 @@ static irqreturn_t pci_epf_test_doorbell_handler(int irq, void *data)
 
        status |= STATUS_DOORBELL_SUCCESS;
        reg->status = cpu_to_le32(status);
+       trace_printk("calling pci_epf_test_raise_irq()\n");
        pci_epf_test_raise_irq(epf_test, reg);
 
        return IRQ_HANDLED;


As you can see:
 irq/58-pci-ep-t-221     [006] .....    58.601020: pci_epf_test_doorbell_handler: calling pci_epf_test_raise_irq()
 irq/58-pci-ep-t-221     [006] .....    58.601421: pci_epf_test_doorbell_handler: calling pci_epf_test_raise_irq()

It is called twice for a single doorbell ring.

Note that you might need to run the whole selftest test a few times to trigger
the WARN_ON(), even if you can see the trace printks every time.
(Using printks instead of trace printks seems to affect the timing too much.)


My first thought was that we should add reinit_completion() calls after each
wait_for_completion_timeout() in pci_endpoint_test_doorbell(), but after
reading up on complete() and wait_for_completion_timeout(), this should
actually not be needed, and if we did, it would simply hide the read problem,
which appears to be that the embedded doorbell IRQ handler on the EP side is
called twice for a single writel() that rings the doorbell.


Kind regards,
Niklas

^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-07-29 20:50 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-29 20:28 [PATCH] misc: pci_endpoint_test: Add WARN_ON() to detect broken EPC drivers Niklas Cassel
2026-07-29 20:38 ` sashiko-bot
2026-07-29 20:49 ` DWC eDMA weirdness Niklas Cassel

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.