[PATCH 3/3] nbd: fix null-ptr-dereference while accessing 'nbd->config'

From: linan666
Date: Thu Nov 16 2023 - 03:29:15 EST


From: Li Nan <linan122@xxxxxxxxxx>

Memory reordering may occur in nbd_genl_connect(), causing config_refs
to be set to 1 while nbd->config is still empty. Opening nbd at this
time will cause null-ptr-dereference.

T1 T2
nbd_open
nbd_get_config_unlocked
nbd_genl_connect
nbd_alloc_and_init_config
//memory reordered
refcount_set(&nbd->config_refs, 1) // 2
nbd->config
->null point
nbd->config = config // 1

Fix it by adding smp barrier to guarantee the execution sequence.

Signed-off-by: Li Nan <linan122@xxxxxxxxxx>
---
drivers/block/nbd.c | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 1b9ee96d3b8a..03f387f1abb5 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -398,8 +398,16 @@ static u32 req_to_nbd_cmd_type(struct request *req)

static struct nbd_config *nbd_get_config_unlocked(struct nbd_device *nbd)
{
- if (refcount_inc_not_zero(&nbd->config_refs))
+ if (refcount_inc_not_zero(&nbd->config_refs)) {
+ /*
+ * Add smp_mb__after_atomic to ensure that reading nbd->config_refs
+ * and reading nbd->config is ordered. The pair is the barrier in
+ * nbd_alloc_and_init_config(), avoid nbd->config_refs is set
+ * before nbd->config.
+ */
+ smp_mb__after_atomic();
return nbd->config;
+ }

return NULL;
}
@@ -1559,7 +1567,15 @@ static int nbd_alloc_and_init_config(struct nbd_device *nbd)
init_waitqueue_head(&config->conn_wait);
config->blksize_bits = NBD_DEF_BLKSIZE_BITS;
atomic_set(&config->live_connections, 0);
+
nbd->config = config;
+ /*
+ * Order refcount_set(&nbd->config_refs, 1) and nbd->config assignment,
+ * its pair is the barrier in nbd_get_config_unlocked().
+ * So nbd_get_config_unlocked() won't see nbd->config as null after
+ * refcount_inc_not_zero() succeed.
+ */
+ smp_mb__before_atomic();
refcount_set(&nbd->config_refs, 1);

return 0;
--
2.39.2