Repository navigation
feat(libScePad): check the arguments of thirteen exports as firmware 13.42 does - #15105
Conversation
…13.42 does From @BunnyFl's disassembly of the 13.42 libScePad.sprx (boykopovar#15087): GetDeviceInfo, GetCapability, GetVersionInfo, GetIdleCount, GetControllerIdleCount, GetActiveTime, SetPS4BcVibrationMode, GetInfoByPortType, GetExtensionUnitInfo, ReadHistory, OpenExt2, ReadStateExt and GetDeviceId get their checks in order, and keep throwing where the console asks the pad driver or reads state that isn't modelled. ReadHistory's third argument is a byte size of 0x960, and OpenExt2 takes (userId, 0x10, param).
BunnyFl
left a comment
There was a problem hiding this comment.
Thanks for building on the reading. One order is wrong, and that's my fault: my comment on #15087 gave GetInfoByPortType's checks without their order and missed a null check. I've corrected that row.
Firmware 13.42's libScePad.sprx (sha256 57eab7054e10d95ba0028dbd4808f94eb34c7663333a2b7ca9f68dc90cf62c60), read from disassembly only, not measured on a console:
0xeac0 scePadGetInfoByPortType: jmp 0xe8a0(info, portType) ; arguments swapped
e8bb: ebx = 0x80920001
e8c7: test rdi, rdi ; je ea98 ; info == NULL -> 0x80920001 (first)
e8d0: cmpl [0x2401c], 0 ; js ea8f ; fd < 0 -> 0x80920005
e8dd: test esi, 0xfffffffd ; jne ea98 ; type not 0 or 2 -> 0x80920001
scePadGetInfoByPortTypemust checkinfo == nullptrfirst, then initialisation, then the type. The PR checks the type first, so two test lines pin answers the console doesn't give:GetInfoByPortType(1, data)before init returns0x80920005on the console, notINVALID_ARG.GetInfoByPortType(2, nullptr)before init returns0x80920001, notNOT_INITIALIZED.
- A note on
scePadReadStateExt, not blocking, since it followsmain'sscePadReadState. The shared routine0x1a70(handle, data, 1)testsdata == NULL(0x80920001) before anything else, then zero-fills the record and calls0x1e50. That one checks initialisation (0x80920005) before the handle, as I noted on #15047. So the console answers(12345, data)before init with0x80920005, and(12345, nullptr)with0x80920001.main'sscePadReadStatehas the same handle-first order. If you'd rather not change both here, a debt line saying the order followsmain'sReadState, not the module, would cover it. - Optional:
scePadGetExtensionUnitInfoalso returns0x80920101when the flag at0x24020isn't 1, after the null check and before the handle lookup. Since the PR throws past its checks, leaving it out invents nothing.
Everything else matches the reading: the fd/initialisation checks, GetIdleCount's 0, the handle == 0 || count == NULL pair, ReadHistory's exact 0x960, OpenExt2's 0x10/null check, and GetDeviceId writing -1 before the lookup. Each still throws past its checks.
Build and test on Windows (WinLibs GCC, BUILD_TESTING=ON), PR merged into current main: guest_pad_firmware_checks and guest_pad pass. main's Export.cpp doesn't compile against the new header signatures, so there's no main control.
@BunnyFl's corrected reading of 0xe8a0: a null output is refused first, then initialisation, then a type other than 0 or 2. The test no longer pins ReadStateExt's handle-first order before init, which follows this library's ReadState rather than the module; the debt entry says so.
|
@BunnyFl Thanks for the correction and the build.
The entry is back under 1000 characters in a follow-up commit. |
BunnyFl
left a comment
There was a problem hiding this comment.
Thanks, @zaryabzip. Re-reviewed at 6b856235a8. GetInfoByPortType now checks null info, then initialisation, then the type, as 0xe8a0 does. The rows pin (1, data) to 0x80920005 before init and a null output to 0x80920001 both before and after. The ReadStateExt debt route is fine.
Build and test on Windows (WinLibs GCC, BUILD_TESTING=ON), merged into current main: guest_pad_firmware_checks and guest_pad pass.
One nit: the longer debt entry is 999 characters, right at the limit, so the next edit to it will need trimming.
|
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.
|
What
Thirteen libScePad exports get the argument checks and order of firmware 13.42's
libScePad.sprx, from @BunnyFl's disassembly (module SHA-25657eab7054e10d95ba0028dbd4808f94eb34c7663333a2b7ca9f68dc90cf62c60), not a console run. Past their checks they still throw: the console asks the pad driver there, or reads state that isn't modelled. Before this, every call threw at once.GetDeviceInfo(int32, info*),GetCapability(int32, uint32*),GetVersionInfo(int32, info*)0x80920005; null output0x80920001GetIdleCount(userId)scePadInitit returns 0 with no errorGetControllerIdleCount(handle, uint64*)0x80920001GetActiveTime(int32, int32, uint64*)SetPS4BcVibrationMode(uint8)GetInfoByPortType(type, info*)0x80920001; not initialised; a type other than 0 or 20x80920001(BunnyFl's corrected order in review)GetExtensionUnitInfo(handle, info*)0x80920101), then a driver ioctl; not modelledReadHistory(handle, data*, uint32 size)0x80920001OpenExt2(int32 userId, int32, param*)param,0x80920001ReadStateExt(handle, data*)scePadReadStatechecks (handle, then output); the module checks the output first and initialisation before the handle, recorded as debtGetDeviceId(handle, int32*)0x80920003A closed device fd counts as not initialised, since AnyPS5 has no pad device. Two signatures change to match the reading.
ReadHistory's third argument is a byte size, not a count; 0x960 is 30 of the 72-byte history records the debt file already lists.OpenExt2takes(int32 userId, int32, const void*), not(uint64, int32, const void*). No other code calls either.The debt file gets two entries for these, with the module hash. No function count changes: every call still ends in
NotImplemented_nid_no_patchpast its checks.GuestPadFirmwareChecks.cppchecks each rejection before and afterscePadInit, and that nothing is written on the refused paths, exceptGetDeviceId's -1 for an unknown handle. It stops at the last check, so it doesn't require the driver paths to throw. It callsReadHistoryandOpenExt2through casts, so it also builds onmain.Overlap:
git merge-treemerges this cleanly withmainand every open pull request. The new constants sit afterPAD_LEONARDO_UNIQUE_DATA_SIZE, away from the lines #14124 changes.Tested
Windows 11 (WinLibs GCC 15.2.0 r7) and Ubuntu 24.04 on WSL2 (GCC 13.3.0), at d094933 merged into
main1d91f29. That commit has the same code as the head, with the two constants defined a few lines earlier.Pad.hpprebuild with none, on both hosts.guest_pad,guest_pad_vibration_mode,guest_pad_controller_queries,guest_pad_firmware_checks,guest_pad_audio_route,pad_rumble_intensityandguest_offline_imports: 7 of 7 on both hosts.guest_pad_firmware_checkspasses 3 of 3.mainwith only the new test file and its CMake block, it builds and aborts at its first call withscePadGetDeviceInfo not implemented(Windows exit 0xc0000409, Linux SIGABRT).tools/check_conventions.pypasses.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)