Subject: coreutils: stack out-of-bounds write in tail --follow

From: Kamil Dudka <[email-masked]>
Date: Thu, 24 Jun 2021 16:26:33 +0200

Hello,

As originally reported by Stepan Broz (CC'd), tail --follow crashes when it
is given too many files to follow, and ulimit -n is set to >1024.

FD_SET(wd, &rfd) in tail_forever_inotify() writes beyond the stack-allocated 
variable in case wd >= FD_SETSIZE.  Minimal example:

# mkdir dir
# cd dir
# touch {1..1021}
# ulimit -n 1025
# tail -f *

The out-of-bound write could be fixed like this:

--- a/src/tail.c
+++ b/src/tail.c
@@ -1647,28 +1647,32 @@ tail_forever_inotify (int wd, struct File_spec *f, 
size_t n_files,
               if (writer_is_dead)
                 exit (EXIT_SUCCESS);

               writer_is_dead = (kill (pid, 0) != 0 && errno != EPERM);

               if (writer_is_dead)
                 delay.tv_sec = delay.tv_usec = 0;
               else
                 {
                   delay.tv_sec = (time_t) sleep_interval;
                   delay.tv_usec = 1000000 * (sleep_interval - delay.tv_sec);
                 }
             }

+           if (FD_SETSIZE <= wd)
+             die (EXIT_FAILURE, 0,
+                  _("too many open files to wait for inotify events"));
+
            fd_set rfd;
            FD_ZERO (&rfd);
            FD_SET (wd, &rfd);
            if (monitor_output)
              FD_SET (STDOUT_FILENO, &rfd);

            int file_change = select (MAX (wd, STDOUT_FILENO) + 1,
                                      &rfd, NULL, NULL, pid ? &delay: NULL);

            if (file_change == 0)
              continue;
            else if (file_change == -1)
              die (EXIT_FAILURE, errno,
                   _("error waiting for inotify and output events"));

Alternatively, we might rewrite the code to use poll() rather than select().

Kamil