<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">From: Alasdair G Kergon &lt;agk@redhat.com&gt;

upgrade_mode() sets bdev to NULL temporarily, and does not have any
locking to exclude anything from seeing that NULL.

In dm_table_any_congested() bdev_get_queue() can dereference that NULL and
cause a reported oops.

Fix this by not changing that field during the mode upgrade.

Cc: stable@kernel.org
Cc: Neil Brown &lt;neilb@suse.de&gt;
Signed-off-by: Alasdair G Kergon &lt;agk@redhat.com&gt;
---
 drivers/md/dm-table.c |   26 ++++++++++++++------------
 1 files changed, 14 insertions(+), 12 deletions(-)

Index: linux-2.6.29/drivers/md/dm-table.c
===================================================================
--- linux-2.6.29.orig/drivers/md/dm-table.c	2009-04-02 16:34:52.000000000 +0100
+++ linux-2.6.29/drivers/md/dm-table.c	2009-04-02 16:45:40.000000000 +0100
@@ -399,28 +399,30 @@ static int check_device_area(struct dm_d
 }
 
 /*
- * This upgrades the mode on an already open dm_dev.  Being
+ * This upgrades the mode on an already open dm_dev, being
  * careful to leave things as they were if we fail to reopen the
- * device.
+ * device and not to touch the existing bdev field in case
+ * it is accessed concurrently inside dm_table_any_congested().
  */
 static int upgrade_mode(struct dm_dev_internal *dd, fmode_t new_mode,
 			struct mapped_device *md)
 {
 	int r;
-	struct dm_dev_internal dd_copy;
-	dev_t dev = dd-&gt;dm_dev.bdev-&gt;bd_dev;
+	struct dm_dev_internal dd_new, dd_old;
 
-	dd_copy = *dd;
+	dd_new = dd_old = *dd;
+
+	dd_new.dm_dev.mode |= new_mode;
+	dd_new.dm_dev.bdev = NULL;
+
+	r = open_dev(&amp;dd_new, dd-&gt;dm_dev.bdev-&gt;bd_dev, md);
+	if (r)
+		return r;
 
 	dd-&gt;dm_dev.mode |= new_mode;
-	dd-&gt;dm_dev.bdev = NULL;
-	r = open_dev(dd, dev, md);
-	if (!r)
-		close_dev(&amp;dd_copy, md);
-	else
-		*dd = dd_copy;
+	close_dev(&amp;dd_old, md);
 
-	return r;
+	return 0;
 }
 
 /*
</pre></body></html>