diff options
author | Jeremy Kerr <jk@ozlabs.org> | 2018-02-12 08:15:47 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-03-14 21:11:01 +0300 |
commit | e0c24bddf07c7735860fe654061938218732c92d (patch) | |
tree | a9b2deb85f7c3a08e2a7f4eeba99e0a471136a66 /drivers/fsi/fsi-master-hub.c | |
parent | 638bd9ac847e8cb25f59b6bdca29830ece477ed6 (diff) | |
download | linux-e0c24bddf07c7735860fe654061938218732c92d.tar.xz |
fsi: master: Clarify master lifetimes & fix use-after-free in hub master
Once we call fsi_master_unregister, the core will put_device,
potentially freeing the hub master. This change adds a comment
explaining the lifetime of an allocated fsi_master.
We then add a reference from the driver to the hub master, so it stays
around until we've finished ->remove().
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Tested-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/fsi/fsi-master-hub.c')
-rw-r--r-- | drivers/fsi/fsi-master-hub.c | 21 |
1 files changed, 18 insertions, 3 deletions
diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c index 5e4cd3134bc0..5885fc4a1ef0 100644 --- a/drivers/fsi/fsi-master-hub.c +++ b/drivers/fsi/fsi-master-hub.c @@ -288,10 +288,19 @@ static int hub_master_probe(struct device *dev) hub_master_init(hub); rc = fsi_master_register(&hub->master); - if (!rc) - return 0; + if (rc) + goto err_release; + + /* At this point, fsi_master_register performs the device_initialize(), + * and holds the sole reference on master.dev. This means the device + * will be freed (via ->release) during any subsequent call to + * fsi_master_unregister. We add our own reference to it here, so we + * can perform cleanup (in _remove()) without it being freed before + * we're ready. + */ + get_device(&hub->master.dev); + return 0; - kfree(hub); err_release: fsi_slave_release_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET, FSI_HUB_LINK_SIZE * links); @@ -306,6 +315,12 @@ static int hub_master_remove(struct device *dev) fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size); of_node_put(hub->master.dev.of_node); + /* + * master.dev will likely be ->release()ed after this, which free()s + * the hub + */ + put_device(&hub->master.dev); + return 0; } |