Adds SceKernelLoadExecVSHParam (matching JPCSP's reference layout - its
first four fields line up with the existing SceKernelLoadExecParam,
which is why the plain sceKernelLoadExec already worked for
sceKernelLoadExecVSHMs2) and fills in the rest of LoadExecForKernel's
NIDs from JPCSP: real implementations for sceKernelExitVSHVSH/Kernel
(mirrors sceKernelExitGame) and sceKernelLoadExecBufferVSHUsbWlan (loads
an exec from an in-RAM buffer instead of a file - the VSH's "push a game
over USB/WLAN" path), plus UNIMPL stubs for everything JPCSP itself only
knows by NID.
sceKernelLoadExecBufferVSHUsbWlan needed __KernelLoadExec split into a
file-reading front end and a shared __KernelLoadExecFromPtr back end
that both it and the new buffer-based path call into - a pure
extract-method refactor of the single most heavily used boot path in the
emulator. Verified no regression: same 11 passed / 9 pre-existing-failed
split on pspautotests/tests/cpu/*, and loader/bss still passes, before
and after this change.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSNaZnHCjmryS3ziVN9gZU
prefersStatusBarHidden was dead code - it computed an orientation and a
(commented out) user preference, then unconditionally returned false. So the
status bar was only ever hidden on iPhone in landscape, and only because iOS
does that on its own in compact height.
Now it honors bImmersiveMode from the DisplayLayoutConfig matching the current
orientation, so it also applies in portrait and on iPad. Adds the corresponding
checkbox to the iOS system settings, and updates the status bar on rotation and
when the setting is toggled.
Also fixes a missing break in the ROTATE_UPDATED case in System_Notify, and a
static/non-static mismatch on sceKernelLoadModuleBufferUsbWlan that broke the
build (the header intentionally exposes it for sceVshBridge).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZk5y4Fzw811WJoNWZb8Sc
nm.nsegment is attacker-controlled but segmentaddr/segmentsize are fixed
4-entry arrays; the debug info loop read past them. Clamp to 4 like the
other consumers.
The space-left clamp was computed in unsigned 32-bit arithmetic, so a
crafted fileoffset/FirstOffsetExtra could underflow to a huge value and
leave addbytes unclamped, driving a Memory::Memcpy past dataBuf_.
Compute the clamp in signed 64-bit, clamp negatives to 0, and skip the
copy when there's nothing to write.
AnalyzeAtracTrack used max(fileSize, size) as the chunk-parse bound with
fileSize taken from the file's RIFF header, so a crafted inflated RIFF
size could push reads past the end of the buffer. Keep the real-library
behavior of tolerating a too-low size, but clamp the parse bound to the
actual mapped guest memory at the buffer.
Also guard ParseWaveAT3's RIFF scan against a blockSize < 4 underflow
that could make the offset negative and bypass the loop bounds check, and
clamp readSize to the mapped region in Atrac2::SetData before parsing.
EP_MAP_STRIDE * EPMapEntriesNum was computed in 32-bit and could wrap,
passing the range check while the loop read the unwrapped count; and the
check was skipped entirely when headerOffset == 0 (the player tempbuf
path).
- Use 64-bit math for the EP map size.
- Keep the guest-RAM range check when headerOffset != 0.
- Cap the entry count for the headerOffset == 0 path so the reads stay
within the player's 64KB tempbuf.
PGF::ReadPtr walked four length-prefixed tables and computed table sizes
before any bounds check, and used signed 32-bit size math that could
overflow, allowing a crafted font to read past the input buffer.
- Validate the total size of all tables up front using 64-bit math.
- Check the rev3 extra header fits before reading it.
- Cap charPointerLength/charMapLength/shadowMapLength to avoid absurd
allocations.
- Bounds-check glyph data offsets before reading each glyph.
Also throw in a warning fix
The import debug reporter used IsValidAddress (start-address only) before
formatting module names with %s, so a crafted unterminated name could be
read past guest RAM. Use IsValidNullTerminatedString instead.
- InitContextFromTrackInfo now rejects files where sampleSize (from
blockAlign) exceeds the buffer size, instead of only clamping later in
DecodeForSas. Keep the DecodeForSas check as defense-in-depth since a
large buffer could still allow a crafted packet to overflow the fixed
assembly buffer.
- CChunkFileReader::Verify now returns ERROR_BROKEN_STATE if bounds
checking fails, so modified savestates are rejected here too.
The loop-with-trailer streaming path copied 'secondBufferByte % sampleSize'
bytes into the main buffer with no clamp. sampleSize is file-derived, so a
crafted value could overflow the destination. Clamp the copy length to
info.bufferByte.
Added KernelModuleAddressDescription() (Core/HLE/sceKernelModule.cpp),
which looks up which currently loaded module (and text/data/bss/segment
section within it) an address falls in, e.g. "EBOOT.BIN.text+1234".
Wired it into:
- Core_MemoryException/Core_ExecException/Core_BreakException
(Core/Core.cpp), appended next to every address/pc/ra shown in their
log lines.
- FormatStackTrace (Core/MemFault.cpp), appended per-frame next to the
existing symbol description.
This makes crash/exception logs actionable even when there's no symbol
at the faulting address - you at least get which module and section
it's in, useful for reverse engineering unfamiliar code.
Verified live via headless: injected a MIPS break instruction at the
current PC (through Tools/wsdbg) and confirmed the log line changed from
"break instruction hit at 088040ac" to "break instruction hit at 088040ac
[sceDisplayWaitVblank Test.text+ac]".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XDNwPPuidmNxQGRJxBuRL6
FixedSizeQueue::DoState serializes the entire fixed backing store. For
the sceAudio channel queues that is 512KB per channel (32768*8 s16
samples), or ~4.6MB of mostly dead bytes in every savestate across the
nine channels - the live sample count at any moment is normally a few
KB. This addresses the existing TODO in DoState.
Add DoStateCompact(), which stores only the live [head, head+count)
region and restores it linearized at the front of storage. A wrapped
live region is written as its two pieces in pop order; since the POD
DoArray path writes raw bytes with no per-element or per-call header,
the single linear read on load consumes them identically. The count is
validated on load and a bad value fails the load cleanly via
p.SetError.
AudioChannel bumps its section to v3 to use the compact form; old
states still load through the unchanged full-storage path. This shrinks
every savestate by several MB uncompressed and cuts the copy/compress
cost of each save, including the rewind feature's periodic states.
On savestate load, two sites delete a pre-load host object that owns an
HLEHelperThread without calling Forget() first, so ~HLEHelperThread runs
__KernelDeleteThread and kernelMemory.Free() with thread ids and block
addresses from before the load, against the freshly restored kernel
state:
- sceUtility: Do(p, accessThread) deletes the stale accessThread inside
DoClass before recreating it from the stream.
- scePsmf: Do(p, psmfPlayerMap) deletes every existing PsmfPlayer, and
~PsmfPlayer -> AbortFinish() deletes its finishThread raw.
When the stale id or block happens to be absent from the restored state
this only logs errors ("... does not exist" / "BlockAllocator: invalid
free"). When it has been recycled, a live thread is terminated or a
live allocation is freed, silently corrupting the loaded state. Easiest
to hit by loading a state while a savedata operation or PSMF player is
active, into a session where those ids were reused.
__IoDoState and __PsmfShutdown already Forget() before deleting; do the
same at these two sites. Worst case behavior change is a leaked
kernel-side thread record where one was previously (incorrectly)
freed.
A call through a still-pending import gets written as a generic "invalid
syscall" opcode (WriteFuncMissingStub) that no longer carries the
original module name or NID by the time it's actually invoked - but the
address of the syscall instruction itself is exactly the stubAddr every
pending FuncSymbolImport already records. Added
KernelFindImportByStubAddr() to search loaded modules' importedFuncs for
a match, and use it in GetSyscallFuncPointer's unknown-syscall path.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Make GetPdpStat behave more like in P2P mode
- Now fetches accumulated ready to be received amount of data
instead of only the immediate to be received Pdp block
- Make GetPtpStat behave more like in P2P mode
- Now fetches accumulated ready to be received amount of data
instead of the size of the next server send block
- Make PtpRecv behave more like in P2P mode
- Fix block like behavior where whole server send block has
to be consumed before getting data from the next send block
- Fix block like bahavior where sender sends in small PtpSend
calls while receiver receive with less frequent but bigger
buffer PtpRecv causes bloat(and eventually server kick)
on relay