diff options
authorAlex Elder <elder@inktank.com>2013-05-13 20:35:37 -0500
committerAlex Elder <elder@inktank.com>2013-05-17 12:50:10 -0500
commit3abef3b3585bbc67d56fdc9c67761a900fb4b69d (patch)
parent7262cfca430a1a0e0707149af29ae86bc0ded230 (diff)
rbd: fix cleanup in rbd_add()
Bjorn Helgaas pointed out that a recent commit introduced a use-after-free condition in an error path for rbd_add(). He correctly stated: I think b536f69a3a5 "rbd: set up devices only for mapped images" introduced a use-after-free error in rbd_add(): ... If rbd_dev_device_setup() returns an error, we call rbd_dev_image_release(), which ultimately kfrees rbd_dev. Then we call rbd_dev_destroy(), which references fields in the already-freed rbd_dev struct before kfreeing it again. The simple fix is to return the error code after the call to rbd_dev_image_release(). Closer examination revealed that there's no need to clean up rbd_opts in that function, so fix that too. Update some other comments that have also become out of date. Reported-by: Bjorn Helgaas <bhelgaas@google.com> Signed-off-by: Alex Elder <elder@inktank.com> Reviewed-by: Josh Durgin <josh.durgin@inktank.com>
1 files changed, 14 insertions, 9 deletions
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 5f64ba77bc7f..3a897a531e9c 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -4700,8 +4700,10 @@ out:
return ret;
-/* Undo whatever state changes are made by v1 or v2 image probe */
+ * Undo whatever state changes are made by v1 or v2 header info
+ * call.
+ */
static void rbd_dev_unprobe(struct rbd_device *rbd_dev)
struct rbd_image_header *header;
@@ -4905,9 +4907,10 @@ static int rbd_dev_image_probe(struct rbd_device *rbd_dev, bool mapping)
int tmp;
- * Get the id from the image id object. If it's not a
- * format 2 image, we'll get ENOENT back, and we'll assume
- * it's a format 1 image.
+ * Get the id from the image id object. Unless there's an
+ * error, rbd_dev->spec->image_id will be filled in with
+ * a dynamically-allocated string, and rbd_dev->image_format
+ * will be set to either 1 or 2.
ret = rbd_dev_image_id(rbd_dev);
if (ret)
@@ -5029,16 +5032,18 @@ static ssize_t rbd_add(struct bus_type *bus,
rbd_dev->mapping.read_only = read_only;
rc = rbd_dev_device_setup(rbd_dev);
- if (!rc)
- return count;
+ if (rc) {
+ rbd_dev_image_release(rbd_dev);
+ goto err_out_module;
+ }
+ return count;
- rbd_dev_image_release(rbd_dev);
- kfree(rbd_opts);