Skip to content

fix(libc): reject snprintf capacities above INT_MAX + 1 - #9296

Merged
adriadam10 merged 3 commits into
boykopovar:mainfrom
superhe11:codex/fix-snprintf-size-overflow
Oct 22, 2026
Merged

adriadam10 merged 3 commits into
boykopovar:mainfrom
superhe11:codex/fix-snprintf-size-overflow

Conversation

@superhe11

@superhe11 superhe11 commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

What

Fixes #8510. snprintf and vsnprintf currently format into a buffer whose declared capacity exceeds INT_MAX + 1. Reject that capacity before reading formatting arguments: return -1, set guest errno to EOVERFLOW (84), and write only the first terminating NUL. Preserve INT_MAX + 1 as a valid capacity.

Carry @xXPBSXx's offered commit f676d6d17524ca6a1dec5a0dfe66680fc0b5f65c, preserving authorship. The behaviour follows FreeBSD 11.4 snprintf.c and vsnprintf.c. Console verification and the null-buffer deviation are recorded in libc's debt file.

The follow-up extends guest_formatting to check both exports, narrow and guest-wide formatting, INT_MAX + 2 and SIZE_MAX rejection, unchanged %n destinations and buffer-tail bytes, valid capacities 0/1/INT_MAX/INT_MAX + 1, and null-buffer size queries. The va_list wrapper also checks that the supplied list is unchanged.

Related: #9087 changes secure formatting. This change leaves the secure variants untouched; a nonmutating git merge-tree with #9087 succeeds.

Tested

Windows x64, WinLibs MinGW-w64 GCC 15.2.0, CMake/Ninja, Release, BUILD_TESTING=ON, full project configuration, rebased onto main 99844c7ee.

  • Baseline: with the extended test kept and only Formatting.cpp restored to main d34da5d69, guest_formatting fails with Formatting check failed. Restoring the fix makes the same test pass.
  • Full libs target: passed, including NID-patched library artifacts. After the final rebase, rebuilt the library artifacts and test executables again.
  • Final runtime tests: 7/7 passed (patched_libraries, guest_formatting, guest_wide_formatting, the three guest_standard_streams modes, and guest_syslog). Total 2.57 seconds; guest_formatting 0.20 seconds.
  • tools/check_conventions.py --base origin/main and git diff --check: passed.

No console or commercial title was tested; Linux/macOS behaviour was not executed locally.

Overlap review

The documentation-only follow-up moves the snprintf debt entry away from the section-start insertion used by #8937. git merge-tree --write-tree with #8937 succeeds. C++ source and test contents are unchanged; conventions and whitespace checks pass.

Manually reviewed the shared-file diffs of #3965, #4627, #5021, #5303, #6044, #6469, #6879, #7081, #7201, #7447, #7648, #7715, #8150 and #8520. None implements this size check. The other debt edits concern different entries; the shared test changes in #4627, #5021 and #7201 add platform/ABI includes. These are not implementation dependencies.

#6044 reorganizes the tests and deletes the old core/libs/tests/Formatting.cpp path. That is a genuine future modify/delete integration concern: preserve or port CheckSizeLimits and its invocation when combining the two PRs. It does not provide functionality this fix requires, so Depends on remains none. The overlap bot may continue to report shared files it cannot compare until those older branches are rebased.

Checklist

xXPBSXx and others added 2 commits October 21, 2026 20:34
…xceeds INT_MAX

FreeBSD 11.4 vsnprintf checks the size minus the terminator against INT_MAX
before formatting and, when it is larger, sets errno to EOVERFLOW, writes a
terminator to the buffer and returns EOF (lib/libc/stdio/vsnprintf.c:61-67).
The guest formatted into the buffer and returned the length.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the fix error Closes an issue (Fixes #N) label Oct 21, 2026
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 9232 docs/dev/debt/libc.md L54
PR 9699 core/libs/tests/Formatting.cpp L371

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 3965 docs/dev/debt/libc.md
PR 5021 core/libs/tests/Formatting.cpp
docs/dev/debt/libc.md
PR 5303 docs/dev/debt/libc.md
PR 6044 core/libs/tests/Formatting.cpp
PR 6469 docs/dev/debt/libc.md
PR 6565 docs/dev/debt/libc.md
PR 6879 docs/dev/debt/libc.md
PR 7081 docs/dev/debt/libc.md
PR 7201 core/libs/tests/Formatting.cpp
PR 7447 docs/dev/debt/libc.md
PR 7648 docs/dev/debt/libc.md
PR 7715 docs/dev/debt/libc.md
PR 8150 docs/dev/debt/libc.md
PR 8658 core/libs/tests/Formatting.cpp
PR 8965 docs/dev/debt/libc.md
PR 9001 docs/dev/debt/libc.md
PR 9038 docs/dev/debt/libc.md
PR 9133 docs/dev/debt/libc.md
PR 9170 docs/dev/debt/libc.md
PR 9408 docs/dev/debt/libc.md
PR 9447 docs/dev/debt/libc.md
PR 9526 docs/dev/debt/libc.md
PR 9527 core/libs/prx/libc/src/Formatting.cpp
docs/dev/debt/libc.md
PR 9669 docs/dev/debt/libc.md

@xXPBSXx

xXPBSXx commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Linux data point for 595dcacdf2 (WSL2, Ubuntu 24.04, kernel 6.18 - a virtualised kernel, not bare-metal Linux; GCC 13.3, Release, BUILD_TESTING=ON), this head merged onto main df2569d2c5 (clean merge). I built guest_formatting_tests and ran the executable directly (exit code, not through ctest):

  • With the PR: exit code 0.
  • Without it (the PR's test sources on top of main, with core/libs/prx/libc/src/Formatting.cpp reset to main): it aborts (SIGABRT, exit code 134).
    Not tested: macOS, Windows, the rest of the test suite.

AI-assisted (Claude Code).

@adriadam10 adriadam10 self-assigned this Oct 22, 2026

@harisibrar1 harisibrar1 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked against main a6646f8 and FreeBSD 11.4 lib/libc/stdio/snprintf.c / vsnprintf.c:

  • FreeBSD decrements a nonzero n and then rejects n > INT_MAX with EOVERFLOW, *str = '\0' and EOF. So INT_MAX + 1 is accepted and INT_MAX + 2 and up are rejected, with no clamping and no formatting. SizeOverflowsInt matches that boundary, the errno value (84) and the single NUL write. The null-buffer guard is the one deviation, and the debt entry documents it.
  • The check runs before va_start, so %n targets are never touched, as on FreeBSD.
  • Main still has no size check in snprintf or vsnprintf, and this head merges with main cleanly.
  • Minor: the new debt bullet is separated from the list above it by a blank line, so it renders as its own list.

Reviewed with AI assistance (Claude Code); the checks above were run locally on my machine.

@adriadam10
adriadam10 merged commit cbcb083 into boykopovar:main Oct 22, 2026
18 of 19 checks passed
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the size guard against FreeBSD 11.4 snprintf.c/vsnprintf.c (n - 1 > INT_MAX → EOVERFLOW, NUL, -1), the Windows and POSIX paths of snprintf, vsnprintf, and that guest_formatting pins the oversized, boundary and untouched-%n cases (fails without the change, confirmed by @xXPBSXx on Linux); merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs fix error Closes an issue (Fixes #N) libc

Projects

None yet

Development

Successfully merging this pull request may close these issues.

libc: snprintf and vsnprintf format into the buffer when the size exceeds INT_MAX, FreeBSD 11.4 fails with EOVERFLOW

4 participants