Commit 9616ff434d96303689391af3d6e1c845d233405f

Authored by David S. Miller
1 parent 7c1f6afcf9

sunsu: Fix use after free in su_remove().

Real serial port 'up' objects are statically allocated from an
array in the driver.  Keyboard and mouse ports, on the other
hand, are dynamically allocated.

Unfortunately, we free these dynamic 'up' objects before we unmap the
I/O registers.

Rearrange su_remove() so that this does not happen.

Noticed by Julia Lawall.

Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 1 changed file with 9 additions and 4 deletions Side-by-side Diff

drivers/serial/sunsu.c
... ... @@ -1500,19 +1500,24 @@
1500 1500 static int __devexit su_remove(struct of_device *op)
1501 1501 {
1502 1502 struct uart_sunsu_port *up = dev_get_drvdata(&op->dev);
  1503 + bool kbdms = false;
1503 1504  
1504 1505 if (up->su_type == SU_PORT_MS ||
1505   - up->su_type == SU_PORT_KBD) {
  1506 + up->su_type == SU_PORT_KBD)
  1507 + kbdms = true;
  1508 +
  1509 + if (kbdms) {
1506 1510 #ifdef CONFIG_SERIO
1507 1511 serio_unregister_port(&up->serio);
1508 1512 #endif
1509   - kfree(up);
1510   - } else if (up->port.type != PORT_UNKNOWN) {
  1513 + } else if (up->port.type != PORT_UNKNOWN)
1511 1514 uart_remove_one_port(&sunsu_reg, &up->port);
1512   - }
1513 1515  
1514 1516 if (up->port.membase)
1515 1517 of_iounmap(&op->resource[0], up->port.membase, up->reg_size);
  1518 +
  1519 + if (kbdms)
  1520 + kfree(up);
1516 1521  
1517 1522 dev_set_drvdata(&op->dev, NULL);
1518 1523