| From c130b666a9a711f985a0a44b58699ebe14bb7245 Mon Sep 17 00:00:00 2001 |
| From: Gabriel Krisman Bertazi <krisman@linux.vnet.ibm.com> |
| Date: Wed, 28 Dec 2016 16:42:00 -0200 |
| Subject: [PATCH] 8250_pci: Fix potential use-after-free in error path |
| |
| commit c130b666a9a711f985a0a44b58699ebe14bb7245 upstream. |
| |
| Commit f209fa03fc9d ("serial: 8250_pci: Detach low-level driver during |
| PCI error recovery") introduces a potential use-after-free in case the |
| pciserial_init_ports call in serial8250_io_resume fails, which may |
| happen if a memory allocation fails or if the .init quirk failed for |
| whatever reason). If this happen, further pci_get_drvdata will return a |
| pointer to freed memory. |
| |
| This patch reworks the PCI recovery resume hook to restore the old priv |
| structure in this case, which should be ok, since the ports were already |
| detached. Such error during recovery causes us to give up on the |
| recovery. |
| |
| Fixes: f209fa03fc9d ("serial: 8250_pci: Detach low-level driver during |
| PCI error recovery") |
| Reported-by: Michal Suchanek <msuchanek@suse.com> |
| Signed-off-by: Gabriel Krisman Bertazi <krisman@linux.vnet.ibm.com> |
| Signed-off-by: Guilherme G. Piccoli <gpiccoli@linux.vnet.ibm.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c |
| index aa0166b6d450..116436b7fa52 100644 |
| --- a/drivers/tty/serial/8250/8250_pci.c |
| +++ b/drivers/tty/serial/8250/8250_pci.c |
| @@ -5642,17 +5642,15 @@ static pci_ers_result_t serial8250_io_slot_reset(struct pci_dev *dev) |
| static void serial8250_io_resume(struct pci_dev *dev) |
| { |
| struct serial_private *priv = pci_get_drvdata(dev); |
| - const struct pciserial_board *board; |
| + struct serial_private *new; |
| |
| if (!priv) |
| return; |
| |
| - board = priv->board; |
| - kfree(priv); |
| - priv = pciserial_init_ports(dev, board); |
| - |
| - if (!IS_ERR(priv)) { |
| - pci_set_drvdata(dev, priv); |
| + new = pciserial_init_ports(dev, priv->board); |
| + if (!IS_ERR(new)) { |
| + pci_set_drvdata(dev, new); |
| + kfree(priv); |
| } |
| } |
| |
| -- |
| 2.12.0 |
| |