]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
sparc64: fix sun4v_build_irq NULL pointer dereference
authorThomas Tai <thomas.tai@oracle.com>
Thu, 10 Nov 2016 20:10:17 +0000 (12:10 -0800)
committerAllen Pais <allen.pais@oracle.com>
Sun, 22 Jan 2017 15:37:07 +0000 (21:07 +0530)
sun4v_build_irq assume the given irq number is valid and use
it to get the handler pointer, the pointer is dereference
without being checked and cause kernel panic.

The cause of the invalid irq is that the tx/rx irq have never
been free during device removal. irq number end up exhausted during
continuous device add/removal test.

tx/rx irq is allocated during vio_device_probe() using irq_alloc()
and cookie_assign(). To free the tx/rx irq, cookie_unassign() and
irq_free() is called when the device is removed.

Orabug: 23082240

Signed-off-by: Thomas Tai <thomas.tai@oracle.com>
Reviewed-by: Chris Hyser <chris.hyser@oracle.com>
(cherry picked from commit 80043637b8fb1eabc16ab5947019f4dcdbb8c79f)
Signed-off-by: Allen Pais <allen.pais@oracle.com>
arch/sparc/include/asm/irq_64.h
arch/sparc/kernel/irq_64.c
arch/sparc/kernel/vio.c

index c3c24f02a6ac8374b7fad63dabf4cca8c9649680..e9760de55db618146cf3372d888b23126d39f7a9 100644 (file)
@@ -46,6 +46,7 @@ void irq_install_pre_handler(int irq,
 unsigned int build_irq(int inofixup, unsigned long iclr, unsigned long imap);
 unsigned int sun4v_build_irq(u32 devhandle, unsigned int devino);
 unsigned int sun4v_build_virq(u32 devhandle, unsigned int devino);
+void sun4v_free_virq(int irq, u32 devhandle, unsigned int devino);
 unsigned int sun4v_build_msi(u32 devhandle, unsigned int *irq_p,
                             unsigned int msi_devino_start,
                             unsigned int msi_devino_end);
index 7c15386e9201dacb82d771ebbbf62cbeac7f91ae..787076a752c70148e3e2431c7945e2c4e8bfee34 100644 (file)
@@ -285,6 +285,15 @@ static void set_cookie(unsigned int devhandle, unsigned int devino)
        set_bit(nr, cookie_bitmap);
 }
 
+static void clear_cookie(unsigned int devhandle, unsigned int devino)
+{
+       int nr = (int) (devhandle | devino);
+
+       if (nr >= NR_COOKIE_BITS)
+               return;
+       clear_bit(nr, cookie_bitmap);
+}
+
 static unsigned int cookie_exists(u32 devhandle, unsigned int devino)
 {
        unsigned long hv_err, cookie;
@@ -752,6 +761,25 @@ static unsigned long cookie_assign(unsigned int irq, u32 devhandle,
        return hv_error;
 }
 
+static unsigned long cookie_unassign(u32 devhandle, unsigned int devino)
+{
+       unsigned long hv_error;
+
+       /* set the cookie value to 0, so that the interrupt source is
+        * returned to the state of having no cookie assigned,
+        * and interrupts are explicitly disabled for the device.
+        */
+       hv_error = sun4v_vintr_set_cookie(devhandle, devino, 0);
+
+       /* clear the cookie bitmap */
+       if (hv_error)
+               pr_err("HV vintr set cookie failed = %ld\n", hv_error);
+       else
+               clear_cookie(devhandle, devino);
+
+       return hv_error;
+}
+
 static void cookie_handler_data(struct irq_handler_data *data,
                                u32 devhandle, unsigned int devino)
 {
@@ -767,6 +795,10 @@ static unsigned int cookie_build_irq(u32 devhandle, unsigned int devino,
 
        irq = sun4v_build_common(devhandle, devino, cookie_handler_data, chip);
 
+       /* ensure irq is valid */
+       if (!irq)
+               return 0;
+
        hv_error = cookie_assign(irq, devhandle, devino);
        if (hv_error) {
                irq_free(irq);
@@ -776,6 +808,18 @@ static unsigned int cookie_build_irq(u32 devhandle, unsigned int devino,
        return irq;
 }
 
+/* cookie_free_irq will reverse the effect of cookie_build_irq sequence,
+ * by unassign the cookie and free the irq resource
+ */
+static void cookie_free_irq(int irq, u32 devhandle, unsigned int devino)
+{
+       /* set vintr cookie to 0 and clear cookie bitmap */
+       cookie_unassign(devhandle, devino);
+
+       /* free irq_handler_data and irq desc */
+       irq_free(irq);
+}
+
 static unsigned int sun4v_build_cookie(u32 devhandle, unsigned int devino)
 {
        unsigned int irq;
@@ -866,6 +910,18 @@ out:
        return irq;
 }
 
+void sun4v_free_virq(int irq, u32 devhandle, unsigned int devino)
+{
+       if (!irq)
+               return;
+
+       /* clear IRQ_NOAUTOGEN which is set in sun4v_build_virq */
+       irq_clear_status_flags(irq, IRQ_NOAUTOEN);
+
+       /* undo the effect of sun4v_build_virq() */
+       cookie_free_irq(irq, devhandle, devino);
+}
+
 void *hardirq_stack[NR_CPUS];
 void *softirq_stack[NR_CPUS];
 
index 4e02b3df6d57430775db28c584bdd69c202c5fb9..093efaea75f3d3ddfbf55e01c1d5c5ac41e037ed 100644 (file)
@@ -94,16 +94,30 @@ static int vio_device_remove(struct device *dev)
 {
        struct vio_dev *vdev = to_vio_dev(dev);
        struct vio_driver *drv = to_vio_driver(dev->driver);
+       int rc;
 
        if (drv->remove) {
 
-               return drv->remove(vdev);
+               rc = drv->remove(vdev);
 
                /*
-                * Ideally, we would remove/deallocate tx/rx virqs
-                * here - however, there are currently no support
-                * routines to do so at the moment. TBD
+                * Remove/deallocate tx/rx virqs unless the
+                * driver specified not to.
                 */
+               if (!drv->no_irq) {
+                       if (vdev->tx_irq != 0) {
+                               sun4v_free_virq(vdev->tx_irq, vdev->dev_handle,
+                                               vdev->tx_ino);
+                               vdev->tx_irq = 0;
+                       }
+                       if (vdev->rx_irq != 0) {
+                               sun4v_free_virq(vdev->rx_irq, vdev->dev_handle,
+                                               vdev->rx_ino);
+                               vdev->rx_irq = 0;
+                       }
+               }
+
+               return rc;
        }
 
        return 1;