Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aa8f0dc6 authored by Jeff Garzik's avatar Jeff Garzik
Browse files

libata: Fix use-after-iounmap

Jens Axboe pointed out that the iounmap() call in libata was occurring
too early, and some drivers (ahci, probably others) were using ioremap'd
memory after it had been unmapped.

The patch should address that problem by way of improving the libata
driver API:

* move ->host_stop() call after all ->port_stop() calls have occurred.

* create default helper function ata_host_stop(), and move iounmap()
call there.

* add ->host_stop_prewalk() hook, use it in sata_qstor.c (hi Mark).
sata_qstor appears to require the host-stop-before-port-stop ordering
that existed prior to applying the attached patch.
parent bef9c558
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -289,6 +289,8 @@ static void ahci_host_stop(struct ata_host_set *host_set)
{
	struct ahci_host_priv *hpriv = host_set->private_data;
	kfree(hpriv);

	ata_host_stop(host_set);
}

static int ahci_port_start(struct ata_port *ap)
+2 −0
Original line number Diff line number Diff line
@@ -153,6 +153,7 @@ static struct ata_port_operations piix_pata_ops = {

	.port_start		= ata_port_start,
	.port_stop		= ata_port_stop,
	.host_stop		= ata_host_stop,
};

static struct ata_port_operations piix_sata_ops = {
@@ -180,6 +181,7 @@ static struct ata_port_operations piix_sata_ops = {

	.port_start		= ata_port_start,
	.port_stop		= ata_port_stop,
	.host_stop		= ata_host_stop,
};

static struct ata_port_info piix_port_info[] = {
+11 −4
Original line number Diff line number Diff line
@@ -3321,6 +3321,13 @@ void ata_port_stop (struct ata_port *ap)
	dma_free_coherent(dev, ATA_PRD_TBL_SZ, ap->prd, ap->prd_dma);
}

void ata_host_stop (struct ata_host_set *host_set)
{
	if (host_set->mmio_base)
		iounmap(host_set->mmio_base);
}


/**
 *	ata_host_remove - Unregister SCSI host structure with upper layers
 *	@ap: Port to unregister
@@ -3877,10 +3884,6 @@ void ata_pci_remove_one (struct pci_dev *pdev)
	}

	free_irq(host_set->irq, host_set);
	if (host_set->ops->host_stop)
		host_set->ops->host_stop(host_set);
	if (host_set->mmio_base)
		iounmap(host_set->mmio_base);

	for (i = 0; i < host_set->n_ports; i++) {
		ap = host_set->ports[i];
@@ -3899,6 +3902,9 @@ void ata_pci_remove_one (struct pci_dev *pdev)
		scsi_host_put(ap->host);
	}

	if (host_set->ops->host_stop)
		host_set->ops->host_stop(host_set);

	kfree(host_set);

	pci_release_regions(pdev);
@@ -3996,6 +4002,7 @@ EXPORT_SYMBOL_GPL(ata_chk_err);
EXPORT_SYMBOL_GPL(ata_exec_command);
EXPORT_SYMBOL_GPL(ata_port_start);
EXPORT_SYMBOL_GPL(ata_port_stop);
EXPORT_SYMBOL_GPL(ata_host_stop);
EXPORT_SYMBOL_GPL(ata_interrupt);
EXPORT_SYMBOL_GPL(ata_qc_prep);
EXPORT_SYMBOL_GPL(ata_bmdma_setup);
+2 −0
Original line number Diff line number Diff line
@@ -329,6 +329,8 @@ static void nv_host_stop (struct ata_host_set *host_set)
		host->host_desc->disable_hotplug(host_set);

	kfree(host);

	ata_host_stop(host_set);
}

static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+1 −0
Original line number Diff line number Diff line
@@ -122,6 +122,7 @@ static struct ata_port_operations pdc_ata_ops = {
	.scr_write		= pdc_sata_scr_write,
	.port_start		= pdc_port_start,
	.port_stop		= pdc_port_stop,
	.host_stop		= ata_host_stop,
};

static struct ata_port_info pdc_port_info[] = {
Loading