Skip to content

fix(libSceShare): copy guest pointers before use in capture, request ID and title exports - #16195

Open
MarcZX10 wants to merge 11 commits into
boykopovar:mainfrom
MarcZX10:fix-share-validates-guest-pointers
Open

MarcZX10 wants to merge 11 commits into
boykopovar:mainfrom
MarcZX10:fix-share-validates-guest-pointers

Conversation

@MarcZX10

@MarcZX10 MarcZX10 commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

What

Guest pointers in four libSceShare exports were dereferenced directly after at most a null check, so an unmapped or read-only pointer faulted the host instead of returning an error. Sibling exports in the same file (sceShareGetCurrentStatus, sceShareGetRunningStatus) already use GuestCopyOut_nid_no_patch.

  • sceShareCaptureScreenshot and sceShareCaptureVideoClip: the req_id write goes through a guarded copy-out (helper WriteRequestIdInvalid). An unwritable pointer returns ERROR_INVALID_PARAM; a null pointer still returns ERROR_NOT_SUPPORTED without a write.
  • sceShareCaptureScreenshotExtended and sceShareCaptureVideoClipExtended: the parameter block is copied in with GuestCopyIn_nid_no_patch before size and the fields are read, and the request ID is written through the guarded copy-out. An unreadable block or unwritable pointer returns ERROR_INVALID_PARAM, as a null pointer already did.
  • sceShareSetContentParamForApplicationTitle: the title is read with CopyGuestString_nid_no_patch (maximum 5120 bytes). The accepted range is unchanged (1 to 5119 bytes); an unreadable pointer returns ERROR_INVALID_PARAM.

The fallbacks for unreadable or unwritable memory are host-protection choices. Console behaviour for those pointers is not measured and is recorded in docs/dev/debt/libSceShare.md.

Commits: four fix/test pairs, one per export (the helper is added with the first call site), and a final docs commit for the debt entries.

Possible conflict: docs/dev/debt/libSceShare.md is also changed by #14212 (blank lines between entries). The two may conflict textually in the Unknown function info section; the content is different.

Tested

  • Run directly: python3 tools/check_conventions.py --base upstream/main passed; git diff --check upstream/main clean; test_public_headers.py --audit-only passed (406 libraries, 30604 exports); tools/pr_precheck.py (no-build) passed diff, conventions, ABI-audit and test-quality checks, and reports "checks incomplete" only because builds are excluded.
  • Manual overlap check (tools/pr_overlap.py did not complete against the live API): open PRs touching libSceShare files were checked; test(libSceAudioIn): audit microphone mute state bounds, stream format and sampling rate rejection #15899 changes only libSceShare/CMakeLists.txt, docs(debt): separate entries with blank lines and drop derived export totals #14212 touches the debt doc only as described above.
  • NOT RUN YET: the build and ctest -R '^guest_share$' (target guest_share_tests). It is queued behind other builds on the shared lock; no result exists for this head yet.
  • NOT RUN YET: the negative control (fixes removed, test expected to fail). Not run against the unfixed code.
  • Not run: Windows, macOS, the full test suite, any title or console.

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

…enshot

sceShareCaptureScreenshot stored -1 through req_id after only a null
check, so an unmapped or read-only pointer faulted the host. The write
now goes through GuestCopyOut_nid_no_patch, as sceShareGetCurrentStatus
does, and an unwritable pointer returns ERROR_INVALID_PARAM. A null
pointer still returns ERROR_NOT_SUPPORTED without a write.
…creenshot

An unmapped request ID pointer must return ERROR_INVALID_PARAM instead of
faulting the host. Without the copy-out fix the call writes to address
0x10 and the test process crashes.
…oClip

sceShareCaptureVideoClip stored -1 through req_id after only a null
check, so an unmapped or read-only pointer faulted the host. It now
uses the same guarded copy-out as sceShareCaptureScreenshot, and an
unwritable pointer returns ERROR_INVALID_PARAM.
…ideoClip

An unmapped request ID pointer must return ERROR_INVALID_PARAM instead of
faulting the host. Without the copy-out fix the call writes to address
0x10 and the test process crashes.
…rs from the guest

sceShareCaptureScreenshotExtended read param->size and then wrote the
request ID directly, so an unmapped parameter block or request ID
pointer faulted the host. The parameter block is now copied in with
GuestCopyIn_nid_no_patch and the request ID is written through the
guarded copy-out. An unreadable block or unwritable pointer returns
ERROR_INVALID_PARAM, as a null pointer already did.
…enshotExtended

An unmapped parameter block and an unmapped request ID pointer must both
return ERROR_INVALID_PARAM. Without the copy fix the call reads address
0x10 and the test process crashes.
…s from the guest

sceShareCaptureVideoClipExtended read the parameter block and wrote the
request ID directly, so an unmapped pointer faulted the host. The block
is now copied in with GuestCopyIn_nid_no_patch and the request ID goes
through the guarded copy-out before the field checks, as before. An
unreadable block or unwritable pointer returns ERROR_INVALID_PARAM.
…oClipExtended

An unmapped parameter block and an unmapped request ID pointer must both
return ERROR_INVALID_PARAM. Without the copy fix the call reads address
0x10 and the test process crashes.
… string through a guest copy

sceShareSetContentParamForApplicationTitle scanned the title byte by byte
from the guest pointer, so an unmapped pointer faulted the host. The
string is now copied with CopyGuestString_nid_no_patch, limited to 5120
bytes. An unreadable pointer, an empty title or one without a terminator
within 5120 bytes returns ERROR_INVALID_PARAM, as before.
…amForApplicationTitle

An unmapped title pointer must return ERROR_INVALID_PARAM. Without the
guest-copy fix the call reads address 0x10 and the test process crashes.
…nters

The copy-out and copy-in fallbacks added to the capture, request ID and
title exports return ERROR_INVALID_PARAM for unreadable or unwritable
guest memory. What the console returns for those pointers is not
measured, so the debt entries say so. The non-extended capture exports
had no entry and now have one.
@JJenkx

JJenkx commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

Windows check of head 9872d04b22, merged onto main 38fe81a14a (3d885609e4, clean merge).

Setup: Windows 11 Enterprise 22621, WinLibs GCC 15.2 UCRT, Release. The hosted Windows check does not run these tests on pull requests.

  • guest_share passes in 3 of 3 runs, none skipped.

AI-assisted: yes.

This branch has not been deployed

No deployments
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.

2 participants