Skip to content

fix(libc): write the constraint message and return from the default handler - #9935

Closed
orezraey wants to merge 8 commits into
boykopovar:mainfrom
orezraey:fix/libc-default-constraint-handler-returns
Closed

orezraey wants to merge 8 commits into
boykopovar:mainfrom
orezraey:fix/libc-default-constraint-handler-returns

Conversation

@orezraey

@orezraey orezraey commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

What

The default runtime-constraint handler of libc called std::abort() without writing anything. It now writes the message and a newline to the guest stderr and returns, so an _s function called with no handler installed returns its error to the title instead of ending it.

This follows the default handler of the libc.prx titles ship in sce_module/, which this module stands for. JJenkx read it from the disassembly of the copies in PPSA13934, PPSA07923 and PPSA05916 (#9087 comment): set_constraint_handler_s stores handler ? handler : default, and the default calls fputs(message, stderr), fputc('\n', stderr) and returns, while abort_handler_s makes the same two calls and then calls abort. The wcsrtombs_s debt entry already describes that handler the same way. The console's libSceLibcInternal.sprx (firmware 13.60, measured for #9087) has a different default that ends the process after writing; that module is not this one, and the debt entry says so.

libSceAmpr's re-export shares the header, so its default handler changes the same way.

Tested

  • Head after merging main (f80ce6cec1, with docs(libc): record feholdexcept, fegetexcept, setlocale and strcasecmp as measured on a PS5 #11121): 7aa063d91c. The only conflict was docs/dev/debt/libc.md: kept main's localeconv entry (and its removal of the setlocale entry) and this PR's set_constraint_handler_s wording. guest_safe_errors passes three runs in a row on Windows (MinGW) and on Linux (WSL Ubuntu, GCC); check_conventions passes.
  • Head after merging main (f26434afa3, with refactor(libs): add full export headers for libs #10706): 0bae438695. The test now takes its declarations from the libc headers; SonyCrt.hpp includes Api.hpp instead of declaring fputs/fputc again. guest_safe_errors, guest_crt, guest_bounds_checking, guest_qsort and guest_wide_strings pass three runs in a row on Windows (MinGW) and on Linux (WSL Ubuntu, GCC); public_headers audit and check_conventions pass.
  • Head after merging main (a2c36d4ed6, with fix(libc): report strerror_s violations as titles' libc.prx does #12589): 9260172a4c. guest_safe_errors now expects the default handler to write strerror_s: bad argument\n twice, the message fix(libc): report strerror_s violations as titles' libc.prx does #12589 passes. guest_safe_errors, guest_crt and guest_bounds_checking pass three runs in a row on Windows (MinGW, GCC 15.2) and on Linux (WSL Ubuntu, GCC); check_conventions passes.
    Windows 11, MinGW (winlibs).
  • guest_safe_errors now installs the default handler with set_constraint_handler_s(NULL), redirects the guest stderr to a file and checks that two strerror_s violations return EINVAL (22) and write strerror_s: bad argument\n twice. On main the test ends with 0xc0000409 at the first violation; with the change it passes.
  • guest_safe_errors, guest_crt, guest_bounds_checking, guest_qsort and guest_wide_strings pass three runs in a row.
  • tools/check_conventions.py passes.
  • Head after merging main (ba8dcc6ae8, with test(libc): register the macOS write watch fault tests once #13220, which removes the duplicate write-watch test names that failed the macOS jobs): d7b7f9ad59. guest_safe_errors passes three runs in a row on Windows (MinGW, GCC 15.2). tools/check_conventions.py passes.
  • Head after merging main (98346b748c, with fix(libc): sort like the console's qsort and qsort_s #9688's qsort_s entry): 91d71873be. The only conflict was docs/dev/debt/libc.md: kept main's qsort_s entry and this PR's set_constraint_handler_s wording. guest_safe_errors passes three runs in a row on Windows (MinGW, GCC 15.2) and on Linux (WSL, GCC 16.2). tools/check_conventions.py passes.

Checklist

…andler

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MarcZX10

Copy link
Copy Markdown
Contributor

I reviewed the implementation and regression path. The handler now mirrors the libc.prx behavior cited from the three disassemblies: write the supplied message and newline to guest stderr, then return; the test verifies both repeated writes and that the violating calls return EINVAL. The debt entry distinguishes that module from the measured libSceLibcInternal.sprx behavior, and the #9087 dependency is appropriate because the two changes share the constraint-handler documentation. I found no code defect in this diff.

I independently ran git diff --check upstream/main...refs/review/9935 and python3 -I tools/check_conventions.py --base upstream/main --head refs/review/9935; both pass. The author reports the focused and neighboring Windows tests passing. Hosted eligibility, conventions, intake, labels, report, relinker and ASan checks pass; the Windows relinker check is still running, while Linux/Windows product jobs are skipped by policy. The exact head is four commits behind current main; please refresh it and rerun guest_safe_errors on the final head before merge. I did not run that guest test locally.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@orezraey

Copy link
Copy Markdown
Contributor Author

Thanks. I merged current main (24c6cc33f0) into the branch in f96b6d2fb8; it applied without conflicts. On that head guest_safe_errors, guest_crt, guest_bounds_checking, guest_qsort and guest_wide_strings pass three runs in a row on Windows, and tools/check_conventions.py passes.

@github-actions

github-actions Bot commented Oct 22, 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 11621 docs/dev/debt/libc.md L96
PR 12598 docs/dev/debt/libc.md L96-97

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 5021 docs/dev/debt/libc.md
PR 5303 docs/dev/debt/libc.md
PR 6819 docs/dev/debt/libc.md
PR 7386 docs/dev/debt/libc.md
PR 7447 docs/dev/debt/libc.md
PR 7715 docs/dev/debt/libc.md
PR 8150 docs/dev/debt/libc.md
PR 8965 docs/dev/debt/libc.md
PR 9340 docs/dev/debt/libc.md
PR 9371 docs/dev/debt/libc.md
PR 9447 docs/dev/debt/libc.md
PR 9526 docs/dev/debt/libc.md
PR 9929 docs/dev/debt/libc.md
PR 10113 docs/dev/debt/libc.md
PR 10226 docs/dev/debt/libc.md
PR 10245 docs/dev/debt/libc.md
PR 10250 docs/dev/debt/libc.md
PR 10253 docs/dev/debt/libc.md
PR 10265 docs/dev/debt/libc.md
PR 10274 docs/dev/debt/libc.md
PR 10325 docs/dev/debt/libc.md
PR 10328 docs/dev/debt/libc.md
PR 10345 docs/dev/debt/libc.md
PR 10349 docs/dev/debt/libc.md
PR 10405 docs/dev/debt/libc.md
PR 10506 docs/dev/debt/libc.md
PR 10510 docs/dev/debt/libc.md
PR 10512 docs/dev/debt/libc.md
PR 10565 docs/dev/debt/libc.md
PR 10987 docs/dev/debt/libc.md
PR 11260 docs/dev/debt/libc.md
PR 11262 docs/dev/debt/libc.md
PR 11266 docs/dev/debt/libc.md
PR 11632 docs/dev/debt/libc.md
PR 11634 docs/dev/debt/libc.md
PR 11831 docs/dev/debt/libc.md
PR 11907 docs/dev/debt/libc.md
PR 12122 docs/dev/debt/libc.md
PR 12397 docs/dev/debt/libc.md
PR 12560 docs/dev/debt/libc.md
PR 12682 docs/dev/debt/libc.md
PR 12688 docs/dev/debt/libc.md
PR 12700 docs/dev/debt/libc.md
PR 12965 docs/dev/debt/libc.md
PR 13119 docs/dev/debt/libc.md
PR 13390 docs/dev/debt/libc.md
PR 13804 docs/dev/debt/libc.md
PR 14212 docs/dev/debt/libc.md
PR 14585 docs/dev/debt/libc.md
PR 14610 docs/dev/debt/libc.md

@MarcZX10

Copy link
Copy Markdown
Contributor

I reviewed head f96b6d2fb8e39db1f0fb8d9c5f3777aee65833bc against current main fcd455870cb9610d929f744719a4d20ffd544499. git diff --check and current-main conventions both pass. The default-handler implementation matches the described shipped-libc disassembly, and the regression redirects guest stderr, checks two exact messages, and verifies both violations return EINVAL. Hosted eligibility, conventions, intake, report, Linux/Windows/macOS relinker, ASan, register-context and write-watch checks pass; Linux and Windows product builds are skipped by policy. I did not run the C++ test locally due to the full root filesystem.

The stated dependency on #9087 is still open; both update the set_constraint_handler_s debt entry, so please retain both changes during integration and rerun the focused safe-error tests after rebasing. No additional issue found in this pass.

@MarcZX10 MarcZX10 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.

Reviewed exact head f96b6d2fb8e39db1f0fb8d9c5f3777aee65833bc against current main 895f47f4eeaf56048b9a7080b7c01a9a47cf0ecf. The default handler writes its supplied message and newline to guest stderr and returns; this is the behavior attributed to the three shipped libc.prx disassemblies, and the debt entry separately retains the measured libSceLibcInternal.sprx process-termination difference. The regression redirects guest stderr, checks two exact messages, and verifies both violating calls return EINVAL. The author reports five focused/neighboring Windows tests passing three repetitions after merging main; I did not rerun the C++ suite locally. I independently ran current-main git diff --check and conventions; both pass. Hosted eligibility, conventions, intake, labels, relinker, ASan, register-context and write-watch checks pass; full Linux/Windows product builds are skipped by repository policy. The declared #9087 debt-text integration is appropriate and no intervening current-main commit changes the reviewed source, test or debt paths. I found no remaining issue in the reviewed change.

@orezraey

Copy link
Copy Markdown
Contributor Author

@Pouare514 @JJenkx: #7081 and #9688 change the same lines as this PR in core/libs/prx/libc/include/SonyCrt.hpp and docs/dev/debt/libc.md (git merge-tree of both heads over current main conflicts there). I propose they land first; I will then merge main into this branch, keeping both sides' behaviour, and rerun its tests. Depends on now lists them (merge order only).

@orezraey

Copy link
Copy Markdown
Contributor Author

@boykopovar #10706 replaces the local extern "C" declaration blocks in the tests with the new headers, and this PR adds declarations to those blocks in core/libs/tests/GuestSafeErrors.cpp, so the two conflict there; git merge-tree of both heads shows no other conflict. I suggest #10706 lands first; this PR will then be refreshed to use the new headers instead of its own declarations, with its tests unchanged. I added #10706 to Depends on (merge order only).

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 24, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added needs rebase Conflicts with main, rebase needed and removed needs rebase Conflicts with main, rebase needed labels Oct 24, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 27, 2026
@orezraey

Copy link
Copy Markdown
Contributor Author

@B-Howell the overlap check lists #11621 here: it adds entries next to the set_constraint_handler_s one this PR edits in docs/dev/debt/libc.md, and git reports a textual conflict there. The code does not overlap. I propose #11621 lands first; I will merge main into this branch afterwards and keep your entries. Depends on now lists it.

@JJenkx

JJenkx commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

@orezraey a note for the merge order: #12574 makes asctime_s and ctime_s call the constraint handler on every violation, as titles' libc.prx does (measured natively on PPSA07923 and PPSA05916). With main's aborting default handler, that would end a title that never installs one, so #12574 lists this PR as a dependency.

The two conflict only on the set_constraint_handler_s debt line. #12574 replaces its last sentence ("ctime_s and asctime_s follow the Dinkumware 26-byte format ... the console's exact checks are not verified") with one about the unverified localtime_s failure path, and keeps the rest of your wording. Merged together on main 319621ba2e, guest_crt, guest_safe_errors, guest_bounds_checking, guest_qsort and guest_strftime pass 3/3 on Windows.

AI-assisted: yes (Claude Code).

@JJenkx

JJenkx commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

@orezraey a heads-up from #12589, which makes strerror_s pass "strerror_s: bad argument" and a null pointer to the constraint handler, as titles' libc.prx does (measured natively on PPSA07923 and PPSA05916). This PR's check that the default handler writes "strerror_s\nstrerror_s\n" would then need "strerror_s: bad argument\nstrerror_s: bad argument\n".

Whichever of the two lands second can update that one line. #12589 notes it, and with that change a local merge of #9935 and #12589 passes guest_safe_errors, guest_crt and guest_bounds_checking 3/3 on Windows. Nothing else conflicts.

AI-assisted: yes (Claude Code).

@orezraey

Copy link
Copy Markdown
Contributor Author

@JJenkx agreed: this PR lands first and #12574 follows it, with the set_constraint_handler_s debt line resolved as you describe.

@orezraey

Copy link
Copy Markdown
Contributor Author

@JJenkx either order works. If #12589 lands first, I'll merge main into this PR and #10270 (which contains it) and change the expected default-handler output to "strerror_s: bad argument\nstrerror_s: bad argument\n"; if this PR lands first, your one-line change applies. I've added #12589 to Depends on (merge order only).

@orezraey

Copy link
Copy Markdown
Contributor Author

@JJenkx #12598 conflicts with this PR only in docs/dev/debt/libc.md, where it rewrites the qsort_s entry right after the set_constraint_handler_s entry this PR edits; the code merges cleanly. I propose #12598 lands first; I will merge main into this branch afterwards keeping both entries. Added to Depends on (merge order only).

@MarcZX10 MarcZX10 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.

Re-checked — new head, looks good

The current head retains the reviewed default-handler behavior and test, and the rebase passes the available static and hosted checks.

What I ran

Reviewed PR head 7aa063d91cb079e5d82cdb64aff5731288b0406f against base f80ce6cec1cad29aa45bd0bf6d8062a714c18e03; refreshed current main is f46f453a56f91d77e29ce0cbd7fe5bfb2fb69834. The merge preview with current main is clean (tree a678797797647eee21884965d95daac02477dea6). Environment: Linux 6.1.0-54-amd64 x86_64; no local compiler, build, or tests run.

git diff --check upstream/main...refs/review/pr-9935-current
python3 tools/check_conventions.py --base upstream/main
git merge-tree --write-tree upstream/main refs/review/pr-9935-current
gh pr checks 9935 --repo boykopovar/AnyPS5

Results

Check Result
Diff whitespace check Pass; exit 0, no output.
Repository conventions Pass.
Merge preview with current main Clean.
Hosted relinker, ASan, register-context, write-watch, conventions, eligibility, labels, and report checks Pass on this exact head.
Hosted Linux and Windows product jobs Skipped by the workflow.
Focused guest_safe_errors and neighboring tests The author reports three passing runs on Windows and WSL2 on this exact head; not rerun locally.
Unedited output from local and hosted checks
$ git diff --check upstream/main...refs/review/pr-9935-current
(exit 0; no output)
$ python3 tools/check_conventions.py --base upstream/main
All conventions checks passed.
$ git merge-tree --write-tree upstream/main refs/review/pr-9935-current
a678797797647eee21884965d95daac02477dea6
$ gh pr checks 9935 --repo boykopovar/AnyPS5
conventions	pass	17s	https://github.com/boykopovar/AnyPS5/actions/runs/40971070959/job/123787653774
eligibility	pass	5s	https://github.com/boykopovar/AnyPS5/actions/runs/40448684045/job/122005990939
labels	pass	10s	https://github.com/boykopovar/AnyPS5/actions/runs/40971071060/job/123787654343
register-context (macos-15-intel, Debug)	pass	53s	https://github.com/boykopovar/AnyPS5/actions/runs/40448684045/job/122006026989
register-context (macos-15-intel, Release)	pass	1m7s	https://github.com/boykopovar/AnyPS5/actions/runs/40448684045/job/122006027030
register-context (ubuntu-latest, Debug)	pass	31s	https://github.com/boykopovar/AnyPS5/actions/runs/40448684045/job/122006026976
register-context (ubuntu-latest, Release)	pass	28s	https://github.com/boykopovar/AnyPS5/actions/runs/40448684045/job/122006027024
relinker (macos-latest, clang++)	pass	2m54s	https://github.com/boykopovar/AnyPS5/actions/runs/40448684045/job/122006027082
relinker (macos-latest, clang++, x86_64)	pass	5m20s	https://github.com/boykopovar/AnyPS5/actions/runs/40448684045/job/122006027040
relinker (ubuntu-latest, g++)	pass	3m41s	https://github.com/boykopovar/AnyPS5/actions/runs/40448684045/job/122006027013
relinker (windows-latest, g++)	pass	7m57s	https://github.com/boykopovar/AnyPS5/actions/runs/40448684045/job/122006026952
relinker-asan	pass	2m34s	https://github.com/boykopovar/AnyPS5/actions/runs/40448684045/job/122006026978
write-watch (macos-15-intel)	pass	49s	https://github.com/boykopovar/AnyPS5/actions/runs/40448684045/job/122006026977
linux	skipping	0	https://github.com/boykopovar/AnyPS5/actions/runs/40448684045/job/122006028604
windows	skipping	0	https://github.com/boykopovar/AnyPS5/actions/runs/40448684045/job/122006028418
report	pass	38s	https://github.com/boykopovar/AnyPS5/actions/runs/40448684047/job/122005990560
write-watch (macos-15)	pass	37s	https://github.com/boykopovar/AnyPS5/actions/runs/40448684045/job/122006026999

The focused test results are author-reported in the PR description; they are not local runs.

Negative control

Assertion Change to code under test Did the new test fail?
The default handler returns instead of aborting and writes its message and newline The author reports running the added test against main without this PR. Yes; it ended with exit code 0xc0000409 at the first violation.

Findings

Blocking

None found in the current head. It writes the supplied message and newline to guest stderr, then returns to the caller; the test exercises two violations and confirms both return EINVAL and produce the expected output.

Non-blocking

The PR records merge-order dependencies for adjacent libc debt and test changes. Preserve that order if one of those dependent changes lands first.

Sources

Not verified

I did not build or run tests locally, independently reproduce the author's Windows/WSL2 tests, or verify behavior on a physical console. The hosted Linux and Windows product jobs were skipped.

orezraey and others added 3 commits October 30, 2026 06:24
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
boykopovar#12589 passes "strerror_s: bad argument" to the constraint handler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Nov 2, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Nov 2, 2026
@orezraey

orezraey commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

This change reached main with #10270, whose branch contained it: git merge-tree of this head (91d71873be) with main (c2eafe4461) gives main's tree unchanged, so merging this PR now adds nothing. main's default constraint handler already writes the message and returns, and guest_safe_errors has the check this PR added.

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Thank you for this. The change is already on main through #10270 (merging this head with main gives main's tree unchanged), so we will close this one as already merged.

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