Re: [PATCH] libfs: Fix duplicate directory entry in offset_dir_lookup

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 3/19/25 11:44 PM, Yongjian Sun wrote:
> From: Yongjian Sun <sunyongjian1@xxxxxxxxxx>
> 
> There is an issue in the kernel:
> 
> In tmpfs, when using the "ls" command to list the contents
> of a directory with a large number of files, glibc performs
> the getdents call in multiple rounds. If a concurrent unlink
> occurs between these getdents calls, it may lead to duplicate
> directory entries in the ls output. One possible reproduction
> scenario is as follows:
> 
> Create 1026 files and execute ls and rm concurrently:
> 
> for i in {1..1026}; do
>     echo "This is file $i" > /tmp/dir/file$i
> done
> 
> ls /tmp/dir				rm /tmp/dir/file4
> 	->getdents(file1026-file5)
> 						->unlink(file4)
> 
> 	->getdents(file5,file3,file2,file1)
> 
> It is expected that the second getdents call to return file3
> through file1, but instead it returns an extra file5.
> 
> The root cause of this problem is in the offset_dir_lookup
> function. It uses mas_find to determine the starting position
> for the current getdents call. Since mas_find locates the first
> position that is greater than or equal to mas->index, when file4
> is deleted, it ends up returning file5.
> 
> It can be fixed by replacing mas_find with mas_find_rev, which
> finds the first position that is less than or equal to mas->index.
> 
> Fixes: b9b588f22a0c ("libfs: Use d_children list to iterate simple_offset directories")
> Signed-off-by: Yongjian Sun <sunyongjian1@xxxxxxxxxx>
> ---
>  fs/libfs.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/fs/libfs.c b/fs/libfs.c
> index 8444f5cc4064..dc042a975a56 100644
> --- a/fs/libfs.c
> +++ b/fs/libfs.c
> @@ -496,7 +496,7 @@ offset_dir_lookup(struct dentry *parent, loff_t offset)
>  		found = find_positive_dentry(parent, NULL, false);
>  	else {
>  		rcu_read_lock();
> -		child = mas_find(&mas, DIR_OFFSET_MAX);
> +		child = mas_find_rev(&mas, DIR_OFFSET_MIN);
>  		found = find_positive_dentry(parent, child, false);
>  		rcu_read_unlock();
>  	}

Reviewed-by: Chuck Lever <chuck.lever@xxxxxxxxxx>

Not an objection, but only an observation: This does not help the
(hopefully exceptionally rare) case when the next entry has an offset
value /larger/ than the current entry because the offset values have
wrapped. I don't have any good ideas about how to deal with that.

-- 
Chuck Lever




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [NTFS 3]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [NTFS 3]     [Samba]     [Device Mapper]     [CEPH Development]

  Powered by Linux