diff options
author | Edward A. James <eajames@us.ibm.com> | 2017-10-26 23:15:12 +0300 |
---|---|---|
committer | Andrew Jeffery <andrew@aj.id.au> | 2017-10-26 23:56:13 +0300 |
commit | ccbb1cf05ec2ad03d858610396f363ffaef10026 (patch) | |
tree | b7f8059436bd190e844784c77c62a20128dd9720 | |
parent | 880fb6125c4ea0547b51011353a82d04ba8832c1 (diff) | |
download | linux-ccbb1cf05ec2ad03d858610396f363ffaef10026.tar.xz |
drivers (fsi): sbefifo: Fix xfr deletion in lists
Deleting an element of a list while iterating means that you'll access
recently freed memory when getting the next list element.
OpenBMC-Staging-Count: 1
Signed-off-by: Edward A. James <eajames@us.ibm.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
-rw-r--r-- | drivers/fsi/fsi-sbefifo.c | 19 |
1 files changed, 12 insertions, 7 deletions
diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c index f756822c397f..088172c9b8f0 100644 --- a/drivers/fsi/fsi-sbefifo.c +++ b/drivers/fsi/fsi-sbefifo.c @@ -316,14 +316,15 @@ static void sbefifo_client_release(struct kref *kref) { struct sbefifo *sbefifo; struct sbefifo_client *client; - struct sbefifo_xfr *xfr; + struct sbefifo_xfr *xfr, *tmp; client = container_of(kref, struct sbefifo_client, kref); sbefifo = client->dev; if (!READ_ONCE(sbefifo->rc)) { - list_for_each_entry(xfr, &client->xfrs, client) { + list_for_each_entry_safe(xfr, tmp, &client->xfrs, client) { if (test_bit(SBEFIFO_XFR_COMPLETE, &xfr->flags)) { + list_del(&xfr->client); kfree(xfr); continue; } @@ -376,7 +377,7 @@ static void sbefifo_poll_timer(unsigned long data) static const unsigned long EOT_MASK = 0x000000ff; struct sbefifo *sbefifo = (void *)data; struct sbefifo_buf *rbuf, *wbuf; - struct sbefifo_xfr *xfr = NULL; + struct sbefifo_xfr *xfr, *tmp; struct sbefifo_buf drain; size_t devn, bufn; int eot = 0; @@ -491,8 +492,10 @@ out: sbefifo->rc = ret; dev_err(&sbefifo->fsi_dev->dev, "Fatal bus access failure: %d\n", ret); - list_for_each_entry(xfr, &sbefifo->xfrs, xfrs) + list_for_each_entry_safe(xfr, tmp, &sbefifo->xfrs, xfrs) { + list_del(&xfr->xfrs); kfree(xfr); + } INIT_LIST_HEAD(&sbefifo->xfrs); } else if (eot && sbefifo_next_xfr(sbefifo)) { @@ -620,8 +623,8 @@ static ssize_t sbefifo_read_common(struct sbefifo_client *client, if (mod_timer(&client->dev->poll_timer, jiffies)) sbefifo_put(sbefifo); } else { - kfree(xfr); list_del(&xfr->client); + kfree(xfr); wake_up_interruptible(&sbefifo->wait); } } @@ -940,13 +943,15 @@ static int sbefifo_probe(struct device *dev) static int sbefifo_remove(struct device *dev) { struct sbefifo *sbefifo = dev_get_drvdata(dev); - struct sbefifo_xfr *xfr; + struct sbefifo_xfr *xfr, *tmp; spin_lock(&sbefifo->lock); WRITE_ONCE(sbefifo->rc, -ENODEV); - list_for_each_entry(xfr, &sbefifo->xfrs, xfrs) + list_for_each_entry_safe(xfr, tmp, &sbefifo->xfrs, xfrs) { + list_del(&xfr->xfrs); kfree(xfr); + } INIT_LIST_HEAD(&sbefifo->xfrs); |