lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-Id: <20200628141229.16121-1-thommyj@gmail.com>
Date:   Sun, 28 Jun 2020 16:12:29 +0200
From:   Thommy Jakobsson <thommyj@...il.com>
To:     linux-kernel@...r.kernel.org
Cc:     dan.carpenter@...cle.com, gregkh@...uxfoundation.org,
        Thommy Jakobsson <thommyj@...il.com>
Subject: [PATCH v2] uio: disable lazy irq disable to avoid double fire

uio_pdrv_genirq and uio_dmem_genirq interrupts are handled in
userspace. So the condition for the interrupt hasn't normally not been
cleared when top half returns. disable_irq_nosync is called in top half,
but since that normally is lazy the irq isn't actually disabled.

For level triggered interrupts this will always result in a spurious
additional fire since the level in to the interrupt controller still is
active. The actual interrupt handler isn't run though since this
spurious irq is just recorded, and later on discared (for level).

This commit disables lazy masking for level triggered interrupts. It
leaves edge triggered interrupts as before, because they work with the
lazy scheme.

All other UIO drivers already seem to clear the interrupt cause at
driver levels.

Example of double fire. First goes all the way up to
uio_pdrv_genirq_handler, second is terminated in handle_fasteoi_irq and
marked as pending.

<idle>-0 [000] d... 8.245870: gic_handle_irq: irq 29
<idle>-0 [000] d.h. 8.245873: uio_pdrv_genirq_handler: disable irq 29
<idle>-0 [000] d... 8.245878: gic_handle_irq: irq 29
<idle>-0 [000] d.h. 8.245880: handle_fasteoi_irq: irq 29 PENDING
HInt-34  [001] d... 8.245897: uio_pdrv_genirq_irqcontrol: enable irq 29

Tested on 5.7rc2 using uio_pdrv_genirq and a custom Xilinx MPSoC board.

Signed-off-by: Thommy Jakobsson <thommyj@...il.com>
---
New in v2:
- use dev_dbg instead of dev_info
- make sure not to change current behaviour. If irq_data doesn't exists
  for some reasone, just skip doing disabling unlazy altogether
- with above also the wrongly added kfree is removed (found by kbuild
  test robot)

I did not add the Reported-by tag for the kbuild test robot finding.
Not sure about the correct procedure here, but since it found the error
in a patch not merged, my reasoning was that it would look wrong in the
git log later on. Please advice if this is customary anyway.

 drivers/uio/uio_dmem_genirq.c | 19 +++++++++++++++++++
 drivers/uio/uio_pdrv_genirq.c | 18 ++++++++++++++++++
 2 files changed, 37 insertions(+)

diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c
index 6e27fe4fcca3..ec7f66f4555a 100644
--- a/drivers/uio/uio_dmem_genirq.c
+++ b/drivers/uio/uio_dmem_genirq.c
@@ -20,6 +20,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/dma-mapping.h>
 #include <linux/slab.h>
+#include <linux/irq.h>
 
 #include <linux/of.h>
 #include <linux/of_platform.h>
@@ -199,6 +200,24 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
 			goto bad1;
 		uioinfo->irq = ret;
 	}
+
+	if (uioinfo->irq) {
+		struct irq_data *irq_data = irq_get_irq_data(uioinfo->irq);
+
+		/*
+		 * If a level interrupt, dont do lazy disable. Otherwise the
+		 * irq will fire again since clearing of the actual cause, on
+		 * device level, is done in userspace
+		 * irqd_is_level_type() isn't used since isn't valid until
+		 * irq is configured.
+		 */
+		if (irq_data &&
+		    irqd_get_trigger_type(irq_data) & IRQ_TYPE_LEVEL_MASK) {
+			dev_dbg(&pdev->dev, "disable lazy unmask\n");
+			irq_set_status_flags(uioinfo->irq, IRQ_DISABLE_UNLAZY);
+		}
+	}
+
 	uiomem = &uioinfo->mem[0];
 
 	for (i = 0; i < pdev->num_resources; ++i) {
diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c
index ae319ef3a832..6de0b115c2da 100644
--- a/drivers/uio/uio_pdrv_genirq.c
+++ b/drivers/uio/uio_pdrv_genirq.c
@@ -20,6 +20,7 @@
 #include <linux/stringify.h>
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
+#include <linux/irq.h>
 
 #include <linux/of.h>
 #include <linux/of_platform.h>
@@ -171,6 +172,23 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
 		}
 	}
 
+	if (uioinfo->irq) {
+		struct irq_data *irq_data = irq_get_irq_data(uioinfo->irq);
+
+		/*
+		 * If a level interrupt, dont do lazy disable. Otherwise the
+		 * irq will fire again since clearing of the actual cause, on
+		 * device level, is done in userspace
+		 * irqd_is_level_type() isn't used since isn't valid until
+		 * irq is configured.
+		 */
+		if (irq_data &&
+		    irqd_get_trigger_type(irq_data) & IRQ_TYPE_LEVEL_MASK) {
+			dev_dbg(&pdev->dev, "disable lazy unmask\n");
+			irq_set_status_flags(uioinfo->irq, IRQ_DISABLE_UNLAZY);
+		}
+	}
+
 	uiomem = &uioinfo->mem[0];
 
 	for (i = 0; i < pdev->num_resources; ++i) {
-- 
2.17.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ