[dm-devel] [PATCH] dm zoned: Fix reference counter initial value of chunk works

Damien Le Moal Damien.LeMoal at wdc.com
Thu Feb 27 00:20:44 UTC 2020


On 2020/02/27 9:18, Shin'ichiro Kawasaki wrote:
> Dm-zoned initializes reference counters of new chunk works with zero
> value and refcount_inc() is called to increment the counter. However, the
> refcount_inc() function handles the addition to zero value as an error
> and triggers the warning as follows:
> 
> refcount_t: addition on 0; use-after-free.
> WARNING: CPU: 7 PID: 1506 at lib/refcount.c:25 refcount_warn_saturate+0x68/0xf0
> Modules linked in: dm_zoned bridge stp llc rpcsec_gss_krb5 auth_rpcgss nfsv4 dns_resolver nfs lockd grace fscache nf_conntrack_netbios_ns nf_o
> CPU: 7 PID: 1506 Comm: systemd-udevd Not tainted 5.4.0+ #134
> ...
> Call Trace:
>  dmz_map+0x2d2/0x350 [dm_zoned]
>  __map_bio+0x42/0x1a0
>  __split_and_process_non_flush+0x14a/0x1b0
>  __split_and_process_bio+0x83/0x240
>  ? kmem_cache_alloc+0x165/0x220
>  dm_process_bio+0x90/0x230
>  ? generic_make_request_checks+0x2e7/0x680
>  dm_make_request+0x3e/0xb0
>  generic_make_request+0xcf/0x320
>  ? memcg_drain_all_list_lrus+0x1c0/0x1c0
>  submit_bio+0x3c/0x160
>  ? guard_bio_eod+0x2c/0x130
>  mpage_readpages+0x182/0x1d0
>  ? bdev_evict_inode+0xf0/0xf0
>  read_pages+0x6b/0x1b0
>  __do_page_cache_readahead+0x1ba/0x1d0
>  force_page_cache_readahead+0x93/0x100
>  generic_file_read_iter+0x83a/0xe40
>  ? __seccomp_filter+0x7b/0x670
>  new_sync_read+0x12a/0x1c0
>  vfs_read+0x9d/0x150
>  ksys_read+0x5f/0xe0
>  do_syscall_64+0x5b/0x180
>  entry_SYSCALL_64_after_hwframe+0x44/0xa9
> ...
> 
> After this warning, following refcount API calls for the counter all fail
> to change the counter value.
> 
> Fix this by setting the initial reference counter value not zero but one
> for the new chunk works. Instead, do not call refcount_inc() via
> dmz_get_chunk_work() for the new chunks works.
> 
> The failure was observed with linux version 5.4 with CONFIG_REFCOUNT_FULL
> enabled. Refcount rework was merged to linux version 5.5 by the
> commit 168829ad09ca ("Merge branch 'locking-core-for-linus' of
> git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip"). After this
> commit, CONFIG_REFCOUNT_FULL was removed and the failure was observed
> regardless of kernel configuration.
> 
> Linux version 4.20 merged the commit 092b5648760a ("dm zoned: target: use
> refcount_t for dm zoned reference counters"). Before this commit, dm
> zoned used atomic_t APIs which does not check addition to zero, then this
> fix is not necessary.
> 
> Fixes: 092b5648760a ("dm zoned: target: use refcount_t for dm zoned reference counters")
> Cc: stable at vger.kernel.org # 5.5
> Cc: stable at vger.kernel.org # 5.4
> Signed-off-by: Shin'ichiro Kawasaki <shinichiro.kawasaki at wdc.com>

Looks good to me.

Reviewed-by: Damien Le Moal <damien.lemoal at wdc.com>


> ---
>  drivers/md/dm-zoned-target.c | 8 ++++----
>  1 file changed, 4 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/md/dm-zoned-target.c b/drivers/md/dm-zoned-target.c
> index 70a1063161c0..b1e64cd31647 100644
> --- a/drivers/md/dm-zoned-target.c
> +++ b/drivers/md/dm-zoned-target.c
> @@ -533,8 +533,9 @@ static int dmz_queue_chunk_work(struct dmz_target *dmz, struct bio *bio)
>  
>  	/* Get the BIO chunk work. If one is not active yet, create one */
>  	cw = radix_tree_lookup(&dmz->chunk_rxtree, chunk);
> -	if (!cw) {
> -
> +	if (cw) {
> +		dmz_get_chunk_work(cw);
> +	} else {
>  		/* Create a new chunk work */
>  		cw = kmalloc(sizeof(struct dm_chunk_work), GFP_NOIO);
>  		if (unlikely(!cw)) {
> @@ -543,7 +544,7 @@ static int dmz_queue_chunk_work(struct dmz_target *dmz, struct bio *bio)
>  		}
>  
>  		INIT_WORK(&cw->work, dmz_chunk_work);
> -		refcount_set(&cw->refcount, 0);
> +		refcount_set(&cw->refcount, 1);
>  		cw->target = dmz;
>  		cw->chunk = chunk;
>  		bio_list_init(&cw->bio_list);
> @@ -556,7 +557,6 @@ static int dmz_queue_chunk_work(struct dmz_target *dmz, struct bio *bio)
>  	}
>  
>  	bio_list_add(&cw->bio_list, bio);
> -	dmz_get_chunk_work(cw);
>  
>  	dmz_reclaim_bio_acc(dmz->reclaim);
>  	if (queue_work(dmz->chunk_wq, &cw->work))
> 


-- 
Damien Le Moal
Western Digital Research






More information about the dm-devel mailing list