Skip to content

fix(libc): read guest wide strings as 16-bit units - #1116

Merged
adriadam10 merged 1 commit into
boykopovar:mainfrom
Fishybeing:fix/libc-utf16-strings
Oct 8, 2026
Merged

adriadam10 merged 1 commit into
boykopovar:mainfrom
Fishybeing:fix/libc-utf16-strings

Conversation

@Fishybeing

Copy link
Copy Markdown
Contributor

What

The guest's wchar_t is 16 bits (UTF-16, see WindowsFormatting.hpp; FormattingWide.cpp and _Mbtowcx already use 16-bit units). The string functions in core/libs/prx/libc/src/Strings.cpp took wchar_t* and forwarded to the host's std::wcs*/std::wmem*, which only matches the guest on Windows. On Linux the host's wchar_t is 32 bits, so these read and wrote guest strings in 4-byte units: wcslen of a guest string returned a wrong length, comparisons were wrong, and the copies could overrun the guest buffer.

This implements them on char16_t:

wmemchr, wmemcmp, wmemcpy, wmemmove, wmemset, wcslen, wcscmp, wcsncmp, wcscpy, wcsncpy, wcschr, wcsrchr, wcsstr, wcspbrk, wcsspn

Comparisons are on unsigned code units and return -1, 0 or 1, as the Windows build returned before, so Windows behaviour does not change.

The wide conversions (wcstod, wcstof, wcstold, wcstol, wcstoll, wcstoul, wcstoull, wcscoll, wcsxfrm, wcsrtombs) and swprintf have the same problem on Linux but need a UTF-16 conversion, so they are left for a follow-up and listed in TechnicalDebt.

guest_wide_strings covers every changed function, including code units above 0x7FFF, embedded terminators, wcsncpy padding and truncation, overlapping wmemmove and writes past the requested count.

Tested

  • Windows 11, MinGW-w64 GCC 15.2.0 (WinLibs 15.2.0posix-14.0.0-ucrt-r7), Release, -DBUILD_TESTING=ON, on d5adabae
  • Strings.cpp and GuestWideStrings.cpp build without warnings, also with -Wall -Wextra.
  • ctest -E 'agc|shader|recompiler|Vulkan|vulkan|video_out': 197/197 passed, including guest_wide_strings and guest_wide_formatting. GPU tests not run.
  • Not tested on Linux: I have no Linux machine. The new test is what would have failed there before this change.

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 in TechnicalDebt
  • 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
  • AI-assisted: yes

@github-actions

github-actions Bot commented Oct 7, 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
#1092 docs/dev/TechnicalDebt.md L211
#1095 docs/dev/TechnicalDebt.md L211
#1128 wcschr_nid_postfix
wcscmp_nid_postfix
wcscpy_nid_postfix
wcslen_nid_postfix
wcsncmp_nid_postfix
wcsrchr_nid_postfix
wcsspn_nid_postfix
wmemchr_nid_postfix
wmemcmp_nid_postfix
wmemset_nid_postfix
#1184 docs/dev/TechnicalDebt.md L211

@fisherjoey

Copy link
Copy Markdown
Contributor

This looks like the same change as #1126: both add wcslen_nid_postfix, wcscpy_nid_postfix and wmemcpy_nid_postfix to libc/src/Strings.cpp for 16-bit guest wide strings, with a GuestWideStrings.cpp test under the same guest_wide_strings ctest name. Linking so they can be compared.

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 8, 2026
The guest's wchar_t is 16 bits, but wcslen, wcscmp, wcsncmp, wcscpy, wcsncpy, wcschr, wcsrchr, wcsstr, wcspbrk, wcsspn and the wmem functions forwarded to the host's wchar_t functions, which use 32-bit units on Linux. Implement them on char16_t. The wide conversions that still use the host's wchar_t are listed in TechnicalDebt.
@adriadam10

Copy link
Copy Markdown
Collaborator

Checked the 15 rewrites against the C semantics (unsigned unit comparison, wcsncpy padding/truncation, overlapping wmemmove, empty needle in wcsstr, terminator in wcschr/wcsrchr) and ran guest_wide_strings on Linux: it passes, and fails with Strings.cpp reverted, so it covers the 32-bit host wchar_t case. Full ctest green (424/424).

Only issue was a TechnicalDebt.md adjacency conflict with main; I rebased and kept all lines. Merging once CI is green on the rebased head.

#1126 is the follow-up and will need a rebase after this lands.

@adriadam10
adriadam10 force-pushed the fix/libc-utf16-strings branch from d482c4a to cd39c8d Compare October 8, 2026 07:57
@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 8, 2026
@adriadam10
adriadam10 merged commit c081eb9 into boykopovar:main Oct 8, 2026
4 checks passed
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.

3 participants