• Filipe Manana's avatar
    btrfs: fix infinite directory reads · 9b378f6a
    Filipe Manana authored
    The readdir implementation currently processes always up to the last index
    it finds. This however can result in an infinite loop if the directory has
    a large number of entries such that they won't all fit in the given buffer
    passed to the readdir callback, that is, dir_emit() returns a non-zero
    value. Because in that case readdir() will be called again and if in the
    meanwhile new directory entries were added and we still can't put all the
    remaining entries in the buffer, we keep repeating this over and over.
    
    The following C program and test script reproduce the problem:
    
      $ cat /mnt/readdir_prog.c
      #include <sys/types.h>
      #include <dirent.h>
      #include <stdio.h>
    
      int main(int argc, char *argv[])
      {
        DIR *dir = opendir(".");
        struct dirent *dd;
    
        while ((dd = readdir(dir))) {
          printf("%s\n", dd->d_name);
          rename(dd->d_name, "TEMPFILE");
          rename("TEMPFILE", dd->d_name);
        }
        closedir(dir);
      }
    
      $ gcc -o /mnt/readdir_prog /mnt/readdir_prog.c
    
      $ cat test.sh
      #!/bin/bash
    
      DEV=/dev/sdi
      MNT=/mnt/sdi
    
      mkfs.btrfs -f $DEV &> /dev/null
      #mkfs.xfs -f $DEV &> /dev/null
      #mkfs.ext4 -F $DEV &> /dev/null
    
      mount $DEV $MNT
    
      mkdir $MNT/testdir
      for ((i = 1; i <= 2000; i++)); do
          echo -n > $MNT/testdir/file_$i
      done
    
      cd $MNT/testdir
      /mnt/readdir_prog
    
      cd /mnt
    
      umount $MNT
    
    This behaviour is surprising to applications and it's unlike ext4, xfs,
    tmpfs, vfat and other filesystems, which always finish. In this case where
    new entries were added due to renames, some file names may be reported
    more than once, but this varies according to each filesystem - for example
    ext4 never reported the same file more than once while xfs reports the
    first 13 file names twice.
    
    So change our readdir implementation to track the last index number when
    opendir() is called and then make readdir() never process beyond that
    index number. This gives the same behaviour as ext4.
    Reported-by: default avatarRob Landley <rob@landley.net>
    Link: https://lore.kernel.org/linux-btrfs/2c8c55ec-04c6-e0dc-9c5c-8c7924778c35@landley.net/
    Link: https://bugzilla.kernel.org/show_bug.cgi?id=217681
    CC: stable@vger.kernel.org # 6.4+
    Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
    Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
    9b378f6a
inode.c 316 KB