Bugs

The PS Vita has bugs. Some bugs can lead to Vulnerabilities. Others lead to nothing useful (yet) but can serve as examples of what not to do.

= Exploitable bugs =

See Vulnerabilities.

= Non-exploitable bugs =

Syscall table collision between modules
Because of the time between a slot is allocated and the time it is written to, there could be collisions. For example, assume there is one empty syscall slot left. Two modules each exporting syscalls are loaded and both of them are assigned the final free slot. One user library is loaded that imports from the first module. Then it imports from the second module. At this point, the function pointer exported by the first module is replaced with the second one.

It is unlikely this would lead to any security vulnerabilities, but it could create system instability. However, if the system has so many (let us assume more than 3000) syscalls loaded, it may be already in an unstable state.

Kernel heap pointer leak in sceKernelGetLibraryInfoByNID
Discovered on 2019-12-17 by Princess of Sleeping.

SceKernelModulemgr leaks a kernel heap pointer, but it is probably not useful for kernel exploitation.

SceKernelLibraryInfo.libname is a pointer to kernel memory. See SceKernelModulemgr.

PoC code:

Not fixed as of FW 3.600.011.

SceIofilemgr misses internal NULL pointer checks
SceIofilemgr's syscalls wrappers do various checks in usermode for the sanity of usermode arguments, but some internal functions that the syscalls call do not do proper checks.

For example, you can simply trigger a Kernel DABT by running the following code:

Confirmed in FW 2.10. FWs >=3.60 have proper checks.

sceAppMgrDestroyAppByAppId triggers kernel panic
Triggering a usermode exception immediately after calling sceAppMgrDestroyAppByAppId causes ?SceKernelThreadMgr? to get confused and trigger a kernel exception.

sceKernelCreateThread in thumb mode
SceKernelThreadMgr checks the memory attributes to see if the entry point is executable, but in thumb mode, the function pointer always has bit 0 as 1, so if the entry point is the last 4-bytes of a memory page, then the next check fails and returns 0x80020006.

sceNetRecvfromForDriver 0xC0022005 error on kernel call
This is because the internal function always sets the is_user flag in the parameter, so setting the kernel memory pointer to data in SceNetPs will result in an error in SceSysmem or SceSysmem.

Illegal alignment check of kernel allocator
Discovered on 2021-08-30 by Princess of Sleeping.

For example, if 0x880 is passed as the alignment argument of kernel malloc, the function will not return NULL.

This affects at least SceNetPs malloc and system malloc internal/external.

Ignored sceGUIDGetNameCore error propagation
Discovered on 2022-03-10 by Princess of Sleeping.

sceGUIDGetNameCore, which is called internally by SceSysmem or SceSysmem, always returns 0 even if an error occurs in the function.

Incomplete register restore on intr handler
Discovered on 2023-03-08 by Princess of Sleeping.

Confirmed on fw 1.810.

In the example below, an interrupt occurs when rw_data is loaded. In that case the interrupt handler will handle it, but not fully restore the DACR when leaving the function, but restore it from the ThreadCB.

And what is set in ThreadCB is the kernel's default client setting of 0x55550000. So when the interrupt ends and you try to write to rx_data, a DABT occurs.

Add disable intr to fix these.

DACR corrupte due to sceKernelIsAccessibleRangeProc
If sceKernelIsAccessibleRangeProc is specified in the pid argument, switches to the target process MMU Mapping, but does not restore DACR correctly at the time of termination processing.

Simplified code.

Be careful if you are in Development Kit. The callback of sceKernellPrintf calls sceKernelIsAccessibleRangeProc. (SceSysmem::sceKernellPrintf -> SceDeci4pSTtyp::handler -> call sceKernelIsAccessibleRangeProc for n/s format)

Also, if you crash something when you write on RX with after DACR 0xFFFFFFFF, suspect this. This is not the only function for MMU Mapping like this.

Limited buffer size in dbginfo handler for sceKernelPrintf*
The handler properly converts dbginfo like  and outputs it to tty, but its buffer size is limited, so if the function name or file name is too long, the conversion will be cut off and the incorrect output will be output to tty.

Out of range access in SKBL
Discovered on 2022-01-20 by Princess of Sleeping.

To decode ARZL encoded Tzs SceSysmem, SKBL maps Compati SRAM (PA 0x1C000000) to Tzs VA with a size of 2MiB. It then calls SKBL with an improper argument, thus using glitches during decoding to exceed 2MiB will pass the size check and access outside the range of the device, so it can trigger a Data abort exception.

Moreover, even if SKBL returns an error code, it will be passed to the argument of SKBL without being checked, so access for up to 0x80560201-bytes will occur.

It is currently just a bug as no glitching has been tried and as a Data abort exception is not useful.

Null dereference in the NSKBL kernel panic handler
(2021/06/19 by Princess of Sleeping) The kernel panic handler accesses the SceSysroot pointer, but since that pointer is set to NULL during early boot, NULL access to SceSysroot occurs.

CelesteBlue: If I understood correctly, this means that as long as NSKBL is running, a non-secure Kernel panic from any cause will end up in a DABT exception at NSKBL level.

CreepNT: The global SceSysroot pointer is initialized during  (soon after the MMU is brought up) and any panic after this point will not DABT. However, if a panic happens before but the MMU is disabled, since 0 is a valid (physical) address, no DABT will occur (but since bogus "Sysroot" data will be read, system may e.g. PABT if bogus data is interpreted as a function pointer). This only leaves a tiny window during which the MMU is enabled but  has not been executed where a panic will cause a DABT (but since there is basically no code in that window that can panic, a DABT should never happen because of this bug).

Present in FW 3.600.011, 3.650.011.

Out-of-bounds write in sceKernelSysrootStart
(2023/04/25 (wiki, discovered much earlier) by CreepNT) In old firmwares, during the execution of the  function, 0x80 bytes are allocated from the Sysroot heap then divided in 4 blocks of 0x20 bytes each. Each CPU then loads the address of its block into the  register.

Later on during this same function, the  subroutine is called - however, it expects   to hold a pointer to a   (Thread object) which is much larger than 0x20, and does the following:. This results in an out-of-bounds write at offsets 0x74, 0x94, 0xB4 and 0xD4 of the Sysroot heap for CPU0, CPU1, CPU2 and CPU3 respectively.

However, this bug has no real consequence (and was probably never noticed) because the data at these offsets is:
 * 0x74: inside the TPIDRPRW block, which is unused
 * 0x94: inside a 0x28 bytes allocation which has not been written to yet
 * 0xB4: inside heap padding (all allocations are rounded up to 32/64B boundary)
 * 0xD4: offset 0x14 inside the  structure, which is unused

Fixed in firmware 0.990 due to a rework of the TLS system ( is gone so this invalid call is no longer performed).