Skip to content

fix(libc): reject a wcsncat_s copy only when its written and read ranges overlap - #9290

Merged
oneandonlydean merged 2 commits into
boykopovar:mainfrom
thuanquang:fix/wcsncat-s-overlap-console-rule
Oct 23, 2026
Merged

oneandonlydean merged 2 commits into
boykopovar:mainfrom
thuanquang:fix/wcsncat-s-overlap-console-rule

Conversation

@thuanquang

@thuanquang thuanquang commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

What

wcsncat_s refuses a copy as overlapping when the source lies within length + 1 units of the write position (Strings.cpp:852-858 on main), so it also refuses copies whose ranges don't meet. @oneandonlydean read the console's own wcsncat_s in libc.prx (the module a retail title ships; read, not run on a console): it rejects a copy only when the bytes it writes, [dest + len1, dest + len1 + len2), intersect the bytes it reads, [src, src + len2). For "abc" with src = dest + 1 and count = 2 it writes indices 3-4 and reads 1-2, so the console copies, returns 0 and leaves "abcbc", while main returns EINVAL (22) and clears dest[0]. This takes the console's rule. He asked for it as a separate PR on #8269 (comment).

  • The length check and its ERANGE (34) are unchanged; the same reading found they match.
  • wcscat_s calls wcsncat_s with an unbounded count, so it gets the same rule; its own console code was not read.
  • wmemcpy_s (Strings.cpp:776) and wcsncpy_s (:817) have similar distance checks; they are left as they are, since nobody has read them on the console.
  • fix(libc): use the Annex K size limit and codes of the PS5 libc, as measured on a PS5 #8965 (open) changes this function's size-limit check, not the overlap check; the two merge cleanly.
  • docs/dev/debt/libc.md gets an entry: the rule follows a static reading of the console's wcsncat_s (not run on a console), wcscat_s inherits it unread, and main's narrow strncat_s keeps a wider rule ([dest, dest + used + length + 1) against [src, src + length + 1), from a998987), so it refuses some copies whose wide equivalent now proceeds.

Tested

Windows 11 26200, WinLibs GCC 15.2.0 r7, Release, BUILD_TESTING=ON. Rebased onto main 30ecc1b7b3 to add the debt entry (the only conflict was that entry's neighbour in docs/dev/debt/libc.md; the code and test are unchanged): with this change's Strings.cpp and test applied to main 9d6adeb550, guest_wide_strings passes 3 of 3. The runs below are from main 4ec064d29.

  • guest_wide_strings gains @oneandonlydean's two cases: the non-overlapping copy above (returns 0, leaves "abcbc"), and u"abc\0xyz" with src = dest + 4 and count = 3, where the read range starts inside the write range (returns 22 with dest[0] cleared).
  • Green: passes 3 of 3 with this change.
  • Red: with main's Strings.cpp and the new test, the test aborts (exit 0xc0000409). With the first new row removed it passes, so the non-overlapping copy is the row main fails.
  • Control: with the overlap check removed from this change, the test fails, so the second row pins the rejection.
  • python tools/check_conventions.py --base origin/main with this title and body passes.
  • @xXPBSXx on Linux (WSL2 Ubuntu 24.04, GCC 13.3, Release), this head merged onto main df2569d2c5: guest_wide_strings exits 0 with the change; with main's Strings.cpp and the PR's tests it aborts (SIGABRT, exit 134).
  • Not tested: on a console; the other _s functions' overlap rules.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes (Claude Code)

🤖 Generated with Claude Code

@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 10270 core/libs/prx/libc/src/Strings.cpp L849-875
docs/dev/debt/libc.md L40

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/prx/libc/src/Strings.cpp
docs/dev/debt/libc.md
PR 5303 docs/dev/debt/libc.md
PR 6044 core/libs/tests/GuestWideStrings.cpp
PR 7081 docs/dev/debt/libc.md
PR 7201 core/libs/prx/libc/src/Strings.cpp
PR 7648 docs/dev/debt/libc.md
PR 7715 docs/dev/debt/libc.md
PR 8150 docs/dev/debt/libc.md
PR 8306 docs/dev/debt/libc.md
PR 8965 core/libs/prx/libc/src/Strings.cpp
core/libs/tests/GuestWideStrings.cpp
docs/dev/debt/libc.md
PR 9133 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 docs/dev/debt/libc.md
PR 9552 docs/dev/debt/libc.md
PR 9669 docs/dev/debt/libc.md
PR 9929 docs/dev/debt/libc.md

@github-actions github-actions Bot added the libc label Oct 22, 2026
@xXPBSXx

xXPBSXx commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Linux data point for edd4ae2936 (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_wide_string_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/Strings.cpp reset to main): it aborts (SIGABRT, exit code 134).
    Not tested: macOS, Windows, the rest of the test suite.

AI-assisted (Claude Code).

@harisibrar1 harisibrar1 left a comment

Copy link
Copy Markdown
Contributor

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 the console-module reading of wcsncat_s on #8269:

  • The new condition is exactly the write range [dest+used, dest+used+length) against the read range [src, src+length). I worked both new rows through it by hand, and a zero-length copy still never rejects, as on main. The head merges cleanly with main.
  • Could you add a debt line in docs/dev/debt/libc.md? It would say that the wcsncat_s overlap rule follows a static reading of libc.prx (not run on a console) and that wcscat_s inherits it without its own code being read. The PR doesn't touch the debt file, and the existing _s entry only covers the size limits.
  • Main's narrow strncat_s uses a wider rule ([dest, dest+used+length+1) against [src, src+length+1), from a998987), and GuestStrings.cpp:137 pins strncat_s(catOverlap + 3, 10, catOverlap, 3) == 22, where the written bytes [6,9) and the read bytes [0,3) don't meet. With this PR, the wide equivalent of that call copies. That's fine if the console really differs, but it may be worth noting in the debt file, or opening an issue so someone reads the narrow function's console code too.

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

thuanquang and others added 2 commits October 23, 2026 15:19
…ges overlap

Co-Authored-By: Claude <noreply@anthropic.com>
The debt entry says the rule follows a static reading of the console's
wcsncat_s in libc.prx, that wcscat_s inherits it unread, and that the
narrow strncat_s keeps a wider rule, as asked in review.

Co-Authored-By: Claude <noreply@anthropic.com>
@thuanquang
thuanquang force-pushed the fix/wcsncat-s-overlap-console-rule branch from edd4ae2 to e53f782 Compare October 23, 2026 08:20
@thuanquang

Copy link
Copy Markdown
Contributor Author

@harisibrar1 Thanks for checking it against the console reading. Added in e53f78214a: an entry in docs/dev/debt/libc.md that says the wcsncat_s rule comes from a static reading of the console's libc.prx (not run on a console), that wcscat_s inherits it without its own code being read, and that main's narrow strncat_s keeps the wider rule from a998987, so it refuses some copies (such as the GuestStrings.cpp:137 case) whose wide equivalent now proceeds. To add it I rebased onto main 30ecc1b7b3; the code and test are unchanged, and guest_wide_strings still passes 3 of 3.

@github-actions github-actions Bot added the docs label Oct 23, 2026
@oneandonlydean oneandonlydean self-assigned this Oct 23, 2026
@oneandonlydean
oneandonlydean merged commit 216e9a6 into boykopovar:main Oct 23, 2026
18 of 19 checks passed
orezraey added a commit to orezraey/AnyPS5 that referenced this pull request Oct 23, 2026
wcsncat_s and wcscat_s keep the overlap rule boykopovar#9290 read from the console's wcsncat_s, which leaves the terminators out of both ranges; strcat_s and strncat_s keep the measured rule that counts them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants