* src/tail.c (tail_forever_inotify): Fallback to polling upon overlow.
* tests/tail/inotify-overflow.sh: Add a new test.
* tests/local.mk: Reference the new test.
* NEWS: Mention the bug fix.
Discovered by Anthropic's OSS Scanner, as bug ANT-2026-UUNT73BK.
---
NEWS | 6 +++
src/tail.c | 9 ++++
tests/local.mk | 1 +
tests/tail/inotify-overflow.sh | 75 ++++++++++++++++++++++++++++++++++
4 files changed, 91 insertions(+)
create mode 100755 tests/tail/inotify-overflow.sh
diff --git a/NEWS b/NEWS
index cacd907b0..76a809c7f 100644
--- a/NEWS
+++ b/NEWS
@@ -52,6 +52,12 @@ GNU coreutils NEWS -*-
outline -*-
it would have failed if the control flag encoding was different.
[This bug was present in "the beginning".]
+ 'tail --follow' no longer ignores an overflow of the inotify event queue,
+ and now reverts to polling in that case. Previously tail could fail
+ to notice new data, or that a file followed by name was replaced,
+ if events arrived faster than tail could process them.
+ [bug introduced in coreutils-7.5]
+
'unexpand' no longer overflows a heap buffer on non-GLIBC platforms,
given whitespace characters whose display width is negative. E.g.,
this edge case can occur with \u0085 (next line) characters on musl.
diff --git a/src/tail.c b/src/tail.c
index 0778c9985..fd42fc34c 100644
--- a/src/tail.c
+++ b/src/tail.c
@@ -1766,6 +1766,15 @@ tail_forever_inotify (int wd, struct File_spec *f, int
n_files,
ev = void_ev;
evbuf_off += sizeof (*ev) + ev->len;
+ /* If the kernel discarded events for any of the watched files
+ or directories, so we can no longer depend on our view of them.
+ Revert to polling now. */
+ if (ev->mask & IN_Q_OVERFLOW)
+ {
+ error (0, 0, _("inotify event queue overflowed"));
+ return;
+ }
+
/* If a directory is deleted, IN_DELETE_SELF is emitted
with ev->name of length 0.
We need to catch it, otherwise it would wait forever,
diff --git a/tests/local.mk b/tests/local.mk
index 7d75b4b5a..bb6d90144 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -207,6 +207,7 @@ all_tests = \
tests/tail/inotify-rotate-resources.sh \
tests/tail/inotify-dir-recreate.sh \
tests/tail/inotify-only-regular.sh \
+ tests/tail/inotify-overflow.sh \
tests/tail/quote-headers.sh \
tests/chmod/no-x.sh \
tests/chgrp/basic.sh \
diff --git a/tests/tail/inotify-overflow.sh b/tests/tail/inotify-overflow.sh
new file mode 100755
index 000000000..c74bfa5e6
--- /dev/null
+++ b/tests/tail/inotify-overflow.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+# Ensure tail -F copes with the kernel discarding inotify events
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ tail
+require_inotify_supported_
+
+max_events=$(cat /proc/sys/fs/inotify/max_queued_events) &&
+test "$max_events" -le 100000 ||
+skip_ 'inotify queue limit unknown or too large'
+
+# Terminate any background tail process
+cleanup_()
+{
+ kill -CONT "$pid" 2>/dev/null
+ kill "$pid" 2>/dev/null
+ wait "$pid" 2>/dev/null
+ pid=
+}
+
+# $check_re - string to be found
+check_tail_output_ ()
+{
+ local delay="$1"
+ grep "$check_re" out > /dev/null ||
+ { sleep $delay ; return 1; }
+}
+
+grep_timeout_ ()
+{
+ check_re="$1"
+ retry_delay_ check_tail_output_ .1 7
+}
+
+# Speed up the fallback-to-polling phase.
+fastpoll='-s.1 --max-unchanged-stats=1'
+
+mkdir dir && echo x > dir/file || framework_failure_
+
+tail --debug --pid=$$ -F $fastpoll dir/file >out 2>&1 & pid=$!
+grep_timeout_ 'using notification mode' ||
+ { cat out; skip_ "inotify can't be used"; }
+
+# Fill the event queue while tail is not reading it,
+# so that the events for the following rotation are discarded.
+kill -STOP $pid || framework_failure_
+(cd dir && seq $max_events | xargs touch) || framework_failure_
+mv dir/file dir/file.1 &&
+echo y > dir/file &&
+kill -CONT $pid || framework_failure_
+
+# From coreutils-7.5 to 9.12 inclusive, the new file never noticed.
+grep_timeout_ '^y$' || { cat out; fail=1; }
+grep 'inotify event queue overflowed' out || {
+ cat out
+ test "$fail" = 1 || framework_failure_
+}
+
+
+Exit $fail
--
2.55.0