diff mbox series

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

Message ID 20230707031536.666482-4-zhongjinghua@huaweicloud.com (mailing list archive)
State New, archived
Headers show
Series nbd: fix null-ptr-dereference while accessing 'nbd->config' | expand

Commit Message

zhongjinghua July 7, 2023, 3:15 a.m. UTC
From: Zhong Jinghua <zhongjinghua@huawei.com>

nbd->config = config and refcount_set(&nbd->config_refs, 1) in
nbd_genl_connect may be out of order, causing config_refs to be set to 1
first, and then nbd_open accessing nbd->config reports a null pointer
reference.
   T1                      T2
vfs_open
  do_dentry_open
    blkdev_open
      blkdev_get
        __blkdev_get
          nbd_open
           nbd_get_config_unlocked
                        genl_rcv_msg
                          genl_family_rcv_msg
                            genl_family_rcv_msg_doit
                              nbd_genl_connect
                                nbd_alloc_and_init_config
                                  // out of order execution
                                  refcount_set(&nbd->config_refs, 1); // 2
             nbd->config
             // null point
                                  nbd->config = config; // 1

Fix it by adding a cpu memory barrier to guarantee sequential execution.

Signed-off-by: Zhong Jinghua <zhongjinghua@huawei.com>
---
 drivers/block/nbd.c | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

Comments

Yu Kuai Sept. 28, 2023, 6:07 a.m. UTC | #1
在 2023/07/07 11:15, Zhong Jinghua 写道:
> From: Zhong Jinghua <zhongjinghua@huawei.com>
> 
> nbd->config = config and refcount_set(&nbd->config_refs, 1) in
> nbd_genl_connect may be out of order, causing config_refs to be set to 1
> first, and then nbd_open accessing nbd->config reports a null pointer
> reference.
>     T1                      T2
> vfs_open
>    do_dentry_open
>      blkdev_open
>        blkdev_get
>          __blkdev_get
>            nbd_open
>             nbd_get_config_unlocked
>                          genl_rcv_msg
>                            genl_family_rcv_msg
>                              genl_family_rcv_msg_doit
>                                nbd_genl_connect
>                                  nbd_alloc_and_init_config
>                                    // out of order execution
>                                    refcount_set(&nbd->config_refs, 1); // 2
>               nbd->config
>               // null point
>                                    nbd->config = config; // 1
> 
> Fix it by adding a cpu memory barrier to guarantee sequential execution.
> 
LGTM

Reviewed-by: Yu Kuai <yukuai3@huawei.com>

> Signed-off-by: Zhong Jinghua <zhongjinghua@huawei.com>
> ---
>   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 7186a9a49445..c410cf29fb0c 100644
> --- a/drivers/block/nbd.c
> +++ b/drivers/block/nbd.c
> @@ -395,8 +395,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;
>   }
> @@ -1555,7 +1563,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;
>
diff mbox series

Patch

diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 7186a9a49445..c410cf29fb0c 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -395,8 +395,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;
 }
@@ -1555,7 +1563,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;