mirror of
https://github.com/coreutils/coreutils.git
synced 2026-10-28 09:44:34 +08:00
tail: fix handling of inotify event queue overflow
* 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.
This commit is contained in:
1 parent
38aaf5e611
commit
12244b4f2b
4 files changed
+91
No files matched your search
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 \
|
||||
|
||||
Executable
+75
@@ -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
|
||||
Reference in new issue
Block a user