[Devel] [PATCH vz10] dm: fix SRCU read-lock leak on dm_llseek_hole() early error paths

Vasileios Almpanis vasileios.almpanis at virtuozzo.com
Fri Jun 5 14:38:31 MSK 2026


Reviewed-by: Vasileios Almpanis <vasileios.almpanis at virtuozzo.com>

On 6/4/26 11:16 AM, Konstantin Khorenko wrote:
> dm_get_live_table() always takes the io_barrier SRCU read lock (storing
> the index in *srcu_idx) and returns the live map. dm_llseek_hole() left
> two early-return paths that skipped the matching dm_put_live_table():
>
> 	table = dm_get_live_table(md, &srcu_idx);
> 	if (!table || !dm_table_get_size(table))
> 		return -ENOTTY;          /* leaks SRCU read lock */
> 	...
> 	if (table->num_targets != 1)
> 		return -EOPNOTSUPP;      /* leaks SRCU read lock */
>
> So a SEEK_HOLE/SEEK_DATA on a dm device with no live table, an empty
> table, or more than one target leaked an SRCU reader. A leaked reader
> stalls any later synchronize_srcu() (device suspend / table reload) and
> unbalances the per-CPU SRCU counter. The remaining error paths already
> use "goto out".
>
> Convert the two early returns to the "ret = ...; goto out;" pattern so
> they release the lock via the out: label like the rest of the function.
>
> Fixes: 808222b3f6b7 ("dm: add llseek_hole infrastructure")
> https://virtuozzo.atlassian.net/browse/VSTOR-132310
> Signed-off-by: Konstantin Khorenko <khorenko at virtuozzo.com>
> ---
>   drivers/md/dm.c | 12 ++++++++----
>   1 file changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/md/dm.c b/drivers/md/dm.c
> index b5e28553824d..130eec93fa10 100644
> --- a/drivers/md/dm.c
> +++ b/drivers/md/dm.c
> @@ -493,16 +493,20 @@ static loff_t dm_llseek_hole(struct block_device *bdev, loff_t offset, int whenc
>   	loff_t ret;
>   
>   	table = dm_get_live_table(md, &srcu_idx);
> -	if (!table || !dm_table_get_size(table))
> -		return -ENOTTY;
> +	if (!table || !dm_table_get_size(table)) {
> +		ret = -ENOTTY;
> +		goto out;
> +	}
>   
>   	/*
>   	 * For now we only support devices that have a single target.
>   	 * But probably it is not hard to break it to a few requests to a different
>   	 * targets
>   	 */
> -	if (table->num_targets != 1)
> -		return -EOPNOTSUPP;
> +	if (table->num_targets != 1) {
> +		ret = -EOPNOTSUPP;
> +		goto out;
> +	}
>   
>   	ti = dm_table_get_target(table, 0);
>   

-- 
Best regards, Vasileios Almpanis
Software Developer, Virtuozzo.



More information about the Devel mailing list