Skip to content

Commit

Permalink
fsi: master: Clarify master lifetimes & fix use-after-free in hub master
Browse files Browse the repository at this point in the history
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 <[email protected]>
Tested-by: Christopher Bostic <[email protected]>
Signed-off-by: Joel Stanley <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
  • Loading branch information
jk-ozlabs authored and gregkh committed Mar 14, 2018
1 parent 638bd9a commit e0c24bd
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 3 deletions.
21 changes: 18 additions & 3 deletions drivers/fsi/fsi-master-hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}

Expand Down
15 changes: 15 additions & 0 deletions drivers/fsi/fsi-master.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,21 @@ struct fsi_master {

#define dev_to_fsi_master(d) container_of(d, struct fsi_master, dev)

/**
* fsi_master registration & lifetime: the fsi_master_register() and
* fsi_master_unregister() functions will take ownership of the master, and
* ->dev in particular. The registration path performs a get_device(), which
* takes the first reference on the device. Similarly, the unregistration path
* performs a put_device(), which may well drop the last reference.
*
* This means that master implementations *may* need to hold their own
* reference (via get_device()) on master->dev. In particular, if the device's
* ->release callback frees the fsi_master, then fsi_master_unregister will
* invoke this free if no other reference is held.
*
* The same applies for the error path of fsi_master_register; if the call
* fails, dev->release will have been invoked.
*/
extern int fsi_master_register(struct fsi_master *master);
extern void fsi_master_unregister(struct fsi_master *master);

Expand Down

0 comments on commit e0c24bd

Please sign in to comment.