drivers/syslog: fix syslog_write() returning -EIO on every write - #19568
Merged
Conversation
casaroli
requested review from
anchao,
fjpanag and
xiaoxiang781216
as code owners
July 28, 2026 12:00
xiaoxiang781216
previously approved these changes
Jul 28, 2026
syslog_write_foreach() compares an unsigned count against a signed
accumulator:
size_t nwritten = 0;
ssize_t nwritten_max = -EIO;
...
if (nwritten > nwritten_max)
{
nwritten_max = nwritten;
}
return nwritten_max;
The usual arithmetic conversions promote nwritten_max to size_t, so -EIO
becomes 4294967291 on a 32-bit target, and the comparison is never true.
nwritten_max keeps its initial value and the function returns -EIO no
matter how many bytes actually went out. Observed under gdb on a running
target: nwritten == 64, nwritten_max == -5, (nwritten > nwritten_max) == 0.
Most callers discard the result -- syslog() itself returns void -- so this
is normally invisible. It becomes fatal when /dev/console is backed by
syslog_console_write(), because then stdio acts on it.
lib_fflush_unlocked() sees a negative return, sets __FS_FLAG_ERROR and
returns early, before resetting fs_bufpos. The bytes have already been
emitted, but the buffer is never cleared, so every subsequent stdio call
re-flushes the same CONFIG_STDIO_BUFFER_SIZE bytes. The console fills
with one repeated fragment and the system makes no further progress.
Reaching that state needs CONFIG_CONSOLE_SYSLOG=y together with no driver
claiming /dev/console ahead of syslog_console_init(). Three in-tree
defconfigs qualify: x86/qemu-i486:ostest, renesas/skp16c26:ostest and
x86_64/qemu-intel64:earlyfb. The other 56 CONSOLE_SYSLOG configurations
have a serial console that registers /dev/console first, which is why this
has gone unnoticed.
Introduced by 1685e8f ("syslog: avoid an infinite loop if one channel
fails"), which changed nwritten_max from size_t to ssize_t = -EIO so that
an all-channels-failed case could be reported. Give nwritten the same type
so the comparison is signed, which preserves that intent: nwritten_max
stays -EIO only when no channel wrote anything. nwritten is never negative,
so the remaining comparisons against buflen are unaffected.
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
Assisted-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
casaroli
force-pushed
the
fix-syslog-write-eio
branch
from
July 28, 2026 13:01
8146222 to
c24694a
Compare
xiaoxiang781216
approved these changes
Jul 28, 2026
|
jerpelea
approved these changes
Jul 29, 2026
Contributor
|
Thanks for the fix! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
syslog_write_foreach()compares an unsigned count against a signed accumulator, so itreturns
-EIOafter a fully successful write:The usual arithmetic conversions promote
nwritten_maxtosize_t, so-5becomes4294967291on a 32-bit target. The comparison is never true, the assignment never runs,and the initial
-EIOis returned no matter how many bytes actually went out.Captured under gdb on a running
qemu-i486:ostestguest, stopped at thereturn:Introduced by 1685e8f ("syslog: avoid an infinite loop if one channel fails"), which
changed
nwritten_maxfromsize_ttossize_t = -EIOso that an all-channels-failedcase could be reported. Casting at the comparison preserves that intent:
nwritten_maxstays
-EIOonly when no channel wrote anything.Impact
Most callers discard the return value —
syslog()itself returnsvoid— so this isnormally invisible.
It becomes fatal when
/dev/consoleis backed bysyslog_console_write(), because thenstdio acts on it.
lib_fflush_unlocked()sees a negative return, sets__FS_FLAG_ERRORand returns early, before resetting
fs_bufpos. The bytes have already been emitted,but the buffer is never cleared, so every subsequent stdio call re-flushes the same
CONFIG_STDIO_BUFFER_SIZEbytes. The console fills with one repeated fragment and thesystem makes no further progress.
Reaching that state needs
CONFIG_CONSOLE_SYSLOG=yand no driver claiming/dev/consoleahead ofsyslog_console_init(). Three in-tree defconfigs qualify:boards/x86/qemu/qemu-i486/configs/ostestboards/renesas/m16c/skp16c26/configs/ostestboards/x86_64/qemu/qemu-intel64/configs/earlyfbThe other 56
CONFIG_CONSOLE_SYSLOG=yconfigurations have a serial console that registers/dev/consolefirst, so syslog'sregister_driver()fails and the bad return neverreaches stdio. That is why this has gone unnoticed.
No API, ABI or configuration change. One line; the fix can only turn a spurious
-EIOinto the correct positive count.
Testing
Host: macOS 26.5.1 on Apple Silicon (arm64), QEMU 11.0.3,
arm-none-eabi-gcc16.1.0,Homebrew
i686-elf-gcc.tools/checkpatch.sh -c -u -m -g origin/master..HEAD→All checks pass.A/B on
mps2-an521(armv8-m, Cortex-M33)No in-tree armv8-m defconfig reaches the failing path, so the arrangement is built from
stock
mps2-an521:nshwith four config changes — no code changes, no stubs:Before — 25 s, 5681 lines, no forward progress. The console repeats one fragment,
cut at the 64-byte stdio buffer boundary, with lines fused together:
After — same 25 s, 175 lines, and left running it walked all three ostest loops
(2483 lines,
user_main: setvbuf testx3), zeroERRORlines, and no repeated fragment:qemu-i486:ostest(the in-tree config that hits this)Before: 5+ MB of console in 60 s,
user_main: Started with argc=5x77094, andostest_main: putenv(Variax356 which never once completes.After, same tree and same QEMU line:
27 KB in 60 s instead of ~5 MB,
user_main: Startedonce instead of 77094 times,mxordblkflat atc9c88across 52 samples.No regression on unaffected configurations
mps2-an521:nshstock and unmodified — serial console owns/dev/console, so the syslogreturn value is never consulted — runs ostest to completion with the patch applied:
964 lines, 227 s, no errors.