<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://switchbrew.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Yannik</id>
	<title>Nintendo Switch Brew - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://switchbrew.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Yannik"/>
	<link rel="alternate" type="text/html" href="https://switchbrew.org/wiki/Special:Contributions/Yannik"/>
	<updated>2026-05-01T16:17:51Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.1</generator>
	<entry>
		<id>https://switchbrew.org/w/index.php?title=Switch_System_Flaws&amp;diff=14306</id>
		<title>Switch System Flaws</title>
		<link rel="alternate" type="text/html" href="https://switchbrew.org/w/index.php?title=Switch_System_Flaws&amp;diff=14306"/>
		<updated>2026-02-19T14:06:30Z</updated>

		<summary type="html">&lt;p&gt;Yannik: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page is a list of publicly known Switch / Switch 2 (S2) flaws.&lt;br /&gt;
&lt;br /&gt;
= Hardware =&lt;br /&gt;
Flaws in this category pertain to the underlying hardware that powers the Switch.&lt;br /&gt;
&lt;br /&gt;
This includes components shared across Tegra based devices such as the [[TSEC]], the [[Security_Engine|Security Engine]], the [[GPU]] and so on.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Fixed with hardware model/revision&lt;br /&gt;
!  Newest hardware model/revision this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| GMMU DMA attack&lt;br /&gt;
| The Switch&#039;s GPU includes a separate MMU (GMMU) that is allowed to bypass the system&#039;s IOMMU (SMMU). By accessing the GPU&#039;s MMIO region and manipulating the page table entries in the GMMU, an attacker can read/write any portion of the DRAM (except memory carveouts).&lt;br /&gt;
&lt;br /&gt;
[5.0.0+] Works around this hardware flaw by using memory pool partitioning. You can no longer escalate into sysmodules with GPU DMA because all their memory is allocated using heap that&#039;s carved out.&lt;br /&gt;
&lt;br /&gt;
HAC-001-01 (Mariko/Tegra214/Tegra210b01): Fixes this by adding a new register which restricts what memory untranslated DMA requests may access. Untranslated GPU DMA may now only access the GPU carveout (physmem 0x80002000-0x80006000), which the GPU already has legitimate and exclusive access to.&lt;br /&gt;
| HAC-001-01 (Mariko/Tegra214/Tegra210b01)&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| Summer 2017&lt;br /&gt;
| December 28, 2017&lt;br /&gt;
| [[User:hexkyz|hexkyz]], [[User:SciresM|SciresM]] and [[User:qlutoo|qlutoo]]&lt;br /&gt;
|-&lt;br /&gt;
| Weak Security Engine context validation&lt;br /&gt;
| The Tegra X1 supports a &amp;quot;deep sleep&amp;quot; feature, where everything but DRAM and the PMC registers lose their content (and the SoC loses power). Upon awaking, the bootrom re-executes, restoring system state. Among these stored states is the Security Engine&#039;s saved state, which uses AES-128-CBC with a random key and all-zeroes IV. However, the bootrom doesn&#039;t perform a MAC on this data, and only validates the last block. This allows one to control most of security engine&#039;s state upon wakeup, if one has a way to modify the encrypted state buffer.&lt;br /&gt;
&lt;br /&gt;
With a way to modify the encrypted state buffer, one can thus dump keys from &amp;quot;write-only&amp;quot; keyslots, etc.&lt;br /&gt;
&lt;br /&gt;
This also bypasses the SBK protection of the bootROM: indeed, at warmboot, bootROM will always clear keyslot 0xE to prevent malicious code from saving the SBK. Moving the SBK to another keyslot in the saved context renders this protection moot.&lt;br /&gt;
&lt;br /&gt;
HAC-001-01 (Mariko/Tegra214/Tegra210b01): Fixes this by streamlining the context save process; security engine contexts are now saved to protected memory which the CPU cannot access or modify.&lt;br /&gt;
| HAC-001-01 (Mariko/Tegra214/Tegra210b01)&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| December 2017&lt;br /&gt;
| January 20, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]] and [[User:motezazer|motezazer]]&lt;br /&gt;
|-&lt;br /&gt;
| Security Engine keyslots vulnerable to partial overwrite attack&lt;br /&gt;
| &lt;br /&gt;
The Tegra X1 security engine supports writing keyslot data to the engine with syntax as follows: &lt;br /&gt;
&lt;br /&gt;
SECURITY_ENGINE-&amp;gt;AES_KEYTABLE_ADDR = (keyslot &amp;lt;&amp;lt; 4) | (dword_index_in_keyslot); &lt;br /&gt;
&lt;br /&gt;
SECURITY_ENGINE-&amp;gt;AES_KEYTABLE_DATA = readle32(key, dword_index_in_keyslot * 4); &lt;br /&gt;
&lt;br /&gt;
However, the Security Engine flushes writes to the internal key tables immediately when AES_KEYTABLE_DATA is written -- this allows one to overwrite a single dword of a key at a time, and thus brute force the contents of keyslots in time (2^32 * 8) = 2^35 instead of 2^256.&lt;br /&gt;
| None&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| Theorized Summer 2017 due to suggestive syntax, confirmed April 9, 2018&lt;br /&gt;
| April 9, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]], almost surely others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| CVE-2018-6242 (leveraged by the ShofEL2 and Fusée Gelée exploits)&lt;br /&gt;
| The USB software stack provided inside the boot instruction rom (IROM/bootROM) contains a copy operation whose length can be controlled by an attacker. By carefully constructing a USB control request, an attacker can leverage this vulnerability to copy the contents of an attacker-controlled buffer over the active execution stack, gaining control of the Boot and Power Management processor (BPMP) before any lock-outs or privilege reductions occur. This execution can then be used to exfiltrate secrets and to load arbitrary code onto the main CPU Complex (CCPLEX) &amp;quot;application processors&amp;quot; at the highest possible level of privilege (typically as the TrustZone Secure Monitor at PL3/EL3).&lt;br /&gt;
| HAC-001-01 (Mariko/Tegra214/Tegra210b01) (also fixed independently on Tegra186).&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| January 2018&lt;br /&gt;
| April 23, 2018&lt;br /&gt;
| [[User:Shuffle2|shuffle2]] and fail0verflow (originally),&amp;lt;br&amp;gt; [[User:Ktemkin|ktemkin]] and ReSwitched Team (independently),&amp;lt;br&amp;gt; [[User:Naehrwert|naehrwert]] (independently),&amp;lt;br&amp;gt; [[User:Hexkyz|hexkyz]] (independently),&amp;lt;br&amp;gt; st4rk with [[User:Shinyquagsire23|Shiny Quagsire]] and Dazzozo (independently),&amp;lt;br&amp;gt; and many others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| Poor validation of bootrom SDRAM configuration parameters leads to arbitrary writes in bootrom&lt;br /&gt;
| &lt;br /&gt;
The Tegra X1 bootrom supports saving SDRAM parameters to scratch registers, and using the saved configuration to enable DRAM during warmboot.&lt;br /&gt;
&lt;br /&gt;
The code that parses these parameters does if (params-&amp;gt;EmcBctSpareN) *params-&amp;gt;EmcBctSpareN = params-&amp;gt;EmcBctSpareNPlusOne for most N, without validating either the address or value written to it.&lt;br /&gt;
There are other arbitrary writes in this code, as well (e.g. BootromPatch parameters intended for patching MISC registers do not check a relative offset to 0x7000000, etc).&lt;br /&gt;
&lt;br /&gt;
This allows a user with access to the PMC registers (via pre-sleep bpmp execution, or otherwise) to gain arbitrary bootrom code execution.&lt;br /&gt;
&lt;br /&gt;
HAC-001-01 (Mariko/Tegra214/Tegra210b01): Fixes this by validating that the spare writes/bootrom patch before performing them.&lt;br /&gt;
| HAC-001-01 (Mariko/Tegra214/Tegra210b01)&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| 2017&lt;br /&gt;
| December 16, 2018&lt;br /&gt;
| Everyone (independently).&lt;br /&gt;
|-&lt;br /&gt;
| TSEC ROM does not clear crypto registers after signature verification&lt;br /&gt;
|&lt;br /&gt;
TSEC supports executing signed-microcode at a greater privilege level than normal payloads.&lt;br /&gt;
&lt;br /&gt;
When jumping to signed microcode, the caller is expected to load hardware crypto register $c6 = &amp;lt;signature&amp;gt;, $c7 = &amp;lt;seed (zero for all officially-signed microcode)&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
TSEC ROM then calculates the expected signature and compares it to the user-supplied one in $c6. On match, the secure payload is executed, and on failure an exception is raised.&lt;br /&gt;
&lt;br /&gt;
However, TSEC ROM fails to clear the crypto registers used to calculate the expected signature in either of the success/failure cases.&lt;br /&gt;
&lt;br /&gt;
Thus, with some way of obtaining the contents of crypto registers (e.g. ROP under some secure payload), an attacker can dump intermediary values from signature calculation.&lt;br /&gt;
&lt;br /&gt;
With enough data/trial/error, this is enough to reconstruct the signature algorithm:&lt;br /&gt;
* mac = &amp;lt;davies meyer hash of (page || address of page) for each 0x100 page in the payload&amp;gt;&lt;br /&gt;
* key = AES-ENCRYPT(hardware csecret 0x1, seed)&lt;br /&gt;
* signature = AES-ENCRYPT(key, mac)&lt;br /&gt;
&lt;br /&gt;
| None&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| Late 2018/Early 2019&lt;br /&gt;
| August 2020&lt;br /&gt;
| [[User:qlutoo|qlutoo]]/[[User:Hexkyz|hexkyz]]/[[User:Shuffle2|shuffle2]], [[User:SciresM|SciresM]]/[[User:motezazer|motezazer]] (independently).&lt;br /&gt;
|-&lt;br /&gt;
| TSEC signature validation design flaw leads to fake-signing&lt;br /&gt;
|&lt;br /&gt;
As mentioned above, when jumping to signed microcode the caller is expected to load hardware crypto register $c6 = &amp;lt;signature&amp;gt;, $c7 = &amp;lt;seed (zero for all officially-signed microcode)&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
However, TSEC ROM performs no validation on the input seed used to generate the signing key.&lt;br /&gt;
&lt;br /&gt;
This leads to the following attack:&lt;br /&gt;
* Attacker gains rop under any secure microcode payload with signature = S.&lt;br /&gt;
* Attacker uses the &amp;quot;csigenc&amp;quot; instruction to obtain K = AES-ENCRYPT(hardware csecret 0x1, S).&lt;br /&gt;
* Attacker jumps to their own microcode with $c6 = &amp;lt;signature calculated on pc using K&amp;gt;, $c7 = S&lt;br /&gt;
* TSEC ROM calculates key = AES-ENCRYPT(hardware csecret 0x1, S) = K, and the signature check passes.&lt;br /&gt;
* Attackers microcode is executed in secure mode as though it were signed by NVidia.&lt;br /&gt;
&lt;br /&gt;
Thus an attacker who has exploited *any* secure payload may use this to obtain a &amp;quot;fake signature key&amp;quot;, which can be used to sign and execute arbitrary microcode in secure mode.&lt;br /&gt;
&lt;br /&gt;
Note: this does not break the TSEC cryptosystem, as the csigenc mechanism relies on the signature of the executing microcode, and fakesigning produces different signatures from NVidia that cannot be controlled.&lt;br /&gt;
| None&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| Late 2018/Early 2019&lt;br /&gt;
| August 2020&lt;br /&gt;
| [[User:qlutoo|qlutoo]]/[[User:Hexkyz|hexkyz]]/[[User:Shuffle2|shuffle2]], [[User:SciresM|SciresM]]/[[User:motezazer|motezazer]] (independently).&lt;br /&gt;
|-&lt;br /&gt;
| ROP under TSEC secure bootrom via DMA engine stack overwrite (--xploit)&lt;br /&gt;
| TSEC DMA engine does not stop when entering TSEC secure bootrom. By pointing TSEC DMA to current stack before secure bootrom entry, stack can be controlled. &lt;br /&gt;
&lt;br /&gt;
One can then use blind ROP against the TSEC secure bootrom (which is execute only, and cannot be dumped).&lt;br /&gt;
&lt;br /&gt;
With sufficient effort, an attacker can construct a ROP chain that leads to csigcmp being executed with fully controlled arguments.&lt;br /&gt;
&lt;br /&gt;
This allows for arbitrary heavy secure mode code execution with the current signature set to an arbitrary value.&lt;br /&gt;
&lt;br /&gt;
This completely breaks the TSEC cryptosystem, by allowing one to obtain the result of csigenc with signature = &amp;lt;any desired value&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This has many uses/results, notably including dumping the &amp;quot;true&amp;quot; signature key (set signature = zeroes, perform csigenc using csecret 0x1).&lt;br /&gt;
| None&lt;br /&gt;
| TSEC for all Tegra devices&lt;br /&gt;
| Late 2018&lt;br /&gt;
| January 2021&lt;br /&gt;
| [[User:Hexkyz|hexkyz]]/[[User:SciresM|SciresM]], [[User:Vale|Vale]]/[[User:Thog|Thog]] (independently), [[User:Tatsuko|Tatsuko]] (independently), possibly others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| Boot straps are not relatched on watchdog resets (strapwn)&lt;br /&gt;
| On boot, the BOOTSELECT, RCM and RAM_CODE straps are latched from external GPIO to determine which boot medium to use and verify from in bootrom. However, APB_MISC_PP_STRAPPING_OPT_A can be overwritten with arbitrary values following bootrom. Write access to PP_STRAPPING_OPT_A would otherwise be mundane, however these straps are not relatched during a watchdog reset (despite being latched during other software resets), allowing for arbitrary straps to be selected and executed in bootrom.&lt;br /&gt;
&lt;br /&gt;
This allows setting NVPROD_UART on some hardware configurations where it would normally be unavailable (ie on Jetson Nano boards), but is otherwise mostly useless and/or useful for testing unintended boot options (such as USB Mass Storage boot) without having to move boot strap resistors.&lt;br /&gt;
| Unknown&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| May 2020&lt;br /&gt;
| April 30, 2021&lt;br /&gt;
| [[User:Shinyquagsire23|Shiny Quagsire]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Firmware =&lt;br /&gt;
Flaws in this category pertain to the firmware running on hardware devices, such as wifi/bluetooth, etc. Firmware is generally uploaded by sysmodules.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Broadpwn (CVE-2017-9417)&lt;br /&gt;
| See [https://blog.exodusintel.com/2017/07/26/broadpwn/ here] and [https://www.blackhat.com/docs/us-17/thursday/us-17-Artenstein-Broadpwn-Remotely-Compromising-Android-And-iOS-Via-A-Bug-In-Broadcoms-Wifi-Chipsets.pdf here].&lt;br /&gt;
| Code execution on the wifi controller (untested on Switch).&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| Switch: July 2022&lt;br /&gt;
| Switch: July 30, 2022&lt;br /&gt;
| Switch: [[User:Yellows8|yellows8]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Software =&lt;br /&gt;
== Bootloader ==&lt;br /&gt;
Flaws in this category pertain to any bootloader component such as the [[Package1#Package1ldr|package1ldr]], the [[Package1#Section_1|NX bootloader]] or the [[Package1#Section_0|warmboot binary]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Null-dereference in panic()&lt;br /&gt;
| The Switch&#039;s stage 1 bootloader, on panic(), clears the stack and then attempts to clear the Security Engine. However, it does so by dereferencing a pointer to the SE in .bss (initially NULL), and this pointer doesn&#039;t get initialized until partway into the bootloader&#039;s main() after several functions that might panic() are called. Thus, a panic() caused prior to SE initialization would result in the SE pointer still being NULL when dereferenced. &lt;br /&gt;
The BPMP doesn&#039;t have an active MPU and the bus won&#039;t data abort on an invalid address, so no exception will be entered: it&#039;ll end up overwriting some exception vectors with NULL before halting.&lt;br /&gt;
&lt;br /&gt;
In 3.0.0, this was fixed by moving the security engine initialization earlier in main(), before the first function that could potentially panic().&lt;br /&gt;
| Some exception vectors overwritten with NULL, before SBK/other keyslots are cleared. Probably useless for anything more interesting.&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| Early July, 2017&lt;br /&gt;
| July 30, 2017&lt;br /&gt;
| Everyone who diff&#039;d 2.3.0 and 3.0.0 Package1&lt;br /&gt;
|-&lt;br /&gt;
| FUSE_DIS_PGM not written by package1 &lt;br /&gt;
| The switch&#039;s hardware fuse driver contains a write-once bit in a register called &amp;quot;FUSE_DIS_PGM&amp;quot;, which disables burning fuses until the next reboot. While Nintendo&#039;s bootloader code for waking up from sleep writes this on all firmware, the actual package1 initial bootloader forgets to write to it on cold reboot. &lt;br /&gt;
&lt;br /&gt;
This isn&#039;t too big of a problem because another fuse is burnt on retail devices (production mode), which prevents burning *all* fuses other than ODM_RESERVED ones in hardware.&lt;br /&gt;
&lt;br /&gt;
This was fixed in 3.0.0 by writing to the register on cold boot (although the write happens in TZ instead of package1 where it should take place, possibly to obfuscate the fact that they made this mistake).&lt;br /&gt;
| Burning arbitrary ODM reserved fuses with TZ code execution, which should never be possible for non-bootloader code.&lt;br /&gt;
&lt;br /&gt;
Warning: one could irreparably brick one&#039;s console by playing with this.&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| Late summer/early fall 2017&lt;br /&gt;
| December 31, 2017&lt;br /&gt;
| [[User:SciresM|SciresM]], [[User:motezazer|motezazer]]&lt;br /&gt;
|-&lt;br /&gt;
| maconstack (TSEC firmware leaves MAC on the stack)&lt;br /&gt;
| Package1ldr loads a firmware blob into TSEC early on boot. This piece of code runs on the TSEC in Authenticated Mode and has the sole purpose of generating the per-console TSEC key (see [[Cryptosystem]]).&lt;br /&gt;
&lt;br /&gt;
As a way to mitigate attacks, the TSEC firmware blob is split into 3 stages: [[TSEC_Firmware#Boot|Boot]] which is unencrypted and unsigned, [[TSEC_Firmware#KeygenLdr|KeygenLdr]] which is unencrypted but signed and [[TSEC_Firmware#Keygen|Keygen]] which is encrypted and signed.&lt;br /&gt;
Boot loads a static pre-generated signature into the Falcon&#039;s CPU crypto registers, loads KeygenLdr into the Falcon&#039;s CODE region and jumps to it. Execution will proceed into KeygenLdr in Heavy Secure Mode if, and only if, the loaded signature matches the one Falcon calculates internally for KeygenLdr.&lt;br /&gt;
&lt;br /&gt;
Among various things, KeygenLdr will attempt to do a &amp;quot;backwards&amp;quot; security check by calculating a CMAC over Boot and comparing it with a known hash stored in the TSEC firmware&#039;s key data (a small buffer stored after Boot&#039;s code). If the hashes don&#039;t match, execution aborts.&lt;br /&gt;
&lt;br /&gt;
KeygenLdr stores the calculated Boot&#039;s CMAC in the stack, but forgets to clear it. Since the stack is located in Falcon&#039;s DATA region, loading the TSEC firmware blob and dumping the DATA region afterwards (via MMIO) will reveal the calculated hash.&lt;br /&gt;
This allows using KeygenLdr as an oracle to generate a valid CMAC for arbitrary Boot code. Replacing the CMAC in the TSEC firmware&#039;s key data region results in KeygenLdr accepting any Boot code, thus rendering this security measure useless.&lt;br /&gt;
&lt;br /&gt;
Additionally, since signed Falcon code can&#039;t be revoked without an hardware revision, an attacker can always reuse the flawed KeygenLdr code even if a fix is issued.&lt;br /&gt;
| Running TSEC firmware&#039;s KeygenLdr in a user controlled environment.&lt;br /&gt;
| None&lt;br /&gt;
| [[5.0.2]]&lt;br /&gt;
| January 2018&lt;br /&gt;
| April 29, 2018&lt;br /&gt;
| [[User:Hexkyz|hexkyz]], [[User:Rei|Reisyukaku]] (independently), probably others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| pk1ldrhax&lt;br /&gt;
| Package1ldr decrypts and verifies the keyblob inside of the current BCT in order to get the package1 key, and then uses the package1 key to decrypt package1. It then validates package1 before jumping to it by checking the PK11 magic number, and that the section sizes sum to the expected size (and are individually less than the expected size). &lt;br /&gt;
&lt;br /&gt;
However, package1ldr does not actually validate the package1 key against a fixed vector (much like kernel9loader forgot to do so on the 3ds). This would normally not matter, as keyblobs are validated -- however, with bootrom code execution one can dump SBK and forge keyblobs, and thus control the package1 key. &lt;br /&gt;
&lt;br /&gt;
Thus (&#039;&#039;&#039;in theory, but not in practice due to the size of the brute force required&#039;&#039;&#039;) one can replace the package1 key with garbage, causing package1 to decrypt into garbage, and hope that this garbage passes validation checks and that package1ldr jumping into the garbage will do something useful.&lt;br /&gt;
&lt;br /&gt;
This was fixed incidentally in [[6.2.0]], as pk1ldr does not use keyblob data to decrypt package1 any more.&lt;br /&gt;
&lt;br /&gt;
| With a large enough brute force: arbitrary package1 code execution from coldboot.&lt;br /&gt;
&lt;br /&gt;
However, a usable brute force is on the order of &amp;gt;= ~2^80, so &#039;&#039;&#039;this is almost certainly not actually usable in any meaningful context&#039;&#039;&#039;.&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| Early 2017 (as soon as plaintext package1ldr was first dumped)&lt;br /&gt;
| November 20, 2018&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Stack smash in TSEC firmware&#039;s KeygenLdr&lt;br /&gt;
| Given that we can control the [[TSEC_Firmware#Key_data|key data]] (which is not authenticated) and the [[TSEC_Firmware#Boot|Boot]] blob (see &amp;quot;maconstack&amp;quot;), as well as the fact Non-secure and Heavy Secure code share the same stack, we can use this to attack KeygenLdr. KeygenLdr uses memcpy to copy over a payload to DMEM to verify it, which can be abused to smash the stack (in DMEM) and write over the return address of said function.&lt;br /&gt;
| ROP under KeygenLdr in Heavy Secure mode.&lt;br /&gt;
| None&lt;br /&gt;
| [[8.0.1]]&lt;br /&gt;
| Early 2018&lt;br /&gt;
| May 21, 2019&lt;br /&gt;
| Everyone (independently).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== TrustZone ==&lt;br /&gt;
Flaws in this category pertain exclusively to the [[Package1#Section_2|Secure Monitor]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Non-atomic mutexes&lt;br /&gt;
| When an [[SMC]] is called, TrustZone sets a global variable to mark that an SMC is in progress, so that two SMCs using shared resources (like the security engine) do not trample on one another. On 1.0.0, this global variable was written using non-atomic writes, and thus a race condition is possible.&lt;br /&gt;
&lt;br /&gt;
However, the SMC handler enforces that all SMCs must be called from core #3, unless the top-level handler ID is 1 (SMCs internal to the kernel). Thus, the only SMCs that can be run side-by-side are [any userland smc] and smcGetRandomBytesForKernel, and this turns out to not really be abusable.&lt;br /&gt;
| Mostly useless. Maybe some oob-write into unused (and thus useless) memory if running smcGetRandomBytesForKernel and smcGetRandomBytesForUser at the same time.&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| December 2017 (Probably earlier by others)&lt;br /&gt;
| January 18, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]], probably others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| jamais vu (non-secure world access to PMC MMIO and pre-deep sleep firmware)&lt;br /&gt;
| On [[1.0.0]], one could map in the PMC registers in userland. In addition, [[AM_services|am]] ran a little-kernel based firmware on the BPMP at runtime. With code execution under am, one could modify the BPMP&#039;s little-kernel firmware to hook deep sleep entry, and modify TrustZone/Security engine state. &lt;br /&gt;
&lt;br /&gt;
This was fixed in [[2.0.0]] by making the PMC secure-world only, blacklisting the BPMP&#039;s exception vectors from being mapped, and thoroughly checking for malicious behavior on deep sleep entry.&lt;br /&gt;
| Arbitrary TrustZone code execution.&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| December, 2017&lt;br /&gt;
| January 20, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]] and [[User:motezazer|motezazer]]&lt;br /&gt;
|-&lt;br /&gt;
| Missed BPMP Exception Vector Writes&lt;br /&gt;
| Starting in [[2.0.0]], the BPMP is asleep at runtime, and is turned on by TrustZone during [[SMC|smcCpuSuspend]] in order to initiate the deep sleep process. When it does so, it is held in RESET, and TrustZone attempts to write to the BPMP exception vectors at 0x6000F200 to register EVP_RESET = lp0_entry_fw_crt0, and all other EVPs to a function that simply reboots. However, while they successfully write EVP_RESET, they miss all the other vectors, accidentally writing to the 0x6000F004-0x6000F020 region instead of the 0x6000F204-0x6000F220 region they want to write to. This results in all the exception vectors for the BPMP other than RESET being &amp;quot;undefined&amp;quot; (attacker controlled).&lt;br /&gt;
&lt;br /&gt;
With some way of causing an exception vector to be taken at the right time, this would give pre-sleep code execution (and thus arbitrary TrustZone code execution, via the security engine flaw). However, none of the abort vectors are really triggerable, and interrupts are disabled for the BPMP when it is taken out of reset. Thus, this is useless in practice.&lt;br /&gt;
&lt;br /&gt;
This was fixed in [[4.0.0]] by writing to the correct registers.&lt;br /&gt;
| Theoretically: Arbitrary TrustZone code execution. In practice: Useless.&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| January, 2018&lt;br /&gt;
| February 23, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]] and [[User:motezazer|motezazer]], [[User:Naehrwert|naehrwert]], [[User:Hexkyz|hexkyz]], probably others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| TSEC has access to the secure kernel carveout &lt;br /&gt;
| TrustZone is responsible for managing security carveouts to prevent DMA controllers from accessing the carveout which contains the kernel, sysmodules, and other critical operating system data.&lt;br /&gt;
&lt;br /&gt;
Until [[8.0.0]], the list of devices that could access the carveout included the TSEC. However, the TSEC can bypass the SMMU when in authenticated mode by writing to a certain register. Thus, pwning nvservices would allow one to take over the TSEC, and use it to write to normally protected mmio/memory.&lt;br /&gt;
&lt;br /&gt;
In [[8.0.0]], this was fixed by removing TSEC access, and adding TSECB access (TSECB cannot bypass the SMMU).&lt;br /&gt;
| With access to the TSEC mmio (nvservices ROP) and code execution in TSEC Heavy Secure mode, kernel code execution, probably.&lt;br /&gt;
| [[8.0.0]]&lt;br /&gt;
| [[8.0.0]]&lt;br /&gt;
| 2017 (when TrustZone code plaintext was first obtained).&lt;br /&gt;
| April 15, 2019&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| deja vu (insufficient system state validation on suspend leads to pre-sleep BPMP code execution)&lt;br /&gt;
| Jamais Vu was fixed in [[2.0.0]] by making the PMC secure-world only, blacklisting the BPMP&#039;s exception vectors from being mapped, and thoroughly checking for malicious behavior on deep sleep entry, since gaining pre-sleep code execution on the BPMP compromises the system.&lt;br /&gt;
&lt;br /&gt;
However, the state validation performed by Nintendo&#039;s Secure Monitor was insufficient to prevent pre-sleep execution from being obtained.&lt;br /&gt;
&lt;br /&gt;
Prior to [[6.0.0]], one could use a DMA controller that had access to IRAM and was not held in reset (there were multiple) to race TrustZone&#039;s writes to the BPMP firmware in IRAM, and thus overwrite Nintendo&#039;s firmware with an attacker&#039;s to gain pre-sleep code execution.&lt;br /&gt;
&lt;br /&gt;
[[6.0.0]] addressed this by performing TrustZone state MAC writes and locking PMC scratch *before* turning on the BPMP, fixing the original Jamais Vu exploit entirely. In addition, the BPMP firmware in TrustZone&#039;s .rodata is now memcmp&#039;d to the actual data after it is written to IRAM. This mitigates race attacks that modify the firmware.&lt;br /&gt;
&lt;br /&gt;
However, Nintendo both forgot to validate the BPMP exception vectors after writing them, and forgot to hold in reset a DMA controller that can write to the BPMP&#039;s exception vectors.&lt;br /&gt;
&lt;br /&gt;
AHB-DMA is not blacklisted by kernel mapping whitelist (Nintendo probably forgot it, because the TX1 TRM does not really document that it&#039;s present, although the MMIO works as documented in older (Tegra 3 and before) TRMs).&lt;br /&gt;
&lt;br /&gt;
Thus, with kernel code execution (or some other way of accessing AHB-DMA, e.g. nspwn on &amp;lt;= 4.1.0, TSEC hax, or other arbitrary mmio access flaws), one can DMA to the BPMP&#039;s exception vectors as they are written, causing TrustZone to start the BPMP executing an attacker&#039;s firmware at a different location than TrustZone intends/validates.&lt;br /&gt;
&lt;br /&gt;
This was fixed in [[8.0.0]] by blocking AHB-DMA arbitration and verifying it is held in reset during suspend, and thus there are no more devices that can write to the relevant MMIO at the right time.&lt;br /&gt;
&lt;br /&gt;
| Arbitrary TrustZone/BootROM code execution, by using either the original Jamais Vu flaw (prior to [[6.0.0]] or a warmboot bootrom exploit (any firmware where pre-sleep execution can be gained).&lt;br /&gt;
| [[8.0.0]]&lt;br /&gt;
| [[8.0.0]]&lt;br /&gt;
| December 2017&lt;br /&gt;
| April 15, 2019&lt;br /&gt;
| [[User:SciresM|SciresM]], [[User:motezazer|motezazer]] and ktemkin,  [[User:Naehrwert|naehrwert]] (independently), almost certainly others (independently)&lt;br /&gt;
|-&lt;br /&gt;
| TrustZone allows using imported RSA exponents with arbitrary modulus&lt;br /&gt;
| TrustZone supports &amp;quot;importing&amp;quot; RSA private exponents for use by userland -- these are stored encrypted with TrustZone only keydata in NAND, and decrypted only to TZRAM. This prevents a console that has compromised userland from learning the private exponents of these keys and doing calculations with them offline. In practice, this is used for FS (gamecard communications), ES (drm), and SSL (console client cert communications).&lt;br /&gt;
&lt;br /&gt;
However, the actual SMC API only imports the RSA exponent, and not the modulus, which is passed separately by userland in each call. There is no validation done on the modulus passed in -- this means that userland can pass in any message and modulus it chooses, and obtain the result of (message ^ private exponent) % modulus back from the secure monitor.&lt;br /&gt;
&lt;br /&gt;
By choosing a prime number modulus P such that P has &amp;quot;smooth&amp;quot; order (totient(P) == P-1 is divisible only by &amp;quot;small&amp;quot; primes), one can efficiently use the [[wikipedia:Pohlig-Hellman algorithm|Pohlig-Hellman algorithm]] to calculate the discrete logarithm of such a result directly, and thus obtain the private exponent.&lt;br /&gt;
&lt;br /&gt;
This is mostly useless in practice, given the general availability of other exploits to obtain these decrypted exponents.&lt;br /&gt;
&lt;br /&gt;
This was fixed in 10.0.0 by importing the modulus in addition to the exponent for the ES device key and ES client cert key. For backwards compatibility reasons the SSL key and Lotus key still only import the exponent.&lt;br /&gt;
&lt;br /&gt;
StorageExpMod also now validates that the exponentiation of &amp;quot;DDDDD...&amp;quot; about the provided modulus by the imported exponent and then the fixed public exponent returns &amp;quot;DDDDD...&amp;quot;, and returns invalid argument if validation fails.&lt;br /&gt;
| With userland privileges sufficient to use an imported RSA key: obtaining that RSA key&#039;s private exponent.&lt;br /&gt;
| [[10.0.0]]&lt;br /&gt;
| [[10.0.0]]&lt;br /&gt;
| August 14, 2019&lt;br /&gt;
| August 14, 2019&lt;br /&gt;
| [[User:SciresM|SciresM]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Kernel ==&lt;br /&gt;
Flaws in this category pertain exclusively to the [[Package2#Section_0|HorizonOS Kernel]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Syscall Infoleaks&lt;br /&gt;
| Many syscalls leaked kernel pointers on sad paths (for example svcSetHeapSize and svcQueryMemory), until they landed a bunch of fixes in 2.0.0.&lt;br /&gt;
| Nothing really.&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| &lt;br /&gt;
| 2017&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| svcWaitSynchronization/svcReplyAndReceive bad cleanup on error&lt;br /&gt;
| If there is a page fault when fetching handles from the userspace array, it cleans up by dereferencing all objects despite having only loaded first N. Allows the attacker to make arbitrary decrefs on any kernel synchronization object, and thus can be used to get UAF. Haven&#039;t actually been tried on real HW though, but should work (tm).&lt;br /&gt;
| Kernel code execution&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| &lt;br /&gt;
| April 24, 2017&lt;br /&gt;
| [[User:qlutoo|qlutoo]]&lt;br /&gt;
|-&lt;br /&gt;
| Bad irq_id check in CreateInterruptEvent&lt;br /&gt;
| CreateInterruptEvent syscall is designed to work only for irq_id &amp;gt;= 32. All irq_ids &amp;lt; 32 are &amp;quot;per-core&amp;quot; and reserved for kernel use (watchdog/scheduling/core communications).&lt;br /&gt;
On 1.0.0 you could supply irq_id &amp;lt; 32 and it would write outside the SharedIrqs table.&lt;br /&gt;
| You can register irq&#039;s in the Core3Irqs table, and thus register per-core irqs for core3, that are normally reserved for kernel. Useless.&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| October 2017&lt;br /&gt;
| October 17, 2017&lt;br /&gt;
| [[User:qlutoo|qlutoo]]&lt;br /&gt;
|-&lt;br /&gt;
| Kernel .text mapped executable in usermode&lt;br /&gt;
| Prior to [[3.0.2]] the kernel .text was [[Memory_layout|mapped]] in usermode as executable. This can be used for usermode ROP for bypassing ASLR, but SVCs/IPC are not usable by running kernel .text in usermode.&lt;br /&gt;
| Executing kernel .text in usermode&lt;br /&gt;
| [[3.0.2]]&lt;br /&gt;
| [[3.0.2]]&lt;br /&gt;
| &lt;br /&gt;
| December 28, 2017 (34c3)&lt;br /&gt;
| [[User:qlutoo|qlutoo]]&lt;br /&gt;
|-&lt;br /&gt;
| Memory Controller not properly secured&lt;br /&gt;
| The Switch OS originally had the memory controller not set to be accessible only by the secure-world, which was problematic because insecure access can compromise the kernel.&lt;br /&gt;
&lt;br /&gt;
This was fixed partially in [[2.0.0]] by blacklisting the memory controller from being mapped by user-processes, and was fixed entirely in [[4.0.0]] by making the memory controller TZ-only and making all kernel accesses go through [[SMC|smcReadWriteRegister]].&lt;br /&gt;
| With some way to access the memory controller MMIO, arbitrary kernel code execution.&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| January 2018&lt;br /&gt;
| January 2018&lt;br /&gt;
| [[User:SciresM|SciresM]], [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Potential [[SVC|svcWaitForAddress]] thread use-after-free&lt;br /&gt;
| Between [[4.0.0]], where svcWaitForAddress was introduced, and [[7.0.0]], there was a second intrusive rbtree node in KThread for the WaitForAddress tree (the key being (address, priority), sorted lexicographically). Unlike the WaitProcessWideKeyAtomic tree, the kernel forgot to reinsert the WaitForAddress node when the thread&#039;s priority changed (priority inheritance and/or SetPriority), breaking the rbtree invariants; and since the kernel walks through the entire tree to remove intrusive nodes, you could cause threads to stay in the tree even after their deletion.&lt;br /&gt;
&lt;br /&gt;
[[7.0.0]] fixed the issue by using the same intrusive node for both trees. The thread/node knows which tree it is in, and the latter is correctly updated when thread priority changes.&lt;br /&gt;
| It unluckily didn&#039;t look exploitable&lt;br /&gt;
| [[7.0.0]]&lt;br /&gt;
| [[7.0.0]]&lt;br /&gt;
| July 2018&lt;br /&gt;
| February 2019&lt;br /&gt;
| [[User:TuxSH|TuxSH]]&lt;br /&gt;
|-&lt;br /&gt;
| Kernel RWX identity mapping never unmapped&lt;br /&gt;
| During init, the kernel binary is identity-mapped as RWX at 0x80060000; this is necessary to facilitate the transitionary period while the MMU is being enabled but mappings for e.g. KASLR are not yet determined, and also to enable smooth MMU enable transition during wake-from-sleep.&lt;br /&gt;
&lt;br /&gt;
However, the identity mapping was never unmapped, and thus the whole kernel code bin remained permanently mapped as RWX for all kernel threads (any thread which does not have an owner process and thus uses the KSupervisorPageTable TTBR0).&lt;br /&gt;
&lt;br /&gt;
Thus, any theoretical exploit which would give kernel memory corruption or ROP under a kernel thread would allow making use of this mapping to modify kernel text + bypass KASLR.&lt;br /&gt;
&lt;br /&gt;
This was fixed in [[16.0.0]] by unmapping the identity-mapping during init, and re identity-mapping only the very first page of kernel .text as R-X (for use by wake-from-sleep), which fixes the shellcode problem and mostly fixes the ROP problem, since this page mostly lacks interesting gadgets.&lt;br /&gt;
| In theory, with another exploitable kernel memory corruption (or ROP under kernel thread) bug: bypassing KASLR + modifying kernel .text. &lt;br /&gt;
&lt;br /&gt;
However, no such bugs are known.&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| Summer 2018&lt;br /&gt;
| February 2023&lt;br /&gt;
| Everyone&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== BootImagePackage System Modules ==&lt;br /&gt;
Flaws in this category pertain to any of the [[Package2#Section_1|built-in system modules]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Service access control bypass (sm:h, smhax, probably other names)&lt;br /&gt;
| Prior to [[3.0.1]], the &#039;&#039;service manager&#039;&#039; (sm) built-in system module treats a user as though it has full permissions if the user creates a new &amp;quot;sm:&amp;quot; port session but bypasses [[Services_API#Initialize|initialization]]. This is due to the other sm commands skipping the service ACL check for Pids &amp;lt;= 7 (i.e. all kernel bundled modules) and that skipping the initialization command leaves the Pid field uninitialized.&lt;br /&gt;
In [[3.0.1]], sm returns error code 0x415 if [[Services_API#Initialize|Initialize]] has not been called yet.&lt;br /&gt;
| Acquiring, registering, and unregistering arbitrary services&lt;br /&gt;
| [[3.0.1]]&lt;br /&gt;
| [[3.0.1]]&lt;br /&gt;
| May 2017&lt;br /&gt;
| August 17, 2017&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Overly permissive SPL service&lt;br /&gt;
| The concept behind the switch&#039;s [[SMC|Secure Monitor]] is that all cryptographic keydata is located in userspace, but stored as &amp;quot;access keys&amp;quot; encrypted with &amp;quot;keks&amp;quot; that never leave TrustZone. The [[SPL services|spl]] (&amp;quot;security processor liaison&amp;quot;?) service serves as an interface between the rest of the system and the secure monitor. Prior to [[4.0.0]], spl exposed only a single service &amp;quot;spl:&amp;quot;, which provided all TrustZone wrapper functions to all sysmodules with access to it. Thus anyone with access to the spl: service (via smhax or by pwning a sysmodule with access) could do crypto with any access keys they knew. &lt;br /&gt;
&lt;br /&gt;
This was fixed in [[4.0.0]] by splitting spl: into spl:, spl:mig, spl:ssl, spl:es, and spl:fs.&lt;br /&gt;
| Arbitrary spl: crypto with any access keys one knows. For example, one could use the SSL module&#039;s access keys to decrypt their console&#039;s SSL certificate private key without having to pwn the SSL sysmodule.&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| Summer 2017 (after smhax was discovered).&lt;br /&gt;
| December 23, 2017&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Single session services not really single session&lt;br /&gt;
| Several &amp;quot;critical&amp;quot; services (like fsp-ldr, fsp-pr, sm:m, etc) are meant to only ever hold a single session with a specific sysmodule. However, when a sysmodule dies, all its service session handles are released -- and thus killing the holder of a single session handle would allow one (via sm:hax etc) to get access to that service. &lt;br /&gt;
&lt;br /&gt;
This was fixed in [[4.0.0]] by adding a semaphore to these critical single-session services, so that even if one gets access to them an error code will be returned when attempting to use any of their commands.&lt;br /&gt;
| With some way to access these services and kill their session holders (like expLDR): dumping sysmodule code, arbitrary service access, elevated filesystem permissions, etc.&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| May/June 2017 (basically immediately after smhax was discovered)&lt;br /&gt;
| December 30, 2017&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| nspwn&lt;br /&gt;
| fsp-ldr command 0 &amp;quot;MountCode&amp;quot; takes in a Content Path (retrieved from NCM by Loader), and returns an IFileSystem for the resulting ExeFS. These content paths, are normally NCAs, but MountCode also supports a number of other formats, including &amp;quot;.nsp&amp;quot; -- which is just a PFS0.&lt;br /&gt;
&lt;br /&gt;
When a path ending in &amp;quot;.nsp&amp;quot; is parsed by MountCode, the PFS0 is treated as a raw ExeFS. Because there is no NCA header, the ACID signatures are not validated -- and because there are no other signatures in a PFS0, this results in no signature checking happening at all.&lt;br /&gt;
&lt;br /&gt;
The actual .nsp handling is eventually done by {content mounting function} called by MountCode and other FS commands.&lt;br /&gt;
&lt;br /&gt;
Thus, by placing an ExeFS (NSOs + &amp;quot;main.npdm&amp;quot;) and setting one&#039;s desired title ID to &amp;quot;@Sdcard:/some_title.nsp&amp;quot; or &amp;quot;@User:/some_title.nsp&amp;quot; etc one can launch arbitrary unsigned code, with arbitrary unsigned NPDMs.&lt;br /&gt;
&lt;br /&gt;
This appears to have been fixed by only allowing .nsp when the input fstype==7 for the internal content-mounting function, returning 0x2EE202 otherwise.&lt;br /&gt;
| With access to &amp;quot;lr&amp;quot;: Arbitrary code execution with full system privileges.&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| Late 2017&lt;br /&gt;
| April 23, 2018&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Single null-byte stack overflow in Loader ContentPath parsing&lt;br /&gt;
| Previously, loader content path parsing looked like this, where path_from_lr was up to 0x300 bytes and not necessarily null-terminated:&lt;br /&gt;
&lt;br /&gt;
  char nca_path[0x300] = {0};&lt;br /&gt;
  strcat(nca_path, path_from_lr);&lt;br /&gt;
  for (int i = 0; nca_path[i]; i++) {&lt;br /&gt;
      if (nca_path[i] == &#039;\\&#039;) { nca_path[i] = &#039;/&#039;); }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Thus, a content path of the maximum length (0x300 bytes) would result in strcat writing a NULL terminator past the end of the nca_path buffer.&lt;br /&gt;
&lt;br /&gt;
This was fixed in [[6.0.0]], the new code looks like this:&lt;br /&gt;
&lt;br /&gt;
  char nca_path[0x300];&lt;br /&gt;
  strncpy(nca_path, path_from_lr, sizeof(nca_path));&lt;br /&gt;
  for (int i = 0; i  &amp;lt; sizeof(nca_path) &amp;amp;&amp;amp; nca_path[i]; i++) {&lt;br /&gt;
      if (nca_path[i] == &#039;\\&#039;) { nca_path[i] = &#039;/&#039;); }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
| With access to &amp;quot;lr&amp;quot;: single null-byte stack overflow in Loader. Maybe (but probably not) loader code execution.&lt;br /&gt;
| [[6.0.0]]&lt;br /&gt;
| [[6.0.0]]&lt;br /&gt;
| September 2, 2018&lt;br /&gt;
| September 19, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]]&lt;br /&gt;
|-&lt;br /&gt;
| System modules vulnerable to selective downgrade attacks&lt;br /&gt;
| Horizon has no mechanism for specifying the specific title version to Loader on process creation.&lt;br /&gt;
&lt;br /&gt;
Observing this, one can note that after a system update one could install a downgraded version of a specific system module (e.g. nvservices) while leaving the rest of the OS at the same version.&lt;br /&gt;
&lt;br /&gt;
Unless there was some breaking API change, this allows one to make a console vulnerable once more to an exploit in a sysmodule by downgrading it and nothing else.&lt;br /&gt;
&lt;br /&gt;
This was fixed in [[8.1.0]] by incrementing a version field in NPDM, and checking it against a hardcoded list for certain titles in Loader&#039;s process creation func.&lt;br /&gt;
| With access to content installation commands (or a vulnerable lower version to selectively install newer titles), reintroducing bugs in vulnerable system modules on newer firmware versions.&lt;br /&gt;
| [[8.1.0]]&lt;br /&gt;
| [[8.1.0]]&lt;br /&gt;
| When FIRM was first dumped in 2017.&lt;br /&gt;
| June 17, 2019&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Broken RNG for [[Loader_services|Loader]] ASLR&lt;br /&gt;
| The RNG used for generating the ASLR slide is only seeded with 32bits, with the data from [[SVC|svcGetInfo]]. Hence, one could bruteforce the seed if one has infoleaks from any programs. This can be successfully bruteforced with at least 2 sample codebin addrs from different programs (with only 1 sample a lot of invalid seeds are found), however in some cases more than 1 seed might be found.&lt;br /&gt;
&lt;br /&gt;
With [15.0.0+] Loader now uses csrng_GenerateRandomBytes for determining the ASLR slide.&lt;br /&gt;
&lt;br /&gt;
See also [https://github.com/switchbrew/loader-aslr-solver loader-aslr-solver].&lt;br /&gt;
| Breaking ASLR for all non-KIP processes, allowing predicting the main-codebin base addr for all non-KIP processes until the next reboot.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| January 30, 2022 (presumably found much earlier?)&lt;br /&gt;
| October 11, 2022&lt;br /&gt;
| Everyone&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== System Modules ==&lt;br /&gt;
Flaws in this category pertain to any non-built-in system module.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| OOB Read in NS system module (pl:utoohax, pl:utonium, maybe other names)&lt;br /&gt;
| Prior to [[3.0.0]], pl:u (Shared Font services implemented in the NS sysmodule) service commands 1,2,3 took in a signed 32-bit index and returned that index of an array but did not check that index at all. This allowed for an arbitrary read within a 34-bit range (33-bit signed) from NS .bss. In [[3.0.0]], sending out of range indexes causes error code 0x60A to be returned.&lt;br /&gt;
| Dumping full NS .text, .rodata and .data, infoleak, etc&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| April 2017&lt;br /&gt;
| June 19, 2017&lt;br /&gt;
| [[User:qlutoo|qlutoo]], ReSwitched Team (independently)&lt;br /&gt;
|-&lt;br /&gt;
| Unchecked domain ID in common IPC code&lt;br /&gt;
| Prior to [[2.0.0]], object IDs in [[IPC_Marshalling#Domain_message|domain messages]] are not bounds checked. This out-of-bounds read could be exploited to brute-force ASLR and get PC control in some services that support domain messages.&lt;br /&gt;
|&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| July 2017&lt;br /&gt;
| July 20, 2017‎&lt;br /&gt;
| [[User:hthh|hthh]]&lt;br /&gt;
|-&lt;br /&gt;
| Out-of-bounds array read for [[BCAT_Content_Container]] secret-data index&lt;br /&gt;
| The [[BCAT_Content_Container]] secret-data index is not validated at all. This is handled before the RSA-signature(?) is ever used. Since the field is an u8, a total of 0x800-bytes relative to the array start can be accessed.&lt;br /&gt;
This is not useful since the string loaded from this array is only involved with key-generation.&lt;br /&gt;
| &lt;br /&gt;
| Unknown&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| August 4, 2017&lt;br /&gt;
| August 6, 2017&lt;br /&gt;
| [[User: shinyquagsire23|Shiny Quagsire]], [[User:Yellows8|yellows8]] (independently)&lt;br /&gt;
|-&lt;br /&gt;
| expLDR (sysmodule handle table exhaustion)&lt;br /&gt;
| Most sysmodules share common template code to handle IPC control messages. The command DuplicateSession (type 5 command 2)&#039;s template code will abort() if it fails to duplicate a session&#039;s handle for the requester. Because many sysmodules have limited handle table size (smaller than the browser/other entrypoints), repeatedly requesting to duplicate one&#039;s session will cause the sysmodule to run out of handle table space and abort, causing the service to release all its handles cleanly.&lt;br /&gt;
| Sysmodule crashes.  Most usefully, crashing ldr allows access to fsp-ldr and crashing pm allows access to fsp-pr. Useless after [[4.0.0]], which mitigated a number of single-session service access issues.&lt;br /&gt;
| Unfixed&lt;br /&gt;
| [[4.1.0]]&lt;br /&gt;
| June 24, 2017&lt;br /&gt;
| March 8, 2018&lt;br /&gt;
| [[User:daeken|daeken]]&lt;br /&gt;
|-&lt;br /&gt;
| Transfer Memory leak in nvservices system module&lt;br /&gt;
| The nvservices sysmodule does not clear most of its transfer memory prior to release.&lt;br /&gt;
| The calling process can read key bits of memory, including breaking ASLR (by revealing the image base) and exposing the address of other transfer memory to set up attacks. More details here: [https://daeken.svbtle.com/nintendo-switch-nvservices-info-leak transfermeme (nvservices info leak)] by [[User:daeken|daeken]]&lt;br /&gt;
| [[6.0.0]]&lt;br /&gt;
| [[6.0.0]]&lt;br /&gt;
| June 2017&lt;br /&gt;
| October 16, 2018&lt;br /&gt;
| [[User:qlutoo|qlutoo]] and [[User:hexkyz|hexkyz]],&lt;br /&gt;
[[User:daeken|daeken]] (independently)&lt;br /&gt;
|-&lt;br /&gt;
| OOB write in audio system module&lt;br /&gt;
| Prior to [[2.0.0]], the [[Audio_services#audout:u|AppendAudioOutBuffer]] and [[Audio_services#audin:u|AppendAudioInBuffer]] IPC commands would blindly increment the appended buffers&#039; count while using said count value as an index to where the user data should be copied into. This resulted in an 0x28 bytes, user controlled, out-of-bounds memory write into the [[Audio_services|audio]] sysmodule&#039;s memory space.&lt;br /&gt;
Combined with the [[Audio_services#audout:u|GetReleasedAudioOutBuffer]] or [[Audio_services#audin:u|GetReleasedAudioInBuffer]] commands, this could also be used as an 8 byte infoleak.&lt;br /&gt;
&lt;br /&gt;
In [[2.0.0]], the commands now return error code 0x1099 if the number of unreleased buffers exceeds 0x1F.&lt;br /&gt;
| Code execution under audio sysmodule&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| &lt;br /&gt;
| November 2, 2018&lt;br /&gt;
| [[User:hexkyz|hexkyz]], probably others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| Infoleak in nvservices system module&lt;br /&gt;
| The [[NV_services|nvservices]] ioctl [[NV_services#NVMAP_IOC_ALLOC|NVMAP_IOC_ALLOC]] takes an optional argument &amp;quot;addr&amp;quot; which allows the calling process to pass a pointer to user allocated memory for backing a nvmap object. If &amp;quot;addr&amp;quot; is left as 0, nvservices uses the transfer memory region (donated by the user during initialization) instead, when allocating memory for the nvmap object.&lt;br /&gt;
By design, freeing the nvmap object by calling the ioctl [[NV_services#NVMAP_IOC_FREE|NVMAP_IOC_FREE]] returns, in its &amp;quot;refcount&amp;quot; argument, the user address previously supplied if the reference count reaches 0.&lt;br /&gt;
However, prior to [[6.2.0]], the case where the transfer memory region is used to allocate the nvmap object was not taken into account, thus resulting in [[NV_services#NVMAP_IOC_FREE|NVMAP_IOC_FREE]] leaking back an address from within the transfer memory region mapped in nvservices&#039; memory space.&lt;br /&gt;
&lt;br /&gt;
In [[6.2.0]], [[NV_services#NVMAP_IOC_FREE|NVMAP_IOC_FREE]] no longer returns the address when the transfer memory region is used instead of user supplied memory.&lt;br /&gt;
| Combined with other vulnerabilities: Defeating ASLR in nvservices sysmodule.&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| April 2017&lt;br /&gt;
| November 24, 2018&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| nvhax (memory corruption in nvservices system module)&lt;br /&gt;
| Prior to [[6.2.0]], the [[NV_services|nvservices]] ioctl [[NV_services#.2Fdev.2Fnvhost-ctrl-gpu|NVGPU_GPU_IOCTL_WAIT_FOR_PAUSE]] would take a single &amp;quot;pwarpstate&amp;quot; argument which would be interpreted by nvservices as a memory pointer for writing 2 &amp;quot;warpstate&amp;quot; structs (one for each Streaming Multiprocessor).&lt;br /&gt;
This resulted in nvservices attempting to blindly memcpy into this user supplied address and trigger a crash. However, if paired with an infoleak, this could be used to arbitrarily write 0x30 bytes anywhere in nvservices&#039; memory space.&lt;br /&gt;
Additionally, the &amp;quot;warpstate&amp;quot; struct itself was never initialized, which means nvservices would leak the 0x30 bytes from the stack. By invoking other ioctls it was also possible to partially control the stack contents and achieve a usable arbitrary memory write primitive.&lt;br /&gt;
&lt;br /&gt;
In [[6.2.0]], [[NV_services#.2Fdev.2Fnvhost-ctrl-gpu|NVGPU_GPU_IOCTL_WAIT_FOR_PAUSE]] now takes 2 inline &amp;quot;warpstate&amp;quot; structs instead of a &amp;quot;pwarpstate&amp;quot; pointer, thus effectively avoiding the bad memcpy.&lt;br /&gt;
| Code execution under nvservices sysmodule&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| April 5, 2017&lt;br /&gt;
| November 24, 2018&lt;br /&gt;
| [[User:hexkyz|hexkyz]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Applet_Manager_services#IStorage|AM IStorage]] infoleak&lt;br /&gt;
| Originally the buffer allocated by [[Applet_Manager_services#CreateStorage|CreateStorage]] using the specified input size was not cleared. With [8.0.0+] this was fixed by adding a memset() for the buffer after successful allocation.&lt;br /&gt;
&lt;br /&gt;
Hence, IStorage-&amp;gt;IStorageAccessor-&amp;gt;Read will return uninitialized memory when the Write cmd was not previously used with the specified region.&lt;br /&gt;
| Infoleak from the main [[Applet_Manager_services#IStorage|AM]] heap, allowing defeating ASLR by reading addresses from previously allocated objects.&lt;br /&gt;
| [[8.0.0]]&lt;br /&gt;
| [[8.1.0]]&lt;br /&gt;
| December 2018&lt;br /&gt;
| August 9, 2019&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[HID_services#hid:sys|hid:sys]] ButtonConfig s32 array-index not validated&lt;br /&gt;
| The input s32 array-index for [[HID_services#hid:sys|hid:sys]] ButtonConfig cmds 1255-1270 was originally not validated. Using a negative or &amp;gt;=5 index results in accessing out-of-bounds data, with an array stored on stack.&lt;br /&gt;
[10.1.0-10.2.0] Each of these cmds will now Abort if the s32 is negative or &amp;gt;=5. [11.0.0+] Now an unsigned compare is used, with 0 or an error being immediately returned when the value is invalid.&lt;br /&gt;
| hid infoleak, out-of-bounds mem-write anywhere in hid address-space relative to the stack array (with constraints on the data).&lt;br /&gt;
| [[10.1.0]]&lt;br /&gt;
| [[11.0.1]]&lt;br /&gt;
| April 18, 2020&lt;br /&gt;
| July 14, 2020&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|Bluetooth]] sdp_server.cc process_service_search() continuation request p_req validation&lt;br /&gt;
| With [5.0.0+], the following was added to the if-block prior to loading cont_offset from p_req: &amp;lt;code&amp;gt;(p_req + sizeof(cont_offset) &amp;gt; p_req_end)&amp;lt;/code&amp;gt; (which verifies that cont_offset is within message bounds).&lt;br /&gt;
| Bluetooth-sysmodule out-of-bounds read from heap, probably not useful since the read value must match a state field, etc.&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[11.0.0]]&lt;br /&gt;
| Switch: December 2020&lt;br /&gt;
| Switch: December 25, 2020&lt;br /&gt;
| Switch: [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|Bluetooth]] A-63146698&lt;br /&gt;
| [https://android.googlesource.com/platform/system/bt/+/226ea26684d4cd609a5b456d3d2cc762453c2d75 A-63146698] / CVE-2017-0785. See also [https://info.armis.com/rs/645-PDC-047/images/BlueBorne%20Technical%20White%20Paper_20171130.pdf here].&lt;br /&gt;
| Bluetooth-sysmodule stack infoleak, which allows defeating ASLR (note: not tested on hw).&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[11.0.0]]&lt;br /&gt;
| Switch: December 2020&lt;br /&gt;
| Switch: December 25, 2020&lt;br /&gt;
| Switch: [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] GetAdapterProperty/SetAdapterProperty unchecked memcpy size&lt;br /&gt;
| GetAdapterProperty copies data from stack to the output buffer using the buffer size, without checking the size (when not handling the Name type). SetAdapterProperty copies data to stack from the input buffer using the buffer size, without checking the size.&lt;br /&gt;
This requires access to the btdrv service, only hid and btm have access.&lt;br /&gt;
&lt;br /&gt;
This was fixed with [[12.0.0]] by replacing the buffer data with a fixed-size-struct.&lt;br /&gt;
| Stack infoleak with GetAdapterProperty, stack buffer overflow (and hence ROP) with SetAdapterProperty.&lt;br /&gt;
| [[12.0.0]]&lt;br /&gt;
| [[12.0.0]]&lt;br /&gt;
| July 17, 2020&lt;br /&gt;
| April 7, 2021&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] stack buffer overflow with HID DATA packets&lt;br /&gt;
| The BSA (bt-stack) func bta_hh_co_data copies data from a HID DATA packet to stack without checking the size, then sends it over Uipc. [7.0.0+] The user Uipc callback also copies the input data to stack without checking the size, then sends it to the sharedmem CircularBuffer.&lt;br /&gt;
With [12.0.2+] this was fixed in bta_hh_co_data by clamping the size to a maximum of 0x2BB. The aforementioned buffer overflow in the Uipc callback can&#039;t be triggered since at that point the size was already clamped.&lt;br /&gt;
&lt;br /&gt;
Before this bta_hh_co_data func is reached, there is no validation of the size (such as comparing against the L2CAP MTU) when Basic Mode is being used.&lt;br /&gt;
&lt;br /&gt;
Actually triggering this requires using a data-size larger than the normal L2CAP MTU. This can be done by for example, using raw HCI to send the packet from the remote bluetooth device.&lt;br /&gt;
&lt;br /&gt;
Note that when the remote device is configured as an audio device for [12.0.0+] where [[Settings_services#BluetoothDevicesSettings|BluetoothDevicesSettings]].TrustedServices was only ever set for audio since system-boot, it is not possible for the remote device to connect to the Switch for HID.&lt;br /&gt;
| ROP under [[Bluetooth_Driver_services|bluetooth]] via HID DATA packet sent by a paired HID bluetooth device. This can be triggered at any time while not in sleep-mode, when not in airplane-mode. The earliest is while the Nintendo Switch logo screen is displayed during system boot.&lt;br /&gt;
| [[12.0.2]]&lt;br /&gt;
| [[12.0.2]]&lt;br /&gt;
| July-August 2020&lt;br /&gt;
| May 11, 2021&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] WriteHidData/WriteHidData2/SetHidReport unchecked memcpy size&lt;br /&gt;
| WriteHidData/SetHidReport copies the input struct to stack, then passes it to the funcptr/vfunc call. WriteHidData2 passes the input buffer addr directly to the funcptr/vfunc call. The called func eventually copies the input data to the stack struct using the specified size without validating it.&lt;br /&gt;
This requires access to the btdrv service, only hid and btm have access.&lt;br /&gt;
&lt;br /&gt;
This was fixed with [[12.1.0]] in WriteHidData/SetHidReport by doing a fixed-size copy into another tmp struct, with the size field being clamped to a maximum of 0x2BB afterwards. This struct is then used when calling the vfunc. The vfuncs called by WriteHidData/WriteHidData2/SetHidReport were also updated to clamp the size to the required maximum value.&lt;br /&gt;
| Stack buffer overflow&lt;br /&gt;
| [[12.1.0]]&lt;br /&gt;
| [[12.1.0]]&lt;br /&gt;
| July 16, 2020&lt;br /&gt;
| July 6, 2021&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Infoleak with [[HID_services|hid:sys]] SetButtonConfigStorage{name}Deprecated&lt;br /&gt;
| These cmds pass a stack ptr for the StorageName when calling the internal func. Nothing is written to this StorageName. Hence, stack infoleak (data is copied as a NUL-terminated string), which can be later read by the GetButtonConfigStorage{name} cmds.&lt;br /&gt;
&lt;br /&gt;
This was fixed by removing the Deprecated cmds in [[13.0.0]].&lt;br /&gt;
| Infoleak of hid stack from a StorageName readable via GetButtonConfigStorage{name}, up to the NUL-terminator.&lt;br /&gt;
| [[13.0.0]]&lt;br /&gt;
| [[13.0.0]]&lt;br /&gt;
| December 11, 2020&lt;br /&gt;
| September 27, 2021&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] EventInfo infoleak&lt;br /&gt;
| The various funcs which send messages to the thread which handles writing to EventInfo, didn&#039;t clear the stack msgbuf. Hence, the various get-EventInfo cmds could return leaked stack data. This likely affected most (?) get-EventInfo cmds, besides CircularBuffer-GetHidReportEventInfo.&lt;br /&gt;
&lt;br /&gt;
This only matters for events where there&#039;s uninitialized regions of the EventInfo, such as events with variable-size data without a memset.&lt;br /&gt;
&lt;br /&gt;
This was fixed by clearing the msgbuf in a number of funcs.&lt;br /&gt;
| Bluetooth-sysmodule stack infoleak, which allows defeating ASLR&lt;br /&gt;
| [[13.0.0]]&lt;br /&gt;
| [[13.1.0]]&lt;br /&gt;
| &lt;br /&gt;
| During initial [[13.0.0|diff]]. Added to this page on: December 12, 2021&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[SSL_services|ssl]] CVE-2021-43527&lt;br /&gt;
| CVE-2021-43527, see also [https://bugs.chromium.org/p/project-zero/issues/detail?id=2237 here] and [https://googleprojectzero.blogspot.com/2021/12/this-shouldnt-have-happened.html here].&lt;br /&gt;
Using BigSig where the server cert sig is RSA-PSS results in the remote server throwing {no shared cipher} error when Switch connects. If however one creates a rootCA using BigSig (RSA-PSS), which then signs a server cert where the server key is RSA (not PSS), the vuln can be triggered (if the rootCA is trusted, via using the import service-cmd). It&#039;s unknown whether there&#039;s other ways to trigger the vuln.&lt;br /&gt;
&lt;br /&gt;
The crash occurs in VFY_Begin when using the previously overwritten data. A bitsize of &amp;lt;code&amp;gt;$((16384 + 32 + 64 + 64 + 64))&amp;lt;/code&amp;gt; is only enough to overwrite cx-&amp;gt;hashcx, to fully overwrite cx-&amp;gt;hashobj an additional 0xC-bytes (additional 96 bits) is needed.&lt;br /&gt;
Note that partial overwrite isn&#039;t an option: this is the func that initializes those fields to begin with, it just does deinit first before initializing hashcx/hashobj (prior to that these fields would be all-zero when not overwritten by the buf-overflow).&lt;br /&gt;
| Heap buffer overflow in [[SSL_services|ssl]], overwriting data including a ptr to an object which is later used to load a funcptr.&lt;br /&gt;
| [[13.2.1]]&lt;br /&gt;
| [[13.2.1]]&lt;br /&gt;
| Switch: December 1-2, 2021&lt;br /&gt;
| Switch: January 19, 2022&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] BSA gatt_process_notification stack buffer overflow&lt;br /&gt;
| gatt_process_notification is the GATT handler for processing notification/indication messages. gatt_process_notification does memcpy to stack from the input bt msg data, without size validation. The input len param isn&#039;t validated in this func either - if the remaining len following op_code is less than 2, a negative value will be used for the data copy to stack.&lt;br /&gt;
These were fixed by adding a bounds check for the size, size==0 is also checked for now.&lt;br /&gt;
| Bluetooth-sysmodule stack buffer overflow, with data received from a bluetooth message&lt;br /&gt;
| [[13.2.1]]&lt;br /&gt;
| [[13.2.1]]&lt;br /&gt;
| November 2021&lt;br /&gt;
| January 19, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Applet_Manager_services#IDisplayController|AM IDisplayController]] TakeScreenShotOfOwnLayer OOB&lt;br /&gt;
| The captureBuf is used as an array index without validation. Data used from this array includes calling a funcptr from the array entry, if set. Eventually this is also used to write bools into this array, one of which is from the command input.&lt;br /&gt;
With [5.0.0+] a func is eventually called to get a ptr determined by the input captureBuf, with nullptr being returned for captureBuf&amp;gt;=0x10. The caller will Abort if nullptr was returned.&lt;br /&gt;
| OOB array access&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[13.1.0]]&lt;br /&gt;
| ~July 31, 2019&lt;br /&gt;
| January 26, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Applet_Manager_services#IDisplayController|AM IDisplayController]] ClearCaptureBuffer OOB&lt;br /&gt;
| The captureBuf is used as an array index without proper validation. There is code validating it, but on failure it just skips over a code-block, with code using captureBuf still being used afterwards. Then this is used to write bools into a global array, one of which is from the command input.&lt;br /&gt;
This was fixed with [9.1.0+] by requiring captureBuf = 0-1.&lt;br /&gt;
| OOB bool writes into an array&lt;br /&gt;
| [[9.1.0]]&lt;br /&gt;
| [[13.1.0]]&lt;br /&gt;
| ~July 31, 2019&lt;br /&gt;
| January 26, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Sockets_services|bsdsockets]] ioctl SIOCGIFCONF infoleak&lt;br /&gt;
| Originally bsd ioctl SIOCGIFCONF was handled by setting the data in IPC outbuf0 to the size/addr of IPC outbuf1. These buffers are HipcAutoSelect, so if buf1 is small enough for HipcPointer (otherwise it would be HipcMapAlias) the IPC-buf-ptr leaked into outbuf0 would be located in the codebin-region. Since this is done before the actual ioctl-handling, it doesn&#039;t matter whether the fd is valid.&lt;br /&gt;
This was fixed in [5.0.0+] by using a tmp struct on stack instead of buf0.&lt;br /&gt;
| bsdsockets-sysmodule codebin-region addr infoleak, which allows defeating ASLR.&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[13.1.0]]&lt;br /&gt;
| February 14, 2022 (probably earlier)&lt;br /&gt;
| February 14, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]], probably others&lt;br /&gt;
|-&lt;br /&gt;
| [[Sockets_services|bsdsockets]] ioctl SIOCGIFMEDIA input can contain ptr&lt;br /&gt;
| Originally bsd ioctl SIOCGIFMEDIA used the user-specified ifmediareq structure directly from the input buffer. This includes a ptr. This ptr probably isn&#039;t actually used?&lt;br /&gt;
With [5.0.0+] the structure used as input for the ioctl was changed to using &amp;lt;code&amp;gt;int ifm_ulist[1]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;int *ifm_ulist&amp;lt;/code&amp;gt; (which is unused). The input structure is copied to a tmp struct which is used as the original ifmediareq structure, with ifm_ulist always NULL. The user can still specify a non-zero ifm_count value, however that&#039;s not useful with ifm_ulist being always NULL.&lt;br /&gt;
| Useless?&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[13.1.0]]&lt;br /&gt;
| February 14, 2022&lt;br /&gt;
| February 14, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]], probably others&lt;br /&gt;
|-&lt;br /&gt;
| Infoleak with [[Joy-Con]] HidCommand PairingIn&lt;br /&gt;
| The joycon protocol handler for PairingIn copies data from stack to the response cmd-buf for sending PairingOut. Only the first byte is set to a type value, the rest is uninitialized stack data.&lt;br /&gt;
&lt;br /&gt;
This was fixed with [15.0.0+] by directly writing to the response data without using stack data.&lt;br /&gt;
| Infoleak of hid stack via a bluetooth/uart message+response with a connected hid controller. This returns addrs for the main-codebin/stack, which allows defeating ASLR.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| September 4, 2020&lt;br /&gt;
| October 10, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Broken RNG for [[RO_services|ro]] ASLR&lt;br /&gt;
| The RNG used to determine where to randomly map NROs in the target process was TinyMT (nn::os::detail::RngManager output, seeded by 128 bits of entropy). However, TinyMT is not cryptographically secure (and can in fact be analytically solved). &lt;br /&gt;
&lt;br /&gt;
Thus, with a few NRO mapping addresses, one could learn the TinyMT state and derive all previous/future RNG outputs, breaking NRO aslr for all processes. &lt;br /&gt;
&lt;br /&gt;
With [15.0.0+] ro now uses csrng_GenerateRandomBytes to determine the random map address for NROs.&lt;br /&gt;
| Breaking ASLR for all NROs loaded in all processes, allowing predicting all NRO mappings for all processes until the next reboot.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| Late 2021/Early 2022&lt;br /&gt;
| October 11, 2022&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Broken RNG used by [[NS_Services|ns]]&lt;br /&gt;
| The code generating the sd seed and the data for the [[SD_Filesystem|sd]] private/private1 file, all use nn::os::GenerateRandomBytes, not csrng. The sd-seed is generated first, then private, then private1. This allows deriving sd-seed from private since this uses TinyMT, as long as the system shipped from factory on [2.0.0+]. private1 is only useful if the system shipped with [4.0.0+].&lt;br /&gt;
&lt;br /&gt;
There&#039;s various other code in ns using nn::os::GenerateRandomBytes as well. This includes the code generating ns_systemseed when it doesn&#039;t exist. ns_systemseed is generated at some point after the various sd-seed-related code (both are called from the same func). Hence, ns_systemseed can be recovered with the above method as well, if it wasn&#039;t recreated at some point without regenerating the above nand-save used with the above.&lt;br /&gt;
&lt;br /&gt;
With [15.0.0+] ns now uses csrng_GenerateRandomBytes for sd-seed/private and ns_systemseed, etc. This only matters when the file is newly generated, which is usually only for factory-fresh systems which ship with this version. This would also apply after being deleted during {System Settings -&amp;gt; Formatting Options -&amp;gt; Initialize Console}, and also with a refurbished console.&lt;br /&gt;
| Generation of a system&#039;s sd-seed allowing decryption of the NAX0 layer of data on [[SD_Filesystem|SD]], derived using the private file from SD. Applies to systems which factory-shipped with a system-version prior to [[15.0.0]] (that is, [2.0.0-14.1.2]).&lt;br /&gt;
| [[15.0.0]], for newly generated files&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| December ~12, 2021&lt;br /&gt;
| October 11, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] BSA bsa_sv_av_cback stack buffer overflow&lt;br /&gt;
| bsa_sv_av_cback checks for two input type values (0xC/0xD), on match it copies the input data to stack without size validation. Then it sends an internal request with this data (likewise when the type values don&#039;t match, except the input data is passed directly with a small size), then it returns.&lt;br /&gt;
This requires the AV functionality added with [13.0.0+], however this func is only reachable with [14.0.0+] where the required functionality was enabled.&lt;br /&gt;
&lt;br /&gt;
This requires message data that&#039;s larger than the MTU, so fragmentation must be used, or manually send the ACL data to bypass the MTU.&lt;br /&gt;
&lt;br /&gt;
This can be triggered via an AVRC message with opcode=0x0 (vendor). The above type 0xC is reached via AVRC ctype 0..4, while 0xD is reached with ctype&amp;gt;=0x9.&lt;br /&gt;
&lt;br /&gt;
With [15.0.0+] the size value for the memcpy (which is also written to the request struct) is clamped to a max value.&lt;br /&gt;
| Bluetooth-sysmodule stack buffer overflow on [14.0.0-14.1.2], with data received from an AVRC bluetooth message with a bluetooth-audio device.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| November 2021&lt;br /&gt;
| October 11, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[WLAN_services|wlan]] SetMulticastList heap buffer overflow&lt;br /&gt;
| The [[WLAN_services#SetMulticastList|SetMulticastList]] command allocates a 0x31-bytes sized buffer and copies to it as much [[WLAN_services#MacAddress|MacAddress]] values from the input [[WLAN_services#MulticastList|MulticastList]] as specified by the &amp;quot;Count&amp;quot; field, but this field is never validated. &lt;br /&gt;
&lt;br /&gt;
With [15.0.0+] error code 0x1906B is now returned if &amp;quot;Count&amp;quot; is larger than 8.&lt;br /&gt;
| wlan-sysmodule heap buffer overflow.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| June 6, 2022&lt;br /&gt;
| November 9, 2022&lt;br /&gt;
| [[User:Hexkyz|hexkyz]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] WriteGattCharacteristic/WriteGattDescriptor stack buffer overflow regression&lt;br /&gt;
| Originally btdrv WriteGattCharacteristic/WriteGattDescriptor (bt service LeClientWriteCharacteristic/LeClientWriteDescriptor are the same) validated the input buffer size. However the size check was removed with [12.0.0+] (which was also when bluetooth was refactored), hence stack buffer overflow. Anything with btdrv/bt services access can trigger it. While this is intended to require a BLE connection, it seems to be possible to trigger the buffer overflow without any BLE connection by passing ConnectionHandle=0xFFFFFFFF (handle not tested on hardware).&lt;br /&gt;
| Bluetooth-sysmodule stack buffer overflow on [12.0.0-15.0.1], with data from BLE IPC cmds.&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| December 10, 2021&lt;br /&gt;
| February 23, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[JIT_services|JIT]] usability issues&lt;br /&gt;
| CreateJitEnvironment will enter infinite-loops using nn::jitsrv::detail::AslrAllocator::GetAslrRegion when either of the input CodeMemory sizes are zero. Also the second CodeMemory is useless for the user-process since the second addr returned by GetCodeAddress is a dup of the first one, set during state init by CreateJitEnvironment.&lt;br /&gt;
With [14.0.0+] size=0 is now properly handled, and also the state for the second addr from GetCodeAddress is now properly initialized.&lt;br /&gt;
| Minor usability issues, not useful for exploitation (size=0 will cause jit-sysmodule to hang in a loop).&lt;br /&gt;
| [[14.0.0]]&lt;br /&gt;
| [[14.0.0]]&lt;br /&gt;
| October 1, 2020&lt;br /&gt;
| February 26, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[USB_services|usbhs]] uninitialized IClientEpSession&lt;br /&gt;
| usbhs IClientIfSession OpenUsbEp creates an IClientEpSession object. The allocated object from ExpHeap is not memset, only select fields are cleared. The rest of initialization is done by PopulateRing - however the user-process could skip using that if wanted (official sw always uses it).&lt;br /&gt;
&lt;br /&gt;
ShareReportRing maps tmem and writes the ring buffer/count field into object state. PopulateRing also eventually initializes these fields, with the buffer being allocated from ExpHeap instead of tmem. These fields are not cleared during object creation from OpenUsbEp.&lt;br /&gt;
&lt;br /&gt;
GetXferReport after validating the cmd input, just uses object state assuming it was initialized. This runs code which is the same as the user-process code handling the tmem ringbuf.&lt;br /&gt;
&lt;br /&gt;
Therefore, by skipping using PopulateRing and then using GetXferReport the sysmodule will use an uninitialized ringbuf ptr, and an uninitialized count field. If one could control these fields by doing ExpHeap allocations prior to OpenUsbEp so that {target fields} would be located at {IClientEpSession ring fields}, then one could read usb-sysmodule memory at the target buffer address.&lt;br /&gt;
&lt;br /&gt;
See [[USB_services#ShareReportRing|here]] for ringbuf format. The sysmodule will Abort if read_index is &amp;gt;= {ring count field from object state}. Otherwise it copies an entry from that index to output, and updates read_index.&lt;br /&gt;
&lt;br /&gt;
This is probably tricky to abuse as the ringbuf ptr has to be valid, and {see above} (likewise for write_index when the report-ringbuf-writing func runs).&lt;br /&gt;
&lt;br /&gt;
PostBufferAsync/BatchBufferAsync also use seperate object ring fields which are left uninitialized from OpenUsbEp. Targeting this would be tricky with the ring restrictions - this would allow writing data to a ring addr however.&lt;br /&gt;
&lt;br /&gt;
Pre-4.0.0 (only 2.0.0 checked) is not affected by these. The ring fields in the object are cleared during object creation (no memset of the entire object however). GetXferReport would null-deref if PopulateRing was skipped. PostBufferAsync/BatchBufferAsync will throw an error if PopulateRing was skipped. Pre-4.0.0 also has different ring handling as well.&lt;br /&gt;
&lt;br /&gt;
[16.0.0+] The IClientEpSession init func now clears the remaining previously uninitialized fields. The cmds using the ring fields still don&#039;t check for NULL, so using GetXferReport/PostBufferAsync/BatchBufferAsync without PopulateRing will just trigger null-deref. Even if the ptr were somehow valid but ring-count field was left at 0, this would then Abort due to: &amp;lt;code&amp;gt;if (ring_count &amp;lt;= index_loaded_from_ringptr) &amp;lt;Abort&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| [4.0.0-15.0.1] If one can trigger using {target values} as the unintialized fields: memory reads from the target addr with GetXferReport, and memory R/W with PostBufferAsync/BatchBufferAsync. This requires access to usb:hs, and an usb device must be connected which is not being used by {other sessions}. If successful, this might (?) result in usb-sysmodule compromise.&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| January 30, 2023&lt;br /&gt;
| February 26, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[NS_services|ns]] RequestMoveApplicationEntity/EstimateSizeToMove buffer overflow&lt;br /&gt;
| ns RequestMoveApplicationEntity eventually calls a func which: Loops through the input buffer. If any entry has value 6, it will call another func to copy data from state to output safely (uses the max_count param). Otherwise, it copies the input buffer to an outbuf (located on caller&#039;s stack) without any size validation (inlined memcpy), even though there is a max_count param.&lt;br /&gt;
&lt;br /&gt;
Additional memwrites are also done to the above outbuf following the initial memcopy. This can be avoided if the buffer doesn&#039;t contain bytes with values 3-6 (if using values in that range is really needed, the cmd input StorageId param can be set to the required value so that the specified value doesn&#039;t trigger the memwrite). Value 6 shouldn&#039;t be used anyway (see above).&lt;br /&gt;
&lt;br /&gt;
ns EstimateSizeToMove first calls the same func which does the copy above (outbuf is also located on stack), then it calls another func. Hence, same vuln here.&lt;br /&gt;
&lt;br /&gt;
By corrupting just the first byte of x29 with EstimateSizeToMove, one can obtain infoleaks. This method with x29 essentially only works with [15.0.0+]. Pre-15.0.0 would require a different method with partial overwrite of retaddr, however it&#039;s unknown whether this would actually work for infoleak (would require [12.0.0+] for the stack layout change).&lt;br /&gt;
With EstimateSizeToMove where x29 is overwritten, the output u64 is the leaked ptr (can be codebin-region). Note that the cmd has to return Result=0 for this to work. x29 is used to load the value which is copied to the cmdreply rawdata.&lt;br /&gt;
&lt;br /&gt;
As of [17.0.0+] an error is thrown if the input array count is larger than 8 (size of the stack dst-array).&lt;br /&gt;
| ns-sysmodule stack buffer overflow, allowing ns infoleak+ROP.&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| January 2, 2023&lt;br /&gt;
| October 17, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[PSC_services|ovln:snd]] OpenSender unvalidated count&lt;br /&gt;
| ovln:snd OpenSender has a count param. This count is used to allocate the specified number of objects in a linked-list for storing the data from Send. If count is 0, the linked-list is left empty, with ptrs to itself within the ISender object.&lt;br /&gt;
&lt;br /&gt;
ISender Send when the above linked-list is empty, runs a switch-statement with &amp;lt;code&amp;gt;(inval&amp;gt;&amp;gt;8)&amp;amp;0xFF&amp;lt;/code&amp;gt;. This uses another linked-list where the ptrs are initially {within ISender obj}.&lt;br /&gt;
No space is allocated in the ISender obj for the linked-list object-data. Therefore using Send with val 1&amp;lt;&amp;lt;8 or 2&amp;lt;&amp;lt;8 (other values throw error) results in the specified input struct being copied into the ISender obj, which then overwrites heap data OOB.&lt;br /&gt;
If for example one used OpenSender again right after the first OpenSender usage, then used Send as described above, this would corrupt the second ISender which includes overwriting the vtable.&lt;br /&gt;
If one would use Send twice in a row like this, the second one would use a corrupted linked-list (written from the first Send). If the linked-list ptrs would be valid (no crash triggered) this would allow one to copy the input data to a controlled addr, though it&#039;s restricted with the linked-list usage.&lt;br /&gt;
&lt;br /&gt;
Using GetUnreceivedMessageCount afterwards is of no interest.&lt;br /&gt;
&lt;br /&gt;
Besides ovln, the only other allocs on this heap is from IPmModule Initialize. This heap is also used for psc:* services (object allocs).&lt;br /&gt;
&lt;br /&gt;
In theory (untested) it may be possible to also use this to obtain infoleaks, however it would only return the high-u32 of ptrs not the low u32. Essentially, one would trigger object allocations so that ExpHeap has layout: {ISender} -&amp;gt; {RF chunk from freeing an object} -&amp;gt; {module object from IPmModule Initialize}. Then one would use the Send vuln to corrupt the RF chunk, changing the size to a larger value. Then one would trigger an object allocation (probably same object which was previously freed), then another object for overwriting the module object (ISender would work) with ptrs at the target offsets in the module object. Then once IPmModule GetRequest is used, the returned u32s would be the high-u32 from ptrs. Due to alignment requirements with each allocation, it isn&#039;t possible to shift the allocations in order to leak ptr low-u32.&lt;br /&gt;
&lt;br /&gt;
[17.0.0+] Now throws an error if the input count for OpenSender is 0.&lt;br /&gt;
| [[PSC_services|psc]]-sysmodule heap memory corruption ([[NS_services|ns]]-sysmodule on pre-8.0.0).&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| January 13, 2023&lt;br /&gt;
| October 20, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[NV_services|nv]] NVGPU_GPU_IOCTL_GET_CHARACTERISTICS Ioctl3 infoleak&lt;br /&gt;
| The handler code for NVGPU_GPU_IOCTL_GET_CHARACTERISTICS for Ioctl/Ioctl3 are essentially the same, except for the value used for the max-size clamp: Ioctl uses constant 0xA0, while Ioctl3 uses the outbuf1_size. So if one uses this with Ioctl3 and a large outbuf1, this will memcpy data OOB from the source buffer, hence infoleak.&lt;br /&gt;
With [17.0.0+] the second block of csel code which previouly essentially used the clamped size from above, was replaced with code which properly clamps to the max-size constant.&lt;br /&gt;
| nvservices-sysmodule infoleak, which allows defeating ASLR.&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| February 25, 2022&lt;br /&gt;
| October 24, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Audio_services|audctl]] GetTargetDeviceInfo infoleak&lt;br /&gt;
| audctl GetTargetDeviceInfo calls an impl func with a ptr to a stackbuf, then if successful memcpys the 0x100-bytes from that buffer to output. This stackbuf is not memset. This func (after doing various state checks) copies a string to output, other than always writing a NUL-terminator there&#039;s no clearing of the buffer.&lt;br /&gt;
&lt;br /&gt;
This will leak audio-sysmodule stack into the output buffer as long as the state/input checks pass (for the remainder of the buffer following the string NUL-terminator).&lt;br /&gt;
&lt;br /&gt;
With [18.0.0+] data is written directly to the outbuf instead of the stack tmpbuf.&lt;br /&gt;
| audio-sysmodule infoleak, which allows defeating ASLR.&lt;br /&gt;
| [[18.0.0]]&lt;br /&gt;
| [[18.0.0]]&lt;br /&gt;
| December 24, 2022&lt;br /&gt;
| March 26, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Audio_services|audctl]] GetSystemInformationForDebug infoleak / buffer overflow&lt;br /&gt;
| audctl GetSystemInformationForDebug calls a func with a 0x1000-byte stack tmpbuf, then afterwards that buffer is memcpy&#039;d into the cmd outbuf. This called func doesn&#039;t clear the buffer. This func eventually uses [[BTM_services|btm]] cmd75 with outarray={global ptr} and count=10. Then if the outcount is s32 &amp;gt;=1, it loops through the output using the outcount, without validating it besides the &amp;lt;1 check. Data from that outarray is copied into the array in the func output buffer (tmpbuf above).&lt;br /&gt;
&lt;br /&gt;
With btm comprimised, one could return a large output count and trigger a stack buffer overflow with data following that global array, however exploiting this would be difficult since that data would be uncontrolled (can&#039;t directly control it from this cmd at least).&lt;br /&gt;
&lt;br /&gt;
A stack infoleak can be obtained with this as well (assuming the above output array isn&#039;t full).&lt;br /&gt;
&lt;br /&gt;
Even though the name has &amp;quot;ForDebug&amp;quot;, there&#039;s no checks which would trigger an error / return early (this also always returns 0).&lt;br /&gt;
&lt;br /&gt;
[18.0.0+] now clears the output buffer, and also now prints strings into the buffer instead of writing binary data (overflow no longer possible).&lt;br /&gt;
| audio-sysmodule infoleak, which allows defeating ASLR. Also audio-sysmodule memory corruption, likely not useful unless there&#039;s a way to control the data.&lt;br /&gt;
| [[18.0.0]]&lt;br /&gt;
| [[18.0.0]]&lt;br /&gt;
| December 7, 2022&lt;br /&gt;
| March 27, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Migration_services|migration]] nn::migration::savedata::IServer cmd1 buffer overflow&lt;br /&gt;
| nn::migration::savedata::IServer cmd1 with [18.0.0-18.0.1] copies data from an array to the output ptr. As the output is an u64 field for the IPC cmd output, this is a field on stack. Hence, if more than 1 entry (8-bytes) are copied a stack buffer overflow will occur. Note that cmd3 loads the same data, except this has a proper output array.&lt;br /&gt;
It&#039;s unknown whether there&#039;s a way to actually control this data with a large enough enough size.&lt;br /&gt;
&lt;br /&gt;
See [[18.1.0]] for the diff/fix.&lt;br /&gt;
| [[Migration_services|migration]] stack buffer overflow, only on [18.0.0-18.0.1].&lt;br /&gt;
| [[18.1.0]]&lt;br /&gt;
| [[18.1.0]]&lt;br /&gt;
| June 11, 2024&lt;br /&gt;
| June 11, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]] (sysupdate diff)&lt;br /&gt;
|-&lt;br /&gt;
| [[SSL_services|ssl]] broken RNG&lt;br /&gt;
| [[SSL_services|ssl]] uses nn::os::GenerateRandomBytes, but not [[SPL_services|spl]] GenerateRandomBytes. See the RNG entries elsewhere. This is used to seed the NSS global RNG (drbg.c, RNG_GenerateGlobalRandomBytes etc).&lt;br /&gt;
&lt;br /&gt;
If one could somehow determine the data which was returned by nn::os::GenerateRandomBytes during seeding (which is likely difficult), the global RNG would be broken.&lt;br /&gt;
&lt;br /&gt;
With [19.0.0+] nn::os::GenerateRandomBytes usage was replaced with [[SPL_services|spl]] GenerateRandomBytes.&lt;br /&gt;
| Breaking [[SSL_services|ssl]] global RNG -&amp;gt; potentially predict RNG data (keys(?)) during TLS comms.&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| December 14, 2021&lt;br /&gt;
| October 8, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Audio_services|audren]] uncleared TransferMemory&lt;br /&gt;
| audren OpenAudioRenderer uses the input tmem as workmem. The IAudioRenderer dtor doesn&#039;t clear the workmem properly. Depending on input params, certain objects stored here have vtables - hence infoleak.&lt;br /&gt;
The exact location in the workmem will vary depending on the input params - these objects are dynamically allocated in the workmem.&lt;br /&gt;
The following will leak vtables: Sink, Effect.&lt;br /&gt;
&lt;br /&gt;
If the initialization func fails, the tmem is unmapped without clearing it first. It&#039;s unknown whether there&#039;s a way to actually trigger an infoleak with this however. With [19.0.0+] it&#039;s now cleared on failure.&lt;br /&gt;
&lt;br /&gt;
With [19.0.0+] the dtor now clears the workmem when needed.&lt;br /&gt;
| Reading leaked data/ptrs from TransferMemory -&amp;gt; defeating ASLR in [[Audio_services|audio]]-sysmodule.&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| December 17, 2022&lt;br /&gt;
| October 13, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Audio_services|audren]] UpdateMixes OOB mem-copy&lt;br /&gt;
| With nn::audio::server::InfoUpdater::UpdateMixes when nn::audio::server::BehaviorInfo::IsMixInParameterDirtyOnlyUpdateSupported() returns true (requires REV7, which is [7.0.0+]), the mix_id from user input is used without validation as input to &amp;lt;code&amp;gt;&amp;lt;nn::audio::server::MixContext::GetInfo(int) const&amp;gt;&amp;lt;/code&amp;gt;, instead of the counter from the for-loop. This allows one to control the destination MixInfo index which the user-input data is written into. If too large, this will trigger OOB data-copy. Note that the u8 at dest_MixInfo+12 must be non-zero.&lt;br /&gt;
Also note that a field is loaded from dest_MixInfo which is used as a splitter_id, so splitters need to be initialized where count is large enough for that id.&lt;br /&gt;
&lt;br /&gt;
With [19.0.0+] after getting the mix_id (loop-index/input) it now does: &amp;lt;code&amp;gt;if (mix_id &amp;lt; 0 || mix_id &amp;gt;= nn::audio::server::MixContext::GetCount()) continue;&amp;lt;/code&amp;gt;&lt;br /&gt;
| OOB mem-copy in [[Audio_services|audio]]-sysmodule, which for example can be used to overwrite a vtable used immediately after UpdateMixes.&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| December 19, 2022&lt;br /&gt;
| October 13, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bus_services|sasbus]] StartPeriodicReceiveMode infoleak&lt;br /&gt;
| StartPeriodicReceiveMode writes a vtable ptr into the mapped tmem at +0. The tmem is mapped RW in the user-process. There is no clearing of tmem during tmem cleanup. Hence, the user-process can read the tmem to obtain a Bus-sysmodule codebin-region infoleak. This vtable-ptr seems to be unused - it&#039;s also empty after the first two entries (stubbed incref/decref).&lt;br /&gt;
[20.0.0+] Removed the vtable ptr, with data intended for the user-process being moved from tmem+0x8 to +0x0. Also, instead of calling memset, funcs are called for manually clearing tmem.&lt;br /&gt;
| Bus-sysmodule infoleak, which allows defeating ASLR.&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| February 22, 2022&lt;br /&gt;
| May 3, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[NFC_services|nfc]] SendCommandByPassThrough buffer overflow&lt;br /&gt;
| SendCommandByPassThrough eventually copies the input buffer into a fixed-size heap buffer, without size validation.&lt;br /&gt;
This was fixed with [20.0.0+] by clamping the size.&lt;br /&gt;
| nfc-sysmodule heap buffer overflow.&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| Late November 2021&lt;br /&gt;
| May 3, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]] (maybe others?)&lt;br /&gt;
|-&lt;br /&gt;
| [[HID_services|hidbus]] EnableJoyPollingReceiveMode infoleak&lt;br /&gt;
| The tmem initialized by hidbus EnableJoyPollingReceiveMode contains a vtable ptr (tmem+0x10), hence infoleak. With [20.0.0+] the vtable ptr write was removed, and tmem is now memset starting at tmem+0x10 instead of +0x20.&lt;br /&gt;
| hid-sysmodule infoleak, which allows defeating ASLR.&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| March 2020&lt;br /&gt;
| May 4, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[SSL_services|ssl]] Certificate verification bypass&lt;br /&gt;
| The ssl sysmodule keeps a list of trusted certificates, that are imported by an app with ImportServerPki. During certificate verification, if the certificate that is provided by the server has the same subject key id as a trusted certificate, the certificate is accepted, even if self-signed. A blog post about this vulnerability can be found [https://reversing.live/sslbypass.html here].&lt;br /&gt;
| Man-in-the-middle for any connection that uses ImportServerPki.&lt;br /&gt;
| [[20.2.0]]&lt;br /&gt;
| [[20.2.0]]&lt;br /&gt;
| June 6, 2025&lt;br /&gt;
| August 8, 2025&lt;br /&gt;
| [https://github.com/kinnay Yannik]&lt;br /&gt;
|-&lt;br /&gt;
| [[LDN_services|ldn]] AdvertiseData OOB-memcpy with EncryptionType3 (AES-128-GCM) actionframes (ldnhax)&lt;br /&gt;
| The ldn action-frame parser object for AES-128-GCM (used with [[LDN_services|EncryptionType3]]), when it does validation once finished, only verifies that the sizes are within bounds of the input buffer. There&#039;s no validation against constants, which the other EncryptionType objects have. The caller code doesn&#039;t validate the size either.&lt;br /&gt;
&lt;br /&gt;
[21.0.0+] Now validates the advert-size with sizeof(NetworkInfo.AdvertiseData).&lt;br /&gt;
&lt;br /&gt;
For more details see [https://gist.github.com/yellows8/16bb56343d085d2db2ab0adc5d4cef99 here].&lt;br /&gt;
| Compromise of ldn starting from OOB-memcpy, even on S2: stack infoleak (ASLR defeat), arbitrary memory read/write (which also allows handle-leak), vfunc-calls with arbitrary [[Security_Mitigations|vtable]].&lt;br /&gt;
| [[21.0.0]]&lt;br /&gt;
| [[21.0.0]]&lt;br /&gt;
| June ~13, 2025&lt;br /&gt;
| November 11, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[HID_services|hid:dbg]] AttachHdlsVirtualDevice unvalidated DeviceTypeInternal&lt;br /&gt;
| hid:dbg AttachHdlsVirtualDevice eventually passes the input from HdlsDeviceInfo into a func without any validation. The DeviceTypeInternal field is used as the index for loading a ptr from a global array. The only validation occurs when the loaded ptr is NULL - this is just for initializing the ptr in the array when it&#039;s not already set.&lt;br /&gt;
&lt;br /&gt;
Since the highest DeviceTypeInternal is value 30, using &amp;gt;=31 will load an OOB ptr. This ptr is written to state, and also immediately passed to a called func. As long as ptr is valid it should be fine with this func.&lt;br /&gt;
&lt;br /&gt;
This functionality is also used eventually by ApplyHdlsNpadAssignmentState and ApplyHdlsStateList.&lt;br /&gt;
&lt;br /&gt;
It&#039;s unknown whether there&#039;s a way to exploit this. Also note that hid:dbg is not normally accessible to retail titles.&lt;br /&gt;
&lt;br /&gt;
[21.0.0+] Arrayindex=0 is now used when the input is invalid.&lt;br /&gt;
| Likely useless, even if reachable?&lt;br /&gt;
| [[21.0.0]]&lt;br /&gt;
| [[21.0.0]]&lt;br /&gt;
| June 3, 2024 (possibly eariler(?))&lt;br /&gt;
| November 14, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] BSA allowed ATT MTU is too large&lt;br /&gt;
| GATT-handler stack buffer overflows with a large input size are only possible if the payload_size (MTU) field in state is large enough. gatt_client_handle_server_rsp/gatt_server_handle_client_req will drop messages where the size is &amp;gt;= payload_size (though unless the request opcode matches certain values it will also send an error-response for invalid-PDU). Both of these handle updating this field when needed, however that&#039;s handled properly.&lt;br /&gt;
&lt;br /&gt;
With bluetooth-classic via L2CAP, a hard-coded MTU of 0x205 is sent in the configure request. However the code handling received configure requests will set payload_size to 0x2A0 if no MTU is specified, or the input MTU if it&#039;s within range 0x30..0x2A0. Hence, sending data large enough for buffer overflows requires bluetooth-classic via L2CAP + manually sending large ACL data.&lt;br /&gt;
&lt;br /&gt;
[15.0.0+] gatt_l2cif_config_ind_cback which handles the received configure-requests with bluetooth-classic mentioned above, now uses MTU range 0x30..0x205 with the default MTU being 0x205. It is therefore no longer possible to trigger the previously mentioned buffer-overflows with bluetooth-classic.&lt;br /&gt;
| Stack buffer overflows in bluetooth-sysmodule due to the allowed MTU for ATT being larger than the stack data.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| November 2021?&lt;br /&gt;
| November 26, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] BSA gatt_process_prep_write_rsp stack buffer overflow&lt;br /&gt;
| BSA gatt_process_prep_write_rsp memcpys to stack without size validation (the input len param which is subtracted to determine the copy-size is also unvalidated). Triggering this is only possible if the system sent ATT_PREPARE_WRITE_REQ, and then received ATT_PREPARE_WRITE_RSP with a large size.&lt;br /&gt;
&lt;br /&gt;
The size used with memcpy is (u16)(insize-4), so when insize is less than 4 the copy size will be {negative value masked to u16}. This will therefore eventually crash when the stacktop is reached during memcpy.&lt;br /&gt;
&lt;br /&gt;
[15.0.0+] Paritially fixed due to corrected MTU handling (doesn&#039;t apply to negative-copysize). [21.0.0+] Fully fixed with proper size validation.&lt;br /&gt;
| Stack buffer overflow in bluetooth-sysmodule when the required ATT messages are sent/received.&lt;br /&gt;
| [[21.0.0]]&lt;br /&gt;
| [[21.0.0]]&lt;br /&gt;
| November 2021?&lt;br /&gt;
| January 19, 2026&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Internet Browser == &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| CVE-2016-4657&lt;br /&gt;
| WebKit vuln discovered around August 2016. Most notably used in the iOS 9.3.X exploit. A simple PoC can be found [https://github.com/LiveOverflow/lo_nintendoswitch/blob/master/poc1.html here]. This was later exploited by [https://twitter.com/qwertyoruiopz Qwertyoruiop] using an adjusted version of his iOS 9.3 webkit exploit (others exploited this prior to then).&lt;br /&gt;
|&lt;br /&gt;
| [[2.1.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| Original: August 2016&lt;br /&gt;
Switch: March 3rd-4th 2017&lt;br /&gt;
|&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| CVE-2017-7005&lt;br /&gt;
| WebKit type confusion.&lt;br /&gt;
|&lt;br /&gt;
| [[3.0.1]]&lt;br /&gt;
| [[3.0.1]]&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| CVE-2016-4622&lt;br /&gt;
| WebKit memory corruption bug. This bug was incorrectly re-introduced in [[4.0.0]]. See [http://www.phrack.org/papers/attacking_javascript_engines.html here] for a detailed write-up from the author.&lt;br /&gt;
|&lt;br /&gt;
| [[6.1.0]]&lt;br /&gt;
| [[6.1.0]]&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| CVE-2018-4441&lt;br /&gt;
| WebKit memory corruption bug. See [https://bugs.chromium.org/p/project-zero/issues/detail?id=1685&amp;amp;desc=2 here].&lt;br /&gt;
|&lt;br /&gt;
| [[7.0.0]]&lt;br /&gt;
| [[7.0.0]]&lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Web-applets OpenSSL broken RNG&lt;br /&gt;
| [[SPL_services|csrng]] access was added to web-applets with [12.1.0+]. Prior to that, csrng and nn::os::GenerateRandomBytes were not used (besides sdk heap code).&lt;br /&gt;
nn::os::GetSystemTick is used to seed the OpenSSL RNG, among other data. Hence, it&#039;s probably (?) possible to bruteforce the RNG initial state, allowing predicting RNG output.&lt;br /&gt;
&lt;br /&gt;
The RNG code is wkcRandomNumbersPeer (peer_wkc nro), with the initialization code using GetSystemTick located in the func immediately before wkcGetTickCountPeer. The former is called from wkcOsslRandFilefReadPeer. wkcOsslRandFilefReadPeer is called for seeding the OpenSSL RNG.&lt;br /&gt;
&lt;br /&gt;
With [12.1.0+], wkcRandomNumberPeer/wkcRandomNumbersPeer wrap nn::os::GenerateRandomBytes. wkcCryptographicallyRandomValuesPeer was added which wraps nn::crypto::GenerateCryptographicallyRandomBytes. wkcOsslRandFilefReadPeer now calls nn::crypto::GenerateCryptographicallyRandomBytes instead of wkcRandomNumbersPeer.&lt;br /&gt;
| Breaking web-applets OpenSSL RNG -&amp;gt; potentially predict RNG data (keys(?)) during TLS comms.&lt;br /&gt;
| [[12.1.0]]&lt;br /&gt;
| [[12.1.0]]&lt;br /&gt;
| January 28, 2022&lt;br /&gt;
| October 8, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]], likely (?) others&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Whitelist ===&lt;br /&gt;
This section documents [[Internet_Browser|WebApplet]] whitelist issues in applications. These can be used to load your own browser content over plain HTTP, which then for example could be used for web-applet exploitation.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Application&lt;br /&gt;
!  Description&lt;br /&gt;
!  Fixed with app version&lt;br /&gt;
!  Newest app version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Sonic Mania&lt;br /&gt;
| Originally this game launched web-applet with a plain-http URL for displaying the manual, this was later changed to https. Originally the whitelist only had 1 entry for a http URL, this was later replaced with various https-only URLs.&lt;br /&gt;
| 1.04, unknown if fixed with an earlier update&lt;br /&gt;
| 1.04&lt;br /&gt;
| January (?) 2022&lt;br /&gt;
| February 23, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NintendoSDK ==&lt;br /&gt;
This section documents vulnerabilities for NSOs in NintendoSDK.&lt;br /&gt;
&lt;br /&gt;
=== nnSdk ===&lt;br /&gt;
This section documents vulnerabilities for nnSdk (sdknso).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in SDK [[System_Versions|version]]&lt;br /&gt;
!  Last SDK version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| [[HID_services|hidbus]] GetJoyPollingReceivedData buffer overflow&lt;br /&gt;
| hidbus GetJoyPollingReceivedData doesn&#039;t validate the u8 size used for memcpy, when copying the data to the output JoyPollingReceivedData. With 11.x, the size is now clamped to a maximum of 0x2C (regardless of polling-mode). Note that 0x2C is the data-size for JoyButtonOnlyPollingDataAccessor, the other polling-modes have a smaller size.&lt;br /&gt;
&lt;br /&gt;
The hid-sysmodule code which writes data here does handle it properly: size is clamped to a max size, and the data-read uses a fixed-size anyway (hence there&#039;s no way to trigger this sdknso vuln with the hid-sysmodule tmem writing code).&lt;br /&gt;
&lt;br /&gt;
This could only be exploited if one directly writes to the tmem when one has previously compromised hid-sysmodule, without using the normal tmem-writing func for this.&lt;br /&gt;
&lt;br /&gt;
There are only a few [[HID_services#ExternalDevices|apps]] which use hidbus.&lt;br /&gt;
| Triggering a buffer overflow in an application which uses hidbus GetJoyPollingReceivedData, from a previously compromised hid-sysmodule.&lt;br /&gt;
| 11.x.0&lt;br /&gt;
| 11.4.0&lt;br /&gt;
| March 2020&lt;br /&gt;
| December 3, 2020&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Profile_Selector|Profile Selector]] uninitialized input data&lt;br /&gt;
| Originally unused regions of [[Profile_Selector]] UiSettings/UserSelectionSettings were not cleared prior to being sent to the applet. With 1.x.x these are now properly memset().&lt;br /&gt;
| Stack infoleak from user-process, sent to the applet.&lt;br /&gt;
| 1.x.x&lt;br /&gt;
| 11.4.0&lt;br /&gt;
| November-December 2019&lt;br /&gt;
| December 31, 2020&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== NEX ===&lt;br /&gt;
This section documents client-side vulnerabilities for [https://github.com/Kinnay/NintendoClients/wiki/NEX-Overview-(Game-Servers) NEX].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in version&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Buffer overflow in StringConversion::T2Char8&lt;br /&gt;
| StringConversion::T2Char8 is used to convert IP addresses from a platform-specific encoding to UTF-8. On the 3DS and Switch, the implementation is simply a strcpy. By sending a long IP address string, a buffer overflow can be triggered on the stack. The vulnerability can be triggered through the NAT traversal protocol. A blog post about this vulnerable can be found [https://reversing.live/hacking-hundreds-of-wii-us-at-once.html here].&lt;br /&gt;
| Stack overflow in any game that uses NEX for matchmaking&lt;br /&gt;
| Fixed server-side&lt;br /&gt;
| December, 2022&lt;br /&gt;
| May, 2024&lt;br /&gt;
| [https://github.com/kinnay Yannik]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Pia ===&lt;br /&gt;
This section documents vulnerabilities for [https://github.com/Kinnay/NintendoClients/wiki/Pia-Overview Pia].&lt;br /&gt;
&lt;br /&gt;
In v5.11.3 (exact starting version unknown) the fixes aren&#039;t present for the below vulns which were fixed in v5.9.3, while in v5.18.98 these are present (exact starting version unknown). This probably indicates that the vuln fixes were backported from a newer Pia version to v5.9.3.&lt;br /&gt;
&lt;br /&gt;
The Pia packet handlers are only active when the game is using multiplayer. LanProtocol is only active in the games which are actively using the LAN-mode option (not Ldn) - only certain games support LAN-mode. The LanProtocol Pia packet handler can be reached while in a lobby or searching for one.&lt;br /&gt;
&lt;br /&gt;
Most Pia packets require an active StationProtocol connection to be active with {InetAddr which the packet was received from}, otherwise the packet is filtered out. The only protocols which don&#039;t use filtering are the following: NatTraversalProtocol, LanProtocol, StationProtocol, LocalProtocol.&lt;br /&gt;
&lt;br /&gt;
Note that broadcast IP-dest Pia packets are accepted - this can be used to target every device on the network which is using Pia (which is really only useful with {above protocols} due to the filtering mentioned above, unless one also handles StationProtocol).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in Pia version&lt;br /&gt;
!  Last Pia version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| nn::pia::session::RelayRouteManageJob::UpdateConnectionReport buffer overflow&lt;br /&gt;
| nn::pia::session::RelayRouteManageJob::UpdateConnectionReport() checks that the input size is at least {value}, but there&#039;s no max size check. This is used to memcpy from the input to elsewhere - hence buf-overflow if size is too large. The dst buffer is allocated on the pead heap - this buffer is probably small.&lt;br /&gt;
Note that there&#039;s various requirements before it would actually reach the memcpy, such as &amp;lt;code&amp;gt;&amp;lt;nn::pia::session::Mesh::IsHost() const&amp;gt;&amp;lt;/code&amp;gt; must return true.&lt;br /&gt;
&lt;br /&gt;
This is called from nn::pia::session::MeshProtocol::ParseConnectionReport().&lt;br /&gt;
&lt;br /&gt;
ParseConnectionReport uses a state ptr for object nn::pia::session::RelayRouteManageJob, it will return if not set. nn::pia::session::Mesh::Initialize handles setup for this, depending on an input field from nn::pia::session::Mesh::Setting. These settings originate from &amp;lt;code&amp;gt;&amp;lt;nn::pia::session::Session::CreateInstance(nn::pia::session::Session::Setting const&amp;amp;)&amp;gt;&amp;lt;/code&amp;gt;, which is called by user-code with the needed settings.&lt;br /&gt;
ParseConnectionReport is therefore only usable if the game explicitly enables the Relay functionality.&lt;br /&gt;
&lt;br /&gt;
In fixed versions immediately after the StationIndex validation it now does: &amp;lt;code&amp;gt;if(statefield+0x10&amp;lt;input_size) return;&amp;lt;/code&amp;gt;&lt;br /&gt;
| Heap buffer overflow triggered by a Pia MeshProtocol message sent to a host device.&lt;br /&gt;
| v5.9.3, see above.&lt;br /&gt;
| v5.9.1/v5.9.2/v5.9.3&lt;br /&gt;
| November 11, 2022&lt;br /&gt;
| November 15, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| nn::pia::lan::LanProtocol::ParseSessionMessage buffer overflow&lt;br /&gt;
| nn::pia::lan::LanProtocol::ParseSessionMessage() calls nn::pia::lan::LanSessionMessage::Deserialize() to deserialize the message payload data buffer into the LanSessionMessage object on stack. LanSessionMessage::Deserialize (among other things) memcpys data from the input buffer to the object, using an u32 from the input buffer - there is no size validation in Deserialize itself.&lt;br /&gt;
There is a size check immediately after calling Deserialize() to verify &amp;lt;code&amp;gt;payloadsize=={u32val}+{constant}&amp;lt;/code&amp;gt;, returning on fail - but this doesn&#039;t matter for too-large-size.&lt;br /&gt;
&lt;br /&gt;
In fixed versions Deserialize now does bounds checking, both for the minimum message size and clamping the memcpy size to a constant. An error is thrown if the clamped memcpy size is larger than the message size. The caller now checks the ret properly, previously it was ignored.&lt;br /&gt;
&lt;br /&gt;
Following the size check in ParseSessionMessage() it calls &amp;lt;code&amp;gt;&amp;lt;nn::pia::session::Mesh::IsProcessingLeaveMesh() const&amp;gt;&amp;lt;/code&amp;gt;, returning if ret is false.&lt;br /&gt;
&lt;br /&gt;
Then it calls nn::pia::lan::LanProtocol::ReceivedFragmentData::Receive(), with the memcpy&#039;d buffer/size from the above LanSessionMessage, and other fields from LanSessionMessage. This eventually memcpys the input buffer to object+{offset}+{chunksize_field}*inputu8, there is no validation for size or inputu8 (except for the above size check). Hence, if the u8 is large enough, this would result in a heap buffer overflow.&lt;br /&gt;
&lt;br /&gt;
In fixed versions ReceivedFragmentData::Receive added a bunch of validation before the memcpy.&lt;br /&gt;
| Stack/heap buffer overflow triggered by a Pia LanProtocol message.&lt;br /&gt;
| v5.9.3, see above.&lt;br /&gt;
| v5.9.1/v5.9.2/v5.9.3&lt;br /&gt;
| November 14, 2022&lt;br /&gt;
| November 15, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| nn::pia::session::SessionProtocol::ParseLeaveMeshInvitation buffer overflow&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nn::pia::session::SessionProtocol::ParseLeaveMeshInvitation(nn::pia::transport::ReceivedMessageAccessor const&amp;amp;)&amp;gt;&amp;lt;/code&amp;gt; This immediately returns if *(ReceivedMessageAccessor+16) is 0. Then the input data is deserialized. The input u64 array is deserialized to stack, the u8 arraycount field from input is not validated.&lt;br /&gt;
&lt;br /&gt;
Hence, stack buffer overflow. Note that there&#039;s similar loop code in nearby funcs, which do validate the count properly.&lt;br /&gt;
&lt;br /&gt;
In fixed versions the arraycount field is now validated.&lt;br /&gt;
&lt;br /&gt;
SessionProtocol uses ReliableSlidingWindow MessageHeader, with a maximum message size of 0x100. The allocated size used for the above u64 array is also 0x100-bytes. Hence, when triggering a buf overflow the data after the buffer is uncontrolled data from the SessionProtocol object.&lt;br /&gt;
| Stack buffer overflow triggered by a Pia SessionProtocol message.&lt;br /&gt;
| v5.9.3, see above.&lt;br /&gt;
| v5.9.1/v5.9.2/v5.9.3&lt;br /&gt;
| November 14, 2022&lt;br /&gt;
| November 15, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Optional Pia packet encryption&lt;br /&gt;
| Pia packet encryption is optional. If the encryption flag is disabled, the packet handler will accept it and skip crypto.&lt;br /&gt;
In fixed versions immediately after grabbing a packet, it now checks the crypto flag. If it&#039;s plaintext the packet is dropped.&lt;br /&gt;
&lt;br /&gt;
This can be used to send a plaintext Pia packet without needing to handle encryption, especially useful if the session-key can&#039;t be obtained (online-play matchmaking). This could be combined with other vulns if wanted.&lt;br /&gt;
| Sending a plaintext Pia packet without needing to handle encryption.&lt;br /&gt;
| v5.9.3, see above.&lt;br /&gt;
| v5.9.3 (and later versions)&lt;br /&gt;
| &lt;br /&gt;
| November 19, 2022&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| nn::pia::session::{JoinMeshJob/ProcessUpdateMeshJob}::SetStationDataList OOB read/write/vfunc-call&lt;br /&gt;
| &amp;lt;code&amp;gt;nn::pia::session::JoinMeshJob::SetStationDataList&amp;lt;/code&amp;gt;is called by &amp;lt;code&amp;gt;nn::pia::session::MeshProtocol::ParseJoinResponse(nn::pia::transport::ReceivedMessageAccessor const&amp;amp;)&amp;gt;&amp;lt;/code&amp;gt; with the ReceivedMessageAccessor buffer.&lt;br /&gt;
SetStationDataList will update state and immediately return if the join was denied. It will also validate the num_mesh_stations field against state. ParseJoinResponse also essentially verifies that the message was received from the host device.&lt;br /&gt;
&lt;br /&gt;
The input buffer size is ignored.&lt;br /&gt;
&lt;br /&gt;
The num_fragments field must be value 1 or &amp;lt;=3 otherwise it will return, there&#039;s two seperate code blocks handling these.&lt;br /&gt;
&lt;br /&gt;
Other than the checks at the start, there&#039;s no validation for the index fields. So large enough values could result in OOB-reads.&lt;br /&gt;
&lt;br /&gt;
When handling multiple fragments, it will loop through the stationinfo list. There is no validation for the u8 count field or the baseindex field. It calls a vfunc from obj baseptr+index*{entrysize} with data from the buffer, where index starts with the above baseindex field. Afterwards, an u8 is copied into an u32 array (with certain versions an u16 is deserialized into an u16 array).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nn::pia::session::ProcessUpdateMeshJob::UpdateStationDataList&amp;lt;/code&amp;gt; is (eventually) called from &amp;lt;code&amp;gt;nn::pia::session::MeshProtocol::ParseUpdateMesh&amp;lt;/code&amp;gt;, which has similar issues to the above.&lt;br /&gt;
&lt;br /&gt;
Note that ParseJoinResponse/ParseUpdateMesh essentially require the message to be received from the host device.&lt;br /&gt;
&lt;br /&gt;
With fixed versions (v5.18.98, exact version unknown) various validation was added. Additional/updated validation was added in a later version (v5.31.0, exact version unknown).&lt;br /&gt;
| OOB read/write / vfunc call where the object is selected by an OOB index, triggered by a Pia MeshProtocol message.&lt;br /&gt;
| v5.18.98 and v5.31.0 (exact versions unknown).&lt;br /&gt;
| v5.31.0&lt;br /&gt;
| November 18, 2022&lt;br /&gt;
| November 21, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Insecure encryption&lt;br /&gt;
| Originally Pia packets used AES-ECB encryption. As documented [https://github.com/Kinnay/NintendoClients/wiki/Pia-Overview here] it was later changed with v5.7.0 to AES-GCM. Each 0x10-byte block would have the same encrypted block output where the plaintext 0x10-byte data is the same.&lt;br /&gt;
The mechanism for generating the Pia SessionKey for LAN has also changed over time.&lt;br /&gt;
&lt;br /&gt;
The [https://github.com/Kinnay/NintendoClients/wiki/LAN-Protocol LAN] non-Pia-encapsulated packets were also originally sent in plaintext, however at some point it was changed to mostly encrypted.&lt;br /&gt;
| &lt;br /&gt;
| AES-GCM fix: v5.7.0&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| nn::pia::transport::UnreliableProtocol::Dispatch buffer overflow&lt;br /&gt;
| &amp;lt;code&amp;gt;nn::pia::transport::UnreliableProtocol::Dispatch&amp;lt;/code&amp;gt; memcpys data from the message into a list entry, without size validation. If the pia packet is the max size, it will only overwrite the 0xC-bytes which were written to immediately before the memcpy: the u32 size and the 8-byte StationAddress (depending on the version there can also be 4-byte padding after the size for alignment).&lt;br /&gt;
However, nn::pia::transport::UnreliableProtocol::Receive will clamp the size from the list entry to the outbuf size when doing the memcpy. So this is probably useless.&lt;br /&gt;
&lt;br /&gt;
It&#039;s unknown whether there&#039;s a version where more data could be overwritten, and whether that would be useful.&lt;br /&gt;
&lt;br /&gt;
This is fixed in v5.31.0, exact version unknown. The message is dropped if too large in Dispatch.&lt;br /&gt;
| Small buffer overflow triggered by a Pia UnreliableProtocol message.&lt;br /&gt;
| v5.31.0, exact version unknown.&lt;br /&gt;
| v5.18.98/v5.31.0&lt;br /&gt;
| November 2022&lt;br /&gt;
| November 29, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Uncleared input structs for [[LDN_services|LDN]]&lt;br /&gt;
| The Pia code using ldn CreateNetwork*/ConnectNetwork*/Scan doesn&#039;t properly memset the input data for SecurityConfig/ScanFilter (when keysize is less than 0x40 for the former). Hence, infoleak from games is sent to ldn (structs are located on stack, so stack data is leaked). This requires ldn compromise/mitm to obtain the leaked data - these are not sent over the network.&lt;br /&gt;
With v6.20.1 (exact version unknown - fix isn&#039;t present in v5.32.0), the code using Scan* now clears the input ScanFilter properly. With v6.25.1 (exact version unknown - fix isn&#039;t present in v6.23.3), the code using CreateNetwork*/ConnectNetwork* now clears the input SecurityConfig properly.&lt;br /&gt;
| Infoleak from games with LDN cmds, requires compromised sysmodule/mitm.&lt;br /&gt;
| v6.20.1 and v6.25.1, exact versions unknown.&lt;br /&gt;
| v5.32.0/v6.20.1/v6.23.3/v6.25.1&lt;br /&gt;
| &lt;br /&gt;
| December 7, 2022&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== ENL ===&lt;br /&gt;
This section documents vulnerabilities for [https://github.com/kinnay/NintendoClients/wiki/ENL-Protocol ENL].&lt;br /&gt;
A framework used by Nintendo games including Mario Kart 8 Deluxe, Splatoon 2 / 3, Mario Maker 2, and more.&lt;br /&gt;
&lt;br /&gt;
Fun fact, this library appears to re-use network code and concepts from older Nintendo titles such as Mario Kart 7 and some Wii multiplayer games.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in Enl version&lt;br /&gt;
!  Last Enl version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| enl::TransportManager::updateReceiveBuffer_() nullptr deref&lt;br /&gt;
| enl::TransportManager::updateReceiveBuffer_() is called when the ENL framework receives a PIA packet from a client, it will fully trust the ENL header which includes a &amp;quot;ContentTransporter&amp;quot; type (ID) and a length.&lt;br /&gt;
The function will try to fetch the content transporter by ID using &amp;lt;code&amp;gt;enl::TransportManager::getContentTransporter(unsigned char const &amp;amp;)&amp;lt;/code&amp;gt;, it returns NULL if there&#039;s no content transporter with the same ID&lt;br /&gt;
&lt;br /&gt;
*NOTE: The function may be inlined&lt;br /&gt;
&lt;br /&gt;
Then it will try to call a virtual method: &amp;lt;code&amp;gt;virtual size_t readyReceiveStream(enl::RamReadStream&amp;amp;, enl::Buffer*, size_t)&amp;lt;/code&amp;gt;, dereferencing the pointer to fetch the vtable ptr&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/Rambo6Glaz/c088e2ed7a12db08f6322e9f7a3c4911 Pseudocode of the function before it was fixed]&lt;br /&gt;
&lt;br /&gt;
| nullptr dereference triggered by an invalid content transporter type in the ENL header (it will crash the game/process)&lt;br /&gt;
| Unknown&lt;br /&gt;
| Depends on the game&lt;br /&gt;
| Early April 2022&lt;br /&gt;
| November 16, 2022&lt;br /&gt;
| [[User:Rambo6Glaz|Rambo6Glaz]], [https://github.com/kinnay Yannik] (massive RE help)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There&#039;s another one more interesting but it will have to wait a bit :)&lt;br /&gt;
&lt;br /&gt;
== Games ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Game&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Impact&lt;br /&gt;
!  Fixed in version&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Mario Kart World&lt;br /&gt;
| ASLR leak in application data&lt;br /&gt;
| A memory address can be leaked by changing your username to something short, and hosting a network session in LAN mode (press L + R + Left Stick on the main menu to enable this). The memory address can be found in bytes 12 - 19 of the application data that is transmitted in response to a browse request.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; there is more uninitialized data in the packet, but the memory address is probably the most interesting part. The vulnerability was fixed by clearing the application data with zeros, before filling in the information.&lt;br /&gt;
&lt;br /&gt;
[https://hackerone.com/reports/3463719 HackerOne report]&lt;br /&gt;
| A memory address can leaked (this is a requirement for many types of attacks).&lt;br /&gt;
| 1.5.0&lt;br /&gt;
| December 12, 2025&lt;br /&gt;
| February 19, 2026&lt;br /&gt;
| [https://github.com/kinnay Yannik]&lt;br /&gt;
|-&lt;br /&gt;
| Splatoon 3&lt;br /&gt;
| Predictable seed in anti-cheat system?&lt;br /&gt;
| [https://hackerone.com/reports/3042475 HackerOne report]&lt;br /&gt;
| Relatively easy way to bypass anti-cheat.&lt;br /&gt;
| ?&lt;br /&gt;
| Reported on March 17, 2025&lt;br /&gt;
| February 19, 2026&lt;br /&gt;
| hana2736&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Yannik</name></author>
	</entry>
	<entry>
		<id>https://switchbrew.org/w/index.php?title=Switch_System_Flaws&amp;diff=14305</id>
		<title>Switch System Flaws</title>
		<link rel="alternate" type="text/html" href="https://switchbrew.org/w/index.php?title=Switch_System_Flaws&amp;diff=14305"/>
		<updated>2026-02-19T14:04:43Z</updated>

		<summary type="html">&lt;p&gt;Yannik: Add information about ASLR leak in Mario Kart World, and a publicly disclosed Splatoon 3 report&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page is a list of publicly known Switch / Switch 2 (S2) flaws.&lt;br /&gt;
&lt;br /&gt;
= Hardware =&lt;br /&gt;
Flaws in this category pertain to the underlying hardware that powers the Switch.&lt;br /&gt;
&lt;br /&gt;
This includes components shared across Tegra based devices such as the [[TSEC]], the [[Security_Engine|Security Engine]], the [[GPU]] and so on.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Fixed with hardware model/revision&lt;br /&gt;
!  Newest hardware model/revision this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| GMMU DMA attack&lt;br /&gt;
| The Switch&#039;s GPU includes a separate MMU (GMMU) that is allowed to bypass the system&#039;s IOMMU (SMMU). By accessing the GPU&#039;s MMIO region and manipulating the page table entries in the GMMU, an attacker can read/write any portion of the DRAM (except memory carveouts).&lt;br /&gt;
&lt;br /&gt;
[5.0.0+] Works around this hardware flaw by using memory pool partitioning. You can no longer escalate into sysmodules with GPU DMA because all their memory is allocated using heap that&#039;s carved out.&lt;br /&gt;
&lt;br /&gt;
HAC-001-01 (Mariko/Tegra214/Tegra210b01): Fixes this by adding a new register which restricts what memory untranslated DMA requests may access. Untranslated GPU DMA may now only access the GPU carveout (physmem 0x80002000-0x80006000), which the GPU already has legitimate and exclusive access to.&lt;br /&gt;
| HAC-001-01 (Mariko/Tegra214/Tegra210b01)&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| Summer 2017&lt;br /&gt;
| December 28, 2017&lt;br /&gt;
| [[User:hexkyz|hexkyz]], [[User:SciresM|SciresM]] and [[User:qlutoo|qlutoo]]&lt;br /&gt;
|-&lt;br /&gt;
| Weak Security Engine context validation&lt;br /&gt;
| The Tegra X1 supports a &amp;quot;deep sleep&amp;quot; feature, where everything but DRAM and the PMC registers lose their content (and the SoC loses power). Upon awaking, the bootrom re-executes, restoring system state. Among these stored states is the Security Engine&#039;s saved state, which uses AES-128-CBC with a random key and all-zeroes IV. However, the bootrom doesn&#039;t perform a MAC on this data, and only validates the last block. This allows one to control most of security engine&#039;s state upon wakeup, if one has a way to modify the encrypted state buffer.&lt;br /&gt;
&lt;br /&gt;
With a way to modify the encrypted state buffer, one can thus dump keys from &amp;quot;write-only&amp;quot; keyslots, etc.&lt;br /&gt;
&lt;br /&gt;
This also bypasses the SBK protection of the bootROM: indeed, at warmboot, bootROM will always clear keyslot 0xE to prevent malicious code from saving the SBK. Moving the SBK to another keyslot in the saved context renders this protection moot.&lt;br /&gt;
&lt;br /&gt;
HAC-001-01 (Mariko/Tegra214/Tegra210b01): Fixes this by streamlining the context save process; security engine contexts are now saved to protected memory which the CPU cannot access or modify.&lt;br /&gt;
| HAC-001-01 (Mariko/Tegra214/Tegra210b01)&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| December 2017&lt;br /&gt;
| January 20, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]] and [[User:motezazer|motezazer]]&lt;br /&gt;
|-&lt;br /&gt;
| Security Engine keyslots vulnerable to partial overwrite attack&lt;br /&gt;
| &lt;br /&gt;
The Tegra X1 security engine supports writing keyslot data to the engine with syntax as follows: &lt;br /&gt;
&lt;br /&gt;
SECURITY_ENGINE-&amp;gt;AES_KEYTABLE_ADDR = (keyslot &amp;lt;&amp;lt; 4) | (dword_index_in_keyslot); &lt;br /&gt;
&lt;br /&gt;
SECURITY_ENGINE-&amp;gt;AES_KEYTABLE_DATA = readle32(key, dword_index_in_keyslot * 4); &lt;br /&gt;
&lt;br /&gt;
However, the Security Engine flushes writes to the internal key tables immediately when AES_KEYTABLE_DATA is written -- this allows one to overwrite a single dword of a key at a time, and thus brute force the contents of keyslots in time (2^32 * 8) = 2^35 instead of 2^256.&lt;br /&gt;
| None&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| Theorized Summer 2017 due to suggestive syntax, confirmed April 9, 2018&lt;br /&gt;
| April 9, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]], almost surely others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| CVE-2018-6242 (leveraged by the ShofEL2 and Fusée Gelée exploits)&lt;br /&gt;
| The USB software stack provided inside the boot instruction rom (IROM/bootROM) contains a copy operation whose length can be controlled by an attacker. By carefully constructing a USB control request, an attacker can leverage this vulnerability to copy the contents of an attacker-controlled buffer over the active execution stack, gaining control of the Boot and Power Management processor (BPMP) before any lock-outs or privilege reductions occur. This execution can then be used to exfiltrate secrets and to load arbitrary code onto the main CPU Complex (CCPLEX) &amp;quot;application processors&amp;quot; at the highest possible level of privilege (typically as the TrustZone Secure Monitor at PL3/EL3).&lt;br /&gt;
| HAC-001-01 (Mariko/Tegra214/Tegra210b01) (also fixed independently on Tegra186).&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| January 2018&lt;br /&gt;
| April 23, 2018&lt;br /&gt;
| [[User:Shuffle2|shuffle2]] and fail0verflow (originally),&amp;lt;br&amp;gt; [[User:Ktemkin|ktemkin]] and ReSwitched Team (independently),&amp;lt;br&amp;gt; [[User:Naehrwert|naehrwert]] (independently),&amp;lt;br&amp;gt; [[User:Hexkyz|hexkyz]] (independently),&amp;lt;br&amp;gt; st4rk with [[User:Shinyquagsire23|Shiny Quagsire]] and Dazzozo (independently),&amp;lt;br&amp;gt; and many others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| Poor validation of bootrom SDRAM configuration parameters leads to arbitrary writes in bootrom&lt;br /&gt;
| &lt;br /&gt;
The Tegra X1 bootrom supports saving SDRAM parameters to scratch registers, and using the saved configuration to enable DRAM during warmboot.&lt;br /&gt;
&lt;br /&gt;
The code that parses these parameters does if (params-&amp;gt;EmcBctSpareN) *params-&amp;gt;EmcBctSpareN = params-&amp;gt;EmcBctSpareNPlusOne for most N, without validating either the address or value written to it.&lt;br /&gt;
There are other arbitrary writes in this code, as well (e.g. BootromPatch parameters intended for patching MISC registers do not check a relative offset to 0x7000000, etc).&lt;br /&gt;
&lt;br /&gt;
This allows a user with access to the PMC registers (via pre-sleep bpmp execution, or otherwise) to gain arbitrary bootrom code execution.&lt;br /&gt;
&lt;br /&gt;
HAC-001-01 (Mariko/Tegra214/Tegra210b01): Fixes this by validating that the spare writes/bootrom patch before performing them.&lt;br /&gt;
| HAC-001-01 (Mariko/Tegra214/Tegra210b01)&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| 2017&lt;br /&gt;
| December 16, 2018&lt;br /&gt;
| Everyone (independently).&lt;br /&gt;
|-&lt;br /&gt;
| TSEC ROM does not clear crypto registers after signature verification&lt;br /&gt;
|&lt;br /&gt;
TSEC supports executing signed-microcode at a greater privilege level than normal payloads.&lt;br /&gt;
&lt;br /&gt;
When jumping to signed microcode, the caller is expected to load hardware crypto register $c6 = &amp;lt;signature&amp;gt;, $c7 = &amp;lt;seed (zero for all officially-signed microcode)&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
TSEC ROM then calculates the expected signature and compares it to the user-supplied one in $c6. On match, the secure payload is executed, and on failure an exception is raised.&lt;br /&gt;
&lt;br /&gt;
However, TSEC ROM fails to clear the crypto registers used to calculate the expected signature in either of the success/failure cases.&lt;br /&gt;
&lt;br /&gt;
Thus, with some way of obtaining the contents of crypto registers (e.g. ROP under some secure payload), an attacker can dump intermediary values from signature calculation.&lt;br /&gt;
&lt;br /&gt;
With enough data/trial/error, this is enough to reconstruct the signature algorithm:&lt;br /&gt;
* mac = &amp;lt;davies meyer hash of (page || address of page) for each 0x100 page in the payload&amp;gt;&lt;br /&gt;
* key = AES-ENCRYPT(hardware csecret 0x1, seed)&lt;br /&gt;
* signature = AES-ENCRYPT(key, mac)&lt;br /&gt;
&lt;br /&gt;
| None&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| Late 2018/Early 2019&lt;br /&gt;
| August 2020&lt;br /&gt;
| [[User:qlutoo|qlutoo]]/[[User:Hexkyz|hexkyz]]/[[User:Shuffle2|shuffle2]], [[User:SciresM|SciresM]]/[[User:motezazer|motezazer]] (independently).&lt;br /&gt;
|-&lt;br /&gt;
| TSEC signature validation design flaw leads to fake-signing&lt;br /&gt;
|&lt;br /&gt;
As mentioned above, when jumping to signed microcode the caller is expected to load hardware crypto register $c6 = &amp;lt;signature&amp;gt;, $c7 = &amp;lt;seed (zero for all officially-signed microcode)&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
However, TSEC ROM performs no validation on the input seed used to generate the signing key.&lt;br /&gt;
&lt;br /&gt;
This leads to the following attack:&lt;br /&gt;
* Attacker gains rop under any secure microcode payload with signature = S.&lt;br /&gt;
* Attacker uses the &amp;quot;csigenc&amp;quot; instruction to obtain K = AES-ENCRYPT(hardware csecret 0x1, S).&lt;br /&gt;
* Attacker jumps to their own microcode with $c6 = &amp;lt;signature calculated on pc using K&amp;gt;, $c7 = S&lt;br /&gt;
* TSEC ROM calculates key = AES-ENCRYPT(hardware csecret 0x1, S) = K, and the signature check passes.&lt;br /&gt;
* Attackers microcode is executed in secure mode as though it were signed by NVidia.&lt;br /&gt;
&lt;br /&gt;
Thus an attacker who has exploited *any* secure payload may use this to obtain a &amp;quot;fake signature key&amp;quot;, which can be used to sign and execute arbitrary microcode in secure mode.&lt;br /&gt;
&lt;br /&gt;
Note: this does not break the TSEC cryptosystem, as the csigenc mechanism relies on the signature of the executing microcode, and fakesigning produces different signatures from NVidia that cannot be controlled.&lt;br /&gt;
| None&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| Late 2018/Early 2019&lt;br /&gt;
| August 2020&lt;br /&gt;
| [[User:qlutoo|qlutoo]]/[[User:Hexkyz|hexkyz]]/[[User:Shuffle2|shuffle2]], [[User:SciresM|SciresM]]/[[User:motezazer|motezazer]] (independently).&lt;br /&gt;
|-&lt;br /&gt;
| ROP under TSEC secure bootrom via DMA engine stack overwrite (--xploit)&lt;br /&gt;
| TSEC DMA engine does not stop when entering TSEC secure bootrom. By pointing TSEC DMA to current stack before secure bootrom entry, stack can be controlled. &lt;br /&gt;
&lt;br /&gt;
One can then use blind ROP against the TSEC secure bootrom (which is execute only, and cannot be dumped).&lt;br /&gt;
&lt;br /&gt;
With sufficient effort, an attacker can construct a ROP chain that leads to csigcmp being executed with fully controlled arguments.&lt;br /&gt;
&lt;br /&gt;
This allows for arbitrary heavy secure mode code execution with the current signature set to an arbitrary value.&lt;br /&gt;
&lt;br /&gt;
This completely breaks the TSEC cryptosystem, by allowing one to obtain the result of csigenc with signature = &amp;lt;any desired value&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This has many uses/results, notably including dumping the &amp;quot;true&amp;quot; signature key (set signature = zeroes, perform csigenc using csecret 0x1).&lt;br /&gt;
| None&lt;br /&gt;
| TSEC for all Tegra devices&lt;br /&gt;
| Late 2018&lt;br /&gt;
| January 2021&lt;br /&gt;
| [[User:Hexkyz|hexkyz]]/[[User:SciresM|SciresM]], [[User:Vale|Vale]]/[[User:Thog|Thog]] (independently), [[User:Tatsuko|Tatsuko]] (independently), possibly others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| Boot straps are not relatched on watchdog resets (strapwn)&lt;br /&gt;
| On boot, the BOOTSELECT, RCM and RAM_CODE straps are latched from external GPIO to determine which boot medium to use and verify from in bootrom. However, APB_MISC_PP_STRAPPING_OPT_A can be overwritten with arbitrary values following bootrom. Write access to PP_STRAPPING_OPT_A would otherwise be mundane, however these straps are not relatched during a watchdog reset (despite being latched during other software resets), allowing for arbitrary straps to be selected and executed in bootrom.&lt;br /&gt;
&lt;br /&gt;
This allows setting NVPROD_UART on some hardware configurations where it would normally be unavailable (ie on Jetson Nano boards), but is otherwise mostly useless and/or useful for testing unintended boot options (such as USB Mass Storage boot) without having to move boot strap resistors.&lt;br /&gt;
| Unknown&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| May 2020&lt;br /&gt;
| April 30, 2021&lt;br /&gt;
| [[User:Shinyquagsire23|Shiny Quagsire]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Firmware =&lt;br /&gt;
Flaws in this category pertain to the firmware running on hardware devices, such as wifi/bluetooth, etc. Firmware is generally uploaded by sysmodules.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Broadpwn (CVE-2017-9417)&lt;br /&gt;
| See [https://blog.exodusintel.com/2017/07/26/broadpwn/ here] and [https://www.blackhat.com/docs/us-17/thursday/us-17-Artenstein-Broadpwn-Remotely-Compromising-Android-And-iOS-Via-A-Bug-In-Broadcoms-Wifi-Chipsets.pdf here].&lt;br /&gt;
| Code execution on the wifi controller (untested on Switch).&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| Switch: July 2022&lt;br /&gt;
| Switch: July 30, 2022&lt;br /&gt;
| Switch: [[User:Yellows8|yellows8]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Software =&lt;br /&gt;
== Bootloader ==&lt;br /&gt;
Flaws in this category pertain to any bootloader component such as the [[Package1#Package1ldr|package1ldr]], the [[Package1#Section_1|NX bootloader]] or the [[Package1#Section_0|warmboot binary]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Null-dereference in panic()&lt;br /&gt;
| The Switch&#039;s stage 1 bootloader, on panic(), clears the stack and then attempts to clear the Security Engine. However, it does so by dereferencing a pointer to the SE in .bss (initially NULL), and this pointer doesn&#039;t get initialized until partway into the bootloader&#039;s main() after several functions that might panic() are called. Thus, a panic() caused prior to SE initialization would result in the SE pointer still being NULL when dereferenced. &lt;br /&gt;
The BPMP doesn&#039;t have an active MPU and the bus won&#039;t data abort on an invalid address, so no exception will be entered: it&#039;ll end up overwriting some exception vectors with NULL before halting.&lt;br /&gt;
&lt;br /&gt;
In 3.0.0, this was fixed by moving the security engine initialization earlier in main(), before the first function that could potentially panic().&lt;br /&gt;
| Some exception vectors overwritten with NULL, before SBK/other keyslots are cleared. Probably useless for anything more interesting.&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| Early July, 2017&lt;br /&gt;
| July 30, 2017&lt;br /&gt;
| Everyone who diff&#039;d 2.3.0 and 3.0.0 Package1&lt;br /&gt;
|-&lt;br /&gt;
| FUSE_DIS_PGM not written by package1 &lt;br /&gt;
| The switch&#039;s hardware fuse driver contains a write-once bit in a register called &amp;quot;FUSE_DIS_PGM&amp;quot;, which disables burning fuses until the next reboot. While Nintendo&#039;s bootloader code for waking up from sleep writes this on all firmware, the actual package1 initial bootloader forgets to write to it on cold reboot. &lt;br /&gt;
&lt;br /&gt;
This isn&#039;t too big of a problem because another fuse is burnt on retail devices (production mode), which prevents burning *all* fuses other than ODM_RESERVED ones in hardware.&lt;br /&gt;
&lt;br /&gt;
This was fixed in 3.0.0 by writing to the register on cold boot (although the write happens in TZ instead of package1 where it should take place, possibly to obfuscate the fact that they made this mistake).&lt;br /&gt;
| Burning arbitrary ODM reserved fuses with TZ code execution, which should never be possible for non-bootloader code.&lt;br /&gt;
&lt;br /&gt;
Warning: one could irreparably brick one&#039;s console by playing with this.&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| Late summer/early fall 2017&lt;br /&gt;
| December 31, 2017&lt;br /&gt;
| [[User:SciresM|SciresM]], [[User:motezazer|motezazer]]&lt;br /&gt;
|-&lt;br /&gt;
| maconstack (TSEC firmware leaves MAC on the stack)&lt;br /&gt;
| Package1ldr loads a firmware blob into TSEC early on boot. This piece of code runs on the TSEC in Authenticated Mode and has the sole purpose of generating the per-console TSEC key (see [[Cryptosystem]]).&lt;br /&gt;
&lt;br /&gt;
As a way to mitigate attacks, the TSEC firmware blob is split into 3 stages: [[TSEC_Firmware#Boot|Boot]] which is unencrypted and unsigned, [[TSEC_Firmware#KeygenLdr|KeygenLdr]] which is unencrypted but signed and [[TSEC_Firmware#Keygen|Keygen]] which is encrypted and signed.&lt;br /&gt;
Boot loads a static pre-generated signature into the Falcon&#039;s CPU crypto registers, loads KeygenLdr into the Falcon&#039;s CODE region and jumps to it. Execution will proceed into KeygenLdr in Heavy Secure Mode if, and only if, the loaded signature matches the one Falcon calculates internally for KeygenLdr.&lt;br /&gt;
&lt;br /&gt;
Among various things, KeygenLdr will attempt to do a &amp;quot;backwards&amp;quot; security check by calculating a CMAC over Boot and comparing it with a known hash stored in the TSEC firmware&#039;s key data (a small buffer stored after Boot&#039;s code). If the hashes don&#039;t match, execution aborts.&lt;br /&gt;
&lt;br /&gt;
KeygenLdr stores the calculated Boot&#039;s CMAC in the stack, but forgets to clear it. Since the stack is located in Falcon&#039;s DATA region, loading the TSEC firmware blob and dumping the DATA region afterwards (via MMIO) will reveal the calculated hash.&lt;br /&gt;
This allows using KeygenLdr as an oracle to generate a valid CMAC for arbitrary Boot code. Replacing the CMAC in the TSEC firmware&#039;s key data region results in KeygenLdr accepting any Boot code, thus rendering this security measure useless.&lt;br /&gt;
&lt;br /&gt;
Additionally, since signed Falcon code can&#039;t be revoked without an hardware revision, an attacker can always reuse the flawed KeygenLdr code even if a fix is issued.&lt;br /&gt;
| Running TSEC firmware&#039;s KeygenLdr in a user controlled environment.&lt;br /&gt;
| None&lt;br /&gt;
| [[5.0.2]]&lt;br /&gt;
| January 2018&lt;br /&gt;
| April 29, 2018&lt;br /&gt;
| [[User:Hexkyz|hexkyz]], [[User:Rei|Reisyukaku]] (independently), probably others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| pk1ldrhax&lt;br /&gt;
| Package1ldr decrypts and verifies the keyblob inside of the current BCT in order to get the package1 key, and then uses the package1 key to decrypt package1. It then validates package1 before jumping to it by checking the PK11 magic number, and that the section sizes sum to the expected size (and are individually less than the expected size). &lt;br /&gt;
&lt;br /&gt;
However, package1ldr does not actually validate the package1 key against a fixed vector (much like kernel9loader forgot to do so on the 3ds). This would normally not matter, as keyblobs are validated -- however, with bootrom code execution one can dump SBK and forge keyblobs, and thus control the package1 key. &lt;br /&gt;
&lt;br /&gt;
Thus (&#039;&#039;&#039;in theory, but not in practice due to the size of the brute force required&#039;&#039;&#039;) one can replace the package1 key with garbage, causing package1 to decrypt into garbage, and hope that this garbage passes validation checks and that package1ldr jumping into the garbage will do something useful.&lt;br /&gt;
&lt;br /&gt;
This was fixed incidentally in [[6.2.0]], as pk1ldr does not use keyblob data to decrypt package1 any more.&lt;br /&gt;
&lt;br /&gt;
| With a large enough brute force: arbitrary package1 code execution from coldboot.&lt;br /&gt;
&lt;br /&gt;
However, a usable brute force is on the order of &amp;gt;= ~2^80, so &#039;&#039;&#039;this is almost certainly not actually usable in any meaningful context&#039;&#039;&#039;.&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| Early 2017 (as soon as plaintext package1ldr was first dumped)&lt;br /&gt;
| November 20, 2018&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Stack smash in TSEC firmware&#039;s KeygenLdr&lt;br /&gt;
| Given that we can control the [[TSEC_Firmware#Key_data|key data]] (which is not authenticated) and the [[TSEC_Firmware#Boot|Boot]] blob (see &amp;quot;maconstack&amp;quot;), as well as the fact Non-secure and Heavy Secure code share the same stack, we can use this to attack KeygenLdr. KeygenLdr uses memcpy to copy over a payload to DMEM to verify it, which can be abused to smash the stack (in DMEM) and write over the return address of said function.&lt;br /&gt;
| ROP under KeygenLdr in Heavy Secure mode.&lt;br /&gt;
| None&lt;br /&gt;
| [[8.0.1]]&lt;br /&gt;
| Early 2018&lt;br /&gt;
| May 21, 2019&lt;br /&gt;
| Everyone (independently).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== TrustZone ==&lt;br /&gt;
Flaws in this category pertain exclusively to the [[Package1#Section_2|Secure Monitor]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Non-atomic mutexes&lt;br /&gt;
| When an [[SMC]] is called, TrustZone sets a global variable to mark that an SMC is in progress, so that two SMCs using shared resources (like the security engine) do not trample on one another. On 1.0.0, this global variable was written using non-atomic writes, and thus a race condition is possible.&lt;br /&gt;
&lt;br /&gt;
However, the SMC handler enforces that all SMCs must be called from core #3, unless the top-level handler ID is 1 (SMCs internal to the kernel). Thus, the only SMCs that can be run side-by-side are [any userland smc] and smcGetRandomBytesForKernel, and this turns out to not really be abusable.&lt;br /&gt;
| Mostly useless. Maybe some oob-write into unused (and thus useless) memory if running smcGetRandomBytesForKernel and smcGetRandomBytesForUser at the same time.&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| December 2017 (Probably earlier by others)&lt;br /&gt;
| January 18, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]], probably others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| jamais vu (non-secure world access to PMC MMIO and pre-deep sleep firmware)&lt;br /&gt;
| On [[1.0.0]], one could map in the PMC registers in userland. In addition, [[AM_services|am]] ran a little-kernel based firmware on the BPMP at runtime. With code execution under am, one could modify the BPMP&#039;s little-kernel firmware to hook deep sleep entry, and modify TrustZone/Security engine state. &lt;br /&gt;
&lt;br /&gt;
This was fixed in [[2.0.0]] by making the PMC secure-world only, blacklisting the BPMP&#039;s exception vectors from being mapped, and thoroughly checking for malicious behavior on deep sleep entry.&lt;br /&gt;
| Arbitrary TrustZone code execution.&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| December, 2017&lt;br /&gt;
| January 20, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]] and [[User:motezazer|motezazer]]&lt;br /&gt;
|-&lt;br /&gt;
| Missed BPMP Exception Vector Writes&lt;br /&gt;
| Starting in [[2.0.0]], the BPMP is asleep at runtime, and is turned on by TrustZone during [[SMC|smcCpuSuspend]] in order to initiate the deep sleep process. When it does so, it is held in RESET, and TrustZone attempts to write to the BPMP exception vectors at 0x6000F200 to register EVP_RESET = lp0_entry_fw_crt0, and all other EVPs to a function that simply reboots. However, while they successfully write EVP_RESET, they miss all the other vectors, accidentally writing to the 0x6000F004-0x6000F020 region instead of the 0x6000F204-0x6000F220 region they want to write to. This results in all the exception vectors for the BPMP other than RESET being &amp;quot;undefined&amp;quot; (attacker controlled).&lt;br /&gt;
&lt;br /&gt;
With some way of causing an exception vector to be taken at the right time, this would give pre-sleep code execution (and thus arbitrary TrustZone code execution, via the security engine flaw). However, none of the abort vectors are really triggerable, and interrupts are disabled for the BPMP when it is taken out of reset. Thus, this is useless in practice.&lt;br /&gt;
&lt;br /&gt;
This was fixed in [[4.0.0]] by writing to the correct registers.&lt;br /&gt;
| Theoretically: Arbitrary TrustZone code execution. In practice: Useless.&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| January, 2018&lt;br /&gt;
| February 23, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]] and [[User:motezazer|motezazer]], [[User:Naehrwert|naehrwert]], [[User:Hexkyz|hexkyz]], probably others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| TSEC has access to the secure kernel carveout &lt;br /&gt;
| TrustZone is responsible for managing security carveouts to prevent DMA controllers from accessing the carveout which contains the kernel, sysmodules, and other critical operating system data.&lt;br /&gt;
&lt;br /&gt;
Until [[8.0.0]], the list of devices that could access the carveout included the TSEC. However, the TSEC can bypass the SMMU when in authenticated mode by writing to a certain register. Thus, pwning nvservices would allow one to take over the TSEC, and use it to write to normally protected mmio/memory.&lt;br /&gt;
&lt;br /&gt;
In [[8.0.0]], this was fixed by removing TSEC access, and adding TSECB access (TSECB cannot bypass the SMMU).&lt;br /&gt;
| With access to the TSEC mmio (nvservices ROP) and code execution in TSEC Heavy Secure mode, kernel code execution, probably.&lt;br /&gt;
| [[8.0.0]]&lt;br /&gt;
| [[8.0.0]]&lt;br /&gt;
| 2017 (when TrustZone code plaintext was first obtained).&lt;br /&gt;
| April 15, 2019&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| deja vu (insufficient system state validation on suspend leads to pre-sleep BPMP code execution)&lt;br /&gt;
| Jamais Vu was fixed in [[2.0.0]] by making the PMC secure-world only, blacklisting the BPMP&#039;s exception vectors from being mapped, and thoroughly checking for malicious behavior on deep sleep entry, since gaining pre-sleep code execution on the BPMP compromises the system.&lt;br /&gt;
&lt;br /&gt;
However, the state validation performed by Nintendo&#039;s Secure Monitor was insufficient to prevent pre-sleep execution from being obtained.&lt;br /&gt;
&lt;br /&gt;
Prior to [[6.0.0]], one could use a DMA controller that had access to IRAM and was not held in reset (there were multiple) to race TrustZone&#039;s writes to the BPMP firmware in IRAM, and thus overwrite Nintendo&#039;s firmware with an attacker&#039;s to gain pre-sleep code execution.&lt;br /&gt;
&lt;br /&gt;
[[6.0.0]] addressed this by performing TrustZone state MAC writes and locking PMC scratch *before* turning on the BPMP, fixing the original Jamais Vu exploit entirely. In addition, the BPMP firmware in TrustZone&#039;s .rodata is now memcmp&#039;d to the actual data after it is written to IRAM. This mitigates race attacks that modify the firmware.&lt;br /&gt;
&lt;br /&gt;
However, Nintendo both forgot to validate the BPMP exception vectors after writing them, and forgot to hold in reset a DMA controller that can write to the BPMP&#039;s exception vectors.&lt;br /&gt;
&lt;br /&gt;
AHB-DMA is not blacklisted by kernel mapping whitelist (Nintendo probably forgot it, because the TX1 TRM does not really document that it&#039;s present, although the MMIO works as documented in older (Tegra 3 and before) TRMs).&lt;br /&gt;
&lt;br /&gt;
Thus, with kernel code execution (or some other way of accessing AHB-DMA, e.g. nspwn on &amp;lt;= 4.1.0, TSEC hax, or other arbitrary mmio access flaws), one can DMA to the BPMP&#039;s exception vectors as they are written, causing TrustZone to start the BPMP executing an attacker&#039;s firmware at a different location than TrustZone intends/validates.&lt;br /&gt;
&lt;br /&gt;
This was fixed in [[8.0.0]] by blocking AHB-DMA arbitration and verifying it is held in reset during suspend, and thus there are no more devices that can write to the relevant MMIO at the right time.&lt;br /&gt;
&lt;br /&gt;
| Arbitrary TrustZone/BootROM code execution, by using either the original Jamais Vu flaw (prior to [[6.0.0]] or a warmboot bootrom exploit (any firmware where pre-sleep execution can be gained).&lt;br /&gt;
| [[8.0.0]]&lt;br /&gt;
| [[8.0.0]]&lt;br /&gt;
| December 2017&lt;br /&gt;
| April 15, 2019&lt;br /&gt;
| [[User:SciresM|SciresM]], [[User:motezazer|motezazer]] and ktemkin,  [[User:Naehrwert|naehrwert]] (independently), almost certainly others (independently)&lt;br /&gt;
|-&lt;br /&gt;
| TrustZone allows using imported RSA exponents with arbitrary modulus&lt;br /&gt;
| TrustZone supports &amp;quot;importing&amp;quot; RSA private exponents for use by userland -- these are stored encrypted with TrustZone only keydata in NAND, and decrypted only to TZRAM. This prevents a console that has compromised userland from learning the private exponents of these keys and doing calculations with them offline. In practice, this is used for FS (gamecard communications), ES (drm), and SSL (console client cert communications).&lt;br /&gt;
&lt;br /&gt;
However, the actual SMC API only imports the RSA exponent, and not the modulus, which is passed separately by userland in each call. There is no validation done on the modulus passed in -- this means that userland can pass in any message and modulus it chooses, and obtain the result of (message ^ private exponent) % modulus back from the secure monitor.&lt;br /&gt;
&lt;br /&gt;
By choosing a prime number modulus P such that P has &amp;quot;smooth&amp;quot; order (totient(P) == P-1 is divisible only by &amp;quot;small&amp;quot; primes), one can efficiently use the [[wikipedia:Pohlig-Hellman algorithm|Pohlig-Hellman algorithm]] to calculate the discrete logarithm of such a result directly, and thus obtain the private exponent.&lt;br /&gt;
&lt;br /&gt;
This is mostly useless in practice, given the general availability of other exploits to obtain these decrypted exponents.&lt;br /&gt;
&lt;br /&gt;
This was fixed in 10.0.0 by importing the modulus in addition to the exponent for the ES device key and ES client cert key. For backwards compatibility reasons the SSL key and Lotus key still only import the exponent.&lt;br /&gt;
&lt;br /&gt;
StorageExpMod also now validates that the exponentiation of &amp;quot;DDDDD...&amp;quot; about the provided modulus by the imported exponent and then the fixed public exponent returns &amp;quot;DDDDD...&amp;quot;, and returns invalid argument if validation fails.&lt;br /&gt;
| With userland privileges sufficient to use an imported RSA key: obtaining that RSA key&#039;s private exponent.&lt;br /&gt;
| [[10.0.0]]&lt;br /&gt;
| [[10.0.0]]&lt;br /&gt;
| August 14, 2019&lt;br /&gt;
| August 14, 2019&lt;br /&gt;
| [[User:SciresM|SciresM]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Kernel ==&lt;br /&gt;
Flaws in this category pertain exclusively to the [[Package2#Section_0|HorizonOS Kernel]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Syscall Infoleaks&lt;br /&gt;
| Many syscalls leaked kernel pointers on sad paths (for example svcSetHeapSize and svcQueryMemory), until they landed a bunch of fixes in 2.0.0.&lt;br /&gt;
| Nothing really.&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| &lt;br /&gt;
| 2017&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| svcWaitSynchronization/svcReplyAndReceive bad cleanup on error&lt;br /&gt;
| If there is a page fault when fetching handles from the userspace array, it cleans up by dereferencing all objects despite having only loaded first N. Allows the attacker to make arbitrary decrefs on any kernel synchronization object, and thus can be used to get UAF. Haven&#039;t actually been tried on real HW though, but should work (tm).&lt;br /&gt;
| Kernel code execution&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| &lt;br /&gt;
| April 24, 2017&lt;br /&gt;
| [[User:qlutoo|qlutoo]]&lt;br /&gt;
|-&lt;br /&gt;
| Bad irq_id check in CreateInterruptEvent&lt;br /&gt;
| CreateInterruptEvent syscall is designed to work only for irq_id &amp;gt;= 32. All irq_ids &amp;lt; 32 are &amp;quot;per-core&amp;quot; and reserved for kernel use (watchdog/scheduling/core communications).&lt;br /&gt;
On 1.0.0 you could supply irq_id &amp;lt; 32 and it would write outside the SharedIrqs table.&lt;br /&gt;
| You can register irq&#039;s in the Core3Irqs table, and thus register per-core irqs for core3, that are normally reserved for kernel. Useless.&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| October 2017&lt;br /&gt;
| October 17, 2017&lt;br /&gt;
| [[User:qlutoo|qlutoo]]&lt;br /&gt;
|-&lt;br /&gt;
| Kernel .text mapped executable in usermode&lt;br /&gt;
| Prior to [[3.0.2]] the kernel .text was [[Memory_layout|mapped]] in usermode as executable. This can be used for usermode ROP for bypassing ASLR, but SVCs/IPC are not usable by running kernel .text in usermode.&lt;br /&gt;
| Executing kernel .text in usermode&lt;br /&gt;
| [[3.0.2]]&lt;br /&gt;
| [[3.0.2]]&lt;br /&gt;
| &lt;br /&gt;
| December 28, 2017 (34c3)&lt;br /&gt;
| [[User:qlutoo|qlutoo]]&lt;br /&gt;
|-&lt;br /&gt;
| Memory Controller not properly secured&lt;br /&gt;
| The Switch OS originally had the memory controller not set to be accessible only by the secure-world, which was problematic because insecure access can compromise the kernel.&lt;br /&gt;
&lt;br /&gt;
This was fixed partially in [[2.0.0]] by blacklisting the memory controller from being mapped by user-processes, and was fixed entirely in [[4.0.0]] by making the memory controller TZ-only and making all kernel accesses go through [[SMC|smcReadWriteRegister]].&lt;br /&gt;
| With some way to access the memory controller MMIO, arbitrary kernel code execution.&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| January 2018&lt;br /&gt;
| January 2018&lt;br /&gt;
| [[User:SciresM|SciresM]], [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Potential [[SVC|svcWaitForAddress]] thread use-after-free&lt;br /&gt;
| Between [[4.0.0]], where svcWaitForAddress was introduced, and [[7.0.0]], there was a second intrusive rbtree node in KThread for the WaitForAddress tree (the key being (address, priority), sorted lexicographically). Unlike the WaitProcessWideKeyAtomic tree, the kernel forgot to reinsert the WaitForAddress node when the thread&#039;s priority changed (priority inheritance and/or SetPriority), breaking the rbtree invariants; and since the kernel walks through the entire tree to remove intrusive nodes, you could cause threads to stay in the tree even after their deletion.&lt;br /&gt;
&lt;br /&gt;
[[7.0.0]] fixed the issue by using the same intrusive node for both trees. The thread/node knows which tree it is in, and the latter is correctly updated when thread priority changes.&lt;br /&gt;
| It unluckily didn&#039;t look exploitable&lt;br /&gt;
| [[7.0.0]]&lt;br /&gt;
| [[7.0.0]]&lt;br /&gt;
| July 2018&lt;br /&gt;
| February 2019&lt;br /&gt;
| [[User:TuxSH|TuxSH]]&lt;br /&gt;
|-&lt;br /&gt;
| Kernel RWX identity mapping never unmapped&lt;br /&gt;
| During init, the kernel binary is identity-mapped as RWX at 0x80060000; this is necessary to facilitate the transitionary period while the MMU is being enabled but mappings for e.g. KASLR are not yet determined, and also to enable smooth MMU enable transition during wake-from-sleep.&lt;br /&gt;
&lt;br /&gt;
However, the identity mapping was never unmapped, and thus the whole kernel code bin remained permanently mapped as RWX for all kernel threads (any thread which does not have an owner process and thus uses the KSupervisorPageTable TTBR0).&lt;br /&gt;
&lt;br /&gt;
Thus, any theoretical exploit which would give kernel memory corruption or ROP under a kernel thread would allow making use of this mapping to modify kernel text + bypass KASLR.&lt;br /&gt;
&lt;br /&gt;
This was fixed in [[16.0.0]] by unmapping the identity-mapping during init, and re identity-mapping only the very first page of kernel .text as R-X (for use by wake-from-sleep), which fixes the shellcode problem and mostly fixes the ROP problem, since this page mostly lacks interesting gadgets.&lt;br /&gt;
| In theory, with another exploitable kernel memory corruption (or ROP under kernel thread) bug: bypassing KASLR + modifying kernel .text. &lt;br /&gt;
&lt;br /&gt;
However, no such bugs are known.&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| Summer 2018&lt;br /&gt;
| February 2023&lt;br /&gt;
| Everyone&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== BootImagePackage System Modules ==&lt;br /&gt;
Flaws in this category pertain to any of the [[Package2#Section_1|built-in system modules]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Service access control bypass (sm:h, smhax, probably other names)&lt;br /&gt;
| Prior to [[3.0.1]], the &#039;&#039;service manager&#039;&#039; (sm) built-in system module treats a user as though it has full permissions if the user creates a new &amp;quot;sm:&amp;quot; port session but bypasses [[Services_API#Initialize|initialization]]. This is due to the other sm commands skipping the service ACL check for Pids &amp;lt;= 7 (i.e. all kernel bundled modules) and that skipping the initialization command leaves the Pid field uninitialized.&lt;br /&gt;
In [[3.0.1]], sm returns error code 0x415 if [[Services_API#Initialize|Initialize]] has not been called yet.&lt;br /&gt;
| Acquiring, registering, and unregistering arbitrary services&lt;br /&gt;
| [[3.0.1]]&lt;br /&gt;
| [[3.0.1]]&lt;br /&gt;
| May 2017&lt;br /&gt;
| August 17, 2017&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Overly permissive SPL service&lt;br /&gt;
| The concept behind the switch&#039;s [[SMC|Secure Monitor]] is that all cryptographic keydata is located in userspace, but stored as &amp;quot;access keys&amp;quot; encrypted with &amp;quot;keks&amp;quot; that never leave TrustZone. The [[SPL services|spl]] (&amp;quot;security processor liaison&amp;quot;?) service serves as an interface between the rest of the system and the secure monitor. Prior to [[4.0.0]], spl exposed only a single service &amp;quot;spl:&amp;quot;, which provided all TrustZone wrapper functions to all sysmodules with access to it. Thus anyone with access to the spl: service (via smhax or by pwning a sysmodule with access) could do crypto with any access keys they knew. &lt;br /&gt;
&lt;br /&gt;
This was fixed in [[4.0.0]] by splitting spl: into spl:, spl:mig, spl:ssl, spl:es, and spl:fs.&lt;br /&gt;
| Arbitrary spl: crypto with any access keys one knows. For example, one could use the SSL module&#039;s access keys to decrypt their console&#039;s SSL certificate private key without having to pwn the SSL sysmodule.&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| Summer 2017 (after smhax was discovered).&lt;br /&gt;
| December 23, 2017&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Single session services not really single session&lt;br /&gt;
| Several &amp;quot;critical&amp;quot; services (like fsp-ldr, fsp-pr, sm:m, etc) are meant to only ever hold a single session with a specific sysmodule. However, when a sysmodule dies, all its service session handles are released -- and thus killing the holder of a single session handle would allow one (via sm:hax etc) to get access to that service. &lt;br /&gt;
&lt;br /&gt;
This was fixed in [[4.0.0]] by adding a semaphore to these critical single-session services, so that even if one gets access to them an error code will be returned when attempting to use any of their commands.&lt;br /&gt;
| With some way to access these services and kill their session holders (like expLDR): dumping sysmodule code, arbitrary service access, elevated filesystem permissions, etc.&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| May/June 2017 (basically immediately after smhax was discovered)&lt;br /&gt;
| December 30, 2017&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| nspwn&lt;br /&gt;
| fsp-ldr command 0 &amp;quot;MountCode&amp;quot; takes in a Content Path (retrieved from NCM by Loader), and returns an IFileSystem for the resulting ExeFS. These content paths, are normally NCAs, but MountCode also supports a number of other formats, including &amp;quot;.nsp&amp;quot; -- which is just a PFS0.&lt;br /&gt;
&lt;br /&gt;
When a path ending in &amp;quot;.nsp&amp;quot; is parsed by MountCode, the PFS0 is treated as a raw ExeFS. Because there is no NCA header, the ACID signatures are not validated -- and because there are no other signatures in a PFS0, this results in no signature checking happening at all.&lt;br /&gt;
&lt;br /&gt;
The actual .nsp handling is eventually done by {content mounting function} called by MountCode and other FS commands.&lt;br /&gt;
&lt;br /&gt;
Thus, by placing an ExeFS (NSOs + &amp;quot;main.npdm&amp;quot;) and setting one&#039;s desired title ID to &amp;quot;@Sdcard:/some_title.nsp&amp;quot; or &amp;quot;@User:/some_title.nsp&amp;quot; etc one can launch arbitrary unsigned code, with arbitrary unsigned NPDMs.&lt;br /&gt;
&lt;br /&gt;
This appears to have been fixed by only allowing .nsp when the input fstype==7 for the internal content-mounting function, returning 0x2EE202 otherwise.&lt;br /&gt;
| With access to &amp;quot;lr&amp;quot;: Arbitrary code execution with full system privileges.&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| Late 2017&lt;br /&gt;
| April 23, 2018&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Single null-byte stack overflow in Loader ContentPath parsing&lt;br /&gt;
| Previously, loader content path parsing looked like this, where path_from_lr was up to 0x300 bytes and not necessarily null-terminated:&lt;br /&gt;
&lt;br /&gt;
  char nca_path[0x300] = {0};&lt;br /&gt;
  strcat(nca_path, path_from_lr);&lt;br /&gt;
  for (int i = 0; nca_path[i]; i++) {&lt;br /&gt;
      if (nca_path[i] == &#039;\\&#039;) { nca_path[i] = &#039;/&#039;); }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Thus, a content path of the maximum length (0x300 bytes) would result in strcat writing a NULL terminator past the end of the nca_path buffer.&lt;br /&gt;
&lt;br /&gt;
This was fixed in [[6.0.0]], the new code looks like this:&lt;br /&gt;
&lt;br /&gt;
  char nca_path[0x300];&lt;br /&gt;
  strncpy(nca_path, path_from_lr, sizeof(nca_path));&lt;br /&gt;
  for (int i = 0; i  &amp;lt; sizeof(nca_path) &amp;amp;&amp;amp; nca_path[i]; i++) {&lt;br /&gt;
      if (nca_path[i] == &#039;\\&#039;) { nca_path[i] = &#039;/&#039;); }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
| With access to &amp;quot;lr&amp;quot;: single null-byte stack overflow in Loader. Maybe (but probably not) loader code execution.&lt;br /&gt;
| [[6.0.0]]&lt;br /&gt;
| [[6.0.0]]&lt;br /&gt;
| September 2, 2018&lt;br /&gt;
| September 19, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]]&lt;br /&gt;
|-&lt;br /&gt;
| System modules vulnerable to selective downgrade attacks&lt;br /&gt;
| Horizon has no mechanism for specifying the specific title version to Loader on process creation.&lt;br /&gt;
&lt;br /&gt;
Observing this, one can note that after a system update one could install a downgraded version of a specific system module (e.g. nvservices) while leaving the rest of the OS at the same version.&lt;br /&gt;
&lt;br /&gt;
Unless there was some breaking API change, this allows one to make a console vulnerable once more to an exploit in a sysmodule by downgrading it and nothing else.&lt;br /&gt;
&lt;br /&gt;
This was fixed in [[8.1.0]] by incrementing a version field in NPDM, and checking it against a hardcoded list for certain titles in Loader&#039;s process creation func.&lt;br /&gt;
| With access to content installation commands (or a vulnerable lower version to selectively install newer titles), reintroducing bugs in vulnerable system modules on newer firmware versions.&lt;br /&gt;
| [[8.1.0]]&lt;br /&gt;
| [[8.1.0]]&lt;br /&gt;
| When FIRM was first dumped in 2017.&lt;br /&gt;
| June 17, 2019&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Broken RNG for [[Loader_services|Loader]] ASLR&lt;br /&gt;
| The RNG used for generating the ASLR slide is only seeded with 32bits, with the data from [[SVC|svcGetInfo]]. Hence, one could bruteforce the seed if one has infoleaks from any programs. This can be successfully bruteforced with at least 2 sample codebin addrs from different programs (with only 1 sample a lot of invalid seeds are found), however in some cases more than 1 seed might be found.&lt;br /&gt;
&lt;br /&gt;
With [15.0.0+] Loader now uses csrng_GenerateRandomBytes for determining the ASLR slide.&lt;br /&gt;
&lt;br /&gt;
See also [https://github.com/switchbrew/loader-aslr-solver loader-aslr-solver].&lt;br /&gt;
| Breaking ASLR for all non-KIP processes, allowing predicting the main-codebin base addr for all non-KIP processes until the next reboot.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| January 30, 2022 (presumably found much earlier?)&lt;br /&gt;
| October 11, 2022&lt;br /&gt;
| Everyone&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== System Modules ==&lt;br /&gt;
Flaws in this category pertain to any non-built-in system module.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| OOB Read in NS system module (pl:utoohax, pl:utonium, maybe other names)&lt;br /&gt;
| Prior to [[3.0.0]], pl:u (Shared Font services implemented in the NS sysmodule) service commands 1,2,3 took in a signed 32-bit index and returned that index of an array but did not check that index at all. This allowed for an arbitrary read within a 34-bit range (33-bit signed) from NS .bss. In [[3.0.0]], sending out of range indexes causes error code 0x60A to be returned.&lt;br /&gt;
| Dumping full NS .text, .rodata and .data, infoleak, etc&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| April 2017&lt;br /&gt;
| June 19, 2017&lt;br /&gt;
| [[User:qlutoo|qlutoo]], ReSwitched Team (independently)&lt;br /&gt;
|-&lt;br /&gt;
| Unchecked domain ID in common IPC code&lt;br /&gt;
| Prior to [[2.0.0]], object IDs in [[IPC_Marshalling#Domain_message|domain messages]] are not bounds checked. This out-of-bounds read could be exploited to brute-force ASLR and get PC control in some services that support domain messages.&lt;br /&gt;
|&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| July 2017&lt;br /&gt;
| July 20, 2017‎&lt;br /&gt;
| [[User:hthh|hthh]]&lt;br /&gt;
|-&lt;br /&gt;
| Out-of-bounds array read for [[BCAT_Content_Container]] secret-data index&lt;br /&gt;
| The [[BCAT_Content_Container]] secret-data index is not validated at all. This is handled before the RSA-signature(?) is ever used. Since the field is an u8, a total of 0x800-bytes relative to the array start can be accessed.&lt;br /&gt;
This is not useful since the string loaded from this array is only involved with key-generation.&lt;br /&gt;
| &lt;br /&gt;
| Unknown&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| August 4, 2017&lt;br /&gt;
| August 6, 2017&lt;br /&gt;
| [[User: shinyquagsire23|Shiny Quagsire]], [[User:Yellows8|yellows8]] (independently)&lt;br /&gt;
|-&lt;br /&gt;
| expLDR (sysmodule handle table exhaustion)&lt;br /&gt;
| Most sysmodules share common template code to handle IPC control messages. The command DuplicateSession (type 5 command 2)&#039;s template code will abort() if it fails to duplicate a session&#039;s handle for the requester. Because many sysmodules have limited handle table size (smaller than the browser/other entrypoints), repeatedly requesting to duplicate one&#039;s session will cause the sysmodule to run out of handle table space and abort, causing the service to release all its handles cleanly.&lt;br /&gt;
| Sysmodule crashes.  Most usefully, crashing ldr allows access to fsp-ldr and crashing pm allows access to fsp-pr. Useless after [[4.0.0]], which mitigated a number of single-session service access issues.&lt;br /&gt;
| Unfixed&lt;br /&gt;
| [[4.1.0]]&lt;br /&gt;
| June 24, 2017&lt;br /&gt;
| March 8, 2018&lt;br /&gt;
| [[User:daeken|daeken]]&lt;br /&gt;
|-&lt;br /&gt;
| Transfer Memory leak in nvservices system module&lt;br /&gt;
| The nvservices sysmodule does not clear most of its transfer memory prior to release.&lt;br /&gt;
| The calling process can read key bits of memory, including breaking ASLR (by revealing the image base) and exposing the address of other transfer memory to set up attacks. More details here: [https://daeken.svbtle.com/nintendo-switch-nvservices-info-leak transfermeme (nvservices info leak)] by [[User:daeken|daeken]]&lt;br /&gt;
| [[6.0.0]]&lt;br /&gt;
| [[6.0.0]]&lt;br /&gt;
| June 2017&lt;br /&gt;
| October 16, 2018&lt;br /&gt;
| [[User:qlutoo|qlutoo]] and [[User:hexkyz|hexkyz]],&lt;br /&gt;
[[User:daeken|daeken]] (independently)&lt;br /&gt;
|-&lt;br /&gt;
| OOB write in audio system module&lt;br /&gt;
| Prior to [[2.0.0]], the [[Audio_services#audout:u|AppendAudioOutBuffer]] and [[Audio_services#audin:u|AppendAudioInBuffer]] IPC commands would blindly increment the appended buffers&#039; count while using said count value as an index to where the user data should be copied into. This resulted in an 0x28 bytes, user controlled, out-of-bounds memory write into the [[Audio_services|audio]] sysmodule&#039;s memory space.&lt;br /&gt;
Combined with the [[Audio_services#audout:u|GetReleasedAudioOutBuffer]] or [[Audio_services#audin:u|GetReleasedAudioInBuffer]] commands, this could also be used as an 8 byte infoleak.&lt;br /&gt;
&lt;br /&gt;
In [[2.0.0]], the commands now return error code 0x1099 if the number of unreleased buffers exceeds 0x1F.&lt;br /&gt;
| Code execution under audio sysmodule&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| &lt;br /&gt;
| November 2, 2018&lt;br /&gt;
| [[User:hexkyz|hexkyz]], probably others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| Infoleak in nvservices system module&lt;br /&gt;
| The [[NV_services|nvservices]] ioctl [[NV_services#NVMAP_IOC_ALLOC|NVMAP_IOC_ALLOC]] takes an optional argument &amp;quot;addr&amp;quot; which allows the calling process to pass a pointer to user allocated memory for backing a nvmap object. If &amp;quot;addr&amp;quot; is left as 0, nvservices uses the transfer memory region (donated by the user during initialization) instead, when allocating memory for the nvmap object.&lt;br /&gt;
By design, freeing the nvmap object by calling the ioctl [[NV_services#NVMAP_IOC_FREE|NVMAP_IOC_FREE]] returns, in its &amp;quot;refcount&amp;quot; argument, the user address previously supplied if the reference count reaches 0.&lt;br /&gt;
However, prior to [[6.2.0]], the case where the transfer memory region is used to allocate the nvmap object was not taken into account, thus resulting in [[NV_services#NVMAP_IOC_FREE|NVMAP_IOC_FREE]] leaking back an address from within the transfer memory region mapped in nvservices&#039; memory space.&lt;br /&gt;
&lt;br /&gt;
In [[6.2.0]], [[NV_services#NVMAP_IOC_FREE|NVMAP_IOC_FREE]] no longer returns the address when the transfer memory region is used instead of user supplied memory.&lt;br /&gt;
| Combined with other vulnerabilities: Defeating ASLR in nvservices sysmodule.&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| April 2017&lt;br /&gt;
| November 24, 2018&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| nvhax (memory corruption in nvservices system module)&lt;br /&gt;
| Prior to [[6.2.0]], the [[NV_services|nvservices]] ioctl [[NV_services#.2Fdev.2Fnvhost-ctrl-gpu|NVGPU_GPU_IOCTL_WAIT_FOR_PAUSE]] would take a single &amp;quot;pwarpstate&amp;quot; argument which would be interpreted by nvservices as a memory pointer for writing 2 &amp;quot;warpstate&amp;quot; structs (one for each Streaming Multiprocessor).&lt;br /&gt;
This resulted in nvservices attempting to blindly memcpy into this user supplied address and trigger a crash. However, if paired with an infoleak, this could be used to arbitrarily write 0x30 bytes anywhere in nvservices&#039; memory space.&lt;br /&gt;
Additionally, the &amp;quot;warpstate&amp;quot; struct itself was never initialized, which means nvservices would leak the 0x30 bytes from the stack. By invoking other ioctls it was also possible to partially control the stack contents and achieve a usable arbitrary memory write primitive.&lt;br /&gt;
&lt;br /&gt;
In [[6.2.0]], [[NV_services#.2Fdev.2Fnvhost-ctrl-gpu|NVGPU_GPU_IOCTL_WAIT_FOR_PAUSE]] now takes 2 inline &amp;quot;warpstate&amp;quot; structs instead of a &amp;quot;pwarpstate&amp;quot; pointer, thus effectively avoiding the bad memcpy.&lt;br /&gt;
| Code execution under nvservices sysmodule&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| April 5, 2017&lt;br /&gt;
| November 24, 2018&lt;br /&gt;
| [[User:hexkyz|hexkyz]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Applet_Manager_services#IStorage|AM IStorage]] infoleak&lt;br /&gt;
| Originally the buffer allocated by [[Applet_Manager_services#CreateStorage|CreateStorage]] using the specified input size was not cleared. With [8.0.0+] this was fixed by adding a memset() for the buffer after successful allocation.&lt;br /&gt;
&lt;br /&gt;
Hence, IStorage-&amp;gt;IStorageAccessor-&amp;gt;Read will return uninitialized memory when the Write cmd was not previously used with the specified region.&lt;br /&gt;
| Infoleak from the main [[Applet_Manager_services#IStorage|AM]] heap, allowing defeating ASLR by reading addresses from previously allocated objects.&lt;br /&gt;
| [[8.0.0]]&lt;br /&gt;
| [[8.1.0]]&lt;br /&gt;
| December 2018&lt;br /&gt;
| August 9, 2019&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[HID_services#hid:sys|hid:sys]] ButtonConfig s32 array-index not validated&lt;br /&gt;
| The input s32 array-index for [[HID_services#hid:sys|hid:sys]] ButtonConfig cmds 1255-1270 was originally not validated. Using a negative or &amp;gt;=5 index results in accessing out-of-bounds data, with an array stored on stack.&lt;br /&gt;
[10.1.0-10.2.0] Each of these cmds will now Abort if the s32 is negative or &amp;gt;=5. [11.0.0+] Now an unsigned compare is used, with 0 or an error being immediately returned when the value is invalid.&lt;br /&gt;
| hid infoleak, out-of-bounds mem-write anywhere in hid address-space relative to the stack array (with constraints on the data).&lt;br /&gt;
| [[10.1.0]]&lt;br /&gt;
| [[11.0.1]]&lt;br /&gt;
| April 18, 2020&lt;br /&gt;
| July 14, 2020&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|Bluetooth]] sdp_server.cc process_service_search() continuation request p_req validation&lt;br /&gt;
| With [5.0.0+], the following was added to the if-block prior to loading cont_offset from p_req: &amp;lt;code&amp;gt;(p_req + sizeof(cont_offset) &amp;gt; p_req_end)&amp;lt;/code&amp;gt; (which verifies that cont_offset is within message bounds).&lt;br /&gt;
| Bluetooth-sysmodule out-of-bounds read from heap, probably not useful since the read value must match a state field, etc.&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[11.0.0]]&lt;br /&gt;
| Switch: December 2020&lt;br /&gt;
| Switch: December 25, 2020&lt;br /&gt;
| Switch: [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|Bluetooth]] A-63146698&lt;br /&gt;
| [https://android.googlesource.com/platform/system/bt/+/226ea26684d4cd609a5b456d3d2cc762453c2d75 A-63146698] / CVE-2017-0785. See also [https://info.armis.com/rs/645-PDC-047/images/BlueBorne%20Technical%20White%20Paper_20171130.pdf here].&lt;br /&gt;
| Bluetooth-sysmodule stack infoleak, which allows defeating ASLR (note: not tested on hw).&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[11.0.0]]&lt;br /&gt;
| Switch: December 2020&lt;br /&gt;
| Switch: December 25, 2020&lt;br /&gt;
| Switch: [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] GetAdapterProperty/SetAdapterProperty unchecked memcpy size&lt;br /&gt;
| GetAdapterProperty copies data from stack to the output buffer using the buffer size, without checking the size (when not handling the Name type). SetAdapterProperty copies data to stack from the input buffer using the buffer size, without checking the size.&lt;br /&gt;
This requires access to the btdrv service, only hid and btm have access.&lt;br /&gt;
&lt;br /&gt;
This was fixed with [[12.0.0]] by replacing the buffer data with a fixed-size-struct.&lt;br /&gt;
| Stack infoleak with GetAdapterProperty, stack buffer overflow (and hence ROP) with SetAdapterProperty.&lt;br /&gt;
| [[12.0.0]]&lt;br /&gt;
| [[12.0.0]]&lt;br /&gt;
| July 17, 2020&lt;br /&gt;
| April 7, 2021&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] stack buffer overflow with HID DATA packets&lt;br /&gt;
| The BSA (bt-stack) func bta_hh_co_data copies data from a HID DATA packet to stack without checking the size, then sends it over Uipc. [7.0.0+] The user Uipc callback also copies the input data to stack without checking the size, then sends it to the sharedmem CircularBuffer.&lt;br /&gt;
With [12.0.2+] this was fixed in bta_hh_co_data by clamping the size to a maximum of 0x2BB. The aforementioned buffer overflow in the Uipc callback can&#039;t be triggered since at that point the size was already clamped.&lt;br /&gt;
&lt;br /&gt;
Before this bta_hh_co_data func is reached, there is no validation of the size (such as comparing against the L2CAP MTU) when Basic Mode is being used.&lt;br /&gt;
&lt;br /&gt;
Actually triggering this requires using a data-size larger than the normal L2CAP MTU. This can be done by for example, using raw HCI to send the packet from the remote bluetooth device.&lt;br /&gt;
&lt;br /&gt;
Note that when the remote device is configured as an audio device for [12.0.0+] where [[Settings_services#BluetoothDevicesSettings|BluetoothDevicesSettings]].TrustedServices was only ever set for audio since system-boot, it is not possible for the remote device to connect to the Switch for HID.&lt;br /&gt;
| ROP under [[Bluetooth_Driver_services|bluetooth]] via HID DATA packet sent by a paired HID bluetooth device. This can be triggered at any time while not in sleep-mode, when not in airplane-mode. The earliest is while the Nintendo Switch logo screen is displayed during system boot.&lt;br /&gt;
| [[12.0.2]]&lt;br /&gt;
| [[12.0.2]]&lt;br /&gt;
| July-August 2020&lt;br /&gt;
| May 11, 2021&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] WriteHidData/WriteHidData2/SetHidReport unchecked memcpy size&lt;br /&gt;
| WriteHidData/SetHidReport copies the input struct to stack, then passes it to the funcptr/vfunc call. WriteHidData2 passes the input buffer addr directly to the funcptr/vfunc call. The called func eventually copies the input data to the stack struct using the specified size without validating it.&lt;br /&gt;
This requires access to the btdrv service, only hid and btm have access.&lt;br /&gt;
&lt;br /&gt;
This was fixed with [[12.1.0]] in WriteHidData/SetHidReport by doing a fixed-size copy into another tmp struct, with the size field being clamped to a maximum of 0x2BB afterwards. This struct is then used when calling the vfunc. The vfuncs called by WriteHidData/WriteHidData2/SetHidReport were also updated to clamp the size to the required maximum value.&lt;br /&gt;
| Stack buffer overflow&lt;br /&gt;
| [[12.1.0]]&lt;br /&gt;
| [[12.1.0]]&lt;br /&gt;
| July 16, 2020&lt;br /&gt;
| July 6, 2021&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Infoleak with [[HID_services|hid:sys]] SetButtonConfigStorage{name}Deprecated&lt;br /&gt;
| These cmds pass a stack ptr for the StorageName when calling the internal func. Nothing is written to this StorageName. Hence, stack infoleak (data is copied as a NUL-terminated string), which can be later read by the GetButtonConfigStorage{name} cmds.&lt;br /&gt;
&lt;br /&gt;
This was fixed by removing the Deprecated cmds in [[13.0.0]].&lt;br /&gt;
| Infoleak of hid stack from a StorageName readable via GetButtonConfigStorage{name}, up to the NUL-terminator.&lt;br /&gt;
| [[13.0.0]]&lt;br /&gt;
| [[13.0.0]]&lt;br /&gt;
| December 11, 2020&lt;br /&gt;
| September 27, 2021&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] EventInfo infoleak&lt;br /&gt;
| The various funcs which send messages to the thread which handles writing to EventInfo, didn&#039;t clear the stack msgbuf. Hence, the various get-EventInfo cmds could return leaked stack data. This likely affected most (?) get-EventInfo cmds, besides CircularBuffer-GetHidReportEventInfo.&lt;br /&gt;
&lt;br /&gt;
This only matters for events where there&#039;s uninitialized regions of the EventInfo, such as events with variable-size data without a memset.&lt;br /&gt;
&lt;br /&gt;
This was fixed by clearing the msgbuf in a number of funcs.&lt;br /&gt;
| Bluetooth-sysmodule stack infoleak, which allows defeating ASLR&lt;br /&gt;
| [[13.0.0]]&lt;br /&gt;
| [[13.1.0]]&lt;br /&gt;
| &lt;br /&gt;
| During initial [[13.0.0|diff]]. Added to this page on: December 12, 2021&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[SSL_services|ssl]] CVE-2021-43527&lt;br /&gt;
| CVE-2021-43527, see also [https://bugs.chromium.org/p/project-zero/issues/detail?id=2237 here] and [https://googleprojectzero.blogspot.com/2021/12/this-shouldnt-have-happened.html here].&lt;br /&gt;
Using BigSig where the server cert sig is RSA-PSS results in the remote server throwing {no shared cipher} error when Switch connects. If however one creates a rootCA using BigSig (RSA-PSS), which then signs a server cert where the server key is RSA (not PSS), the vuln can be triggered (if the rootCA is trusted, via using the import service-cmd). It&#039;s unknown whether there&#039;s other ways to trigger the vuln.&lt;br /&gt;
&lt;br /&gt;
The crash occurs in VFY_Begin when using the previously overwritten data. A bitsize of &amp;lt;code&amp;gt;$((16384 + 32 + 64 + 64 + 64))&amp;lt;/code&amp;gt; is only enough to overwrite cx-&amp;gt;hashcx, to fully overwrite cx-&amp;gt;hashobj an additional 0xC-bytes (additional 96 bits) is needed.&lt;br /&gt;
Note that partial overwrite isn&#039;t an option: this is the func that initializes those fields to begin with, it just does deinit first before initializing hashcx/hashobj (prior to that these fields would be all-zero when not overwritten by the buf-overflow).&lt;br /&gt;
| Heap buffer overflow in [[SSL_services|ssl]], overwriting data including a ptr to an object which is later used to load a funcptr.&lt;br /&gt;
| [[13.2.1]]&lt;br /&gt;
| [[13.2.1]]&lt;br /&gt;
| Switch: December 1-2, 2021&lt;br /&gt;
| Switch: January 19, 2022&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] BSA gatt_process_notification stack buffer overflow&lt;br /&gt;
| gatt_process_notification is the GATT handler for processing notification/indication messages. gatt_process_notification does memcpy to stack from the input bt msg data, without size validation. The input len param isn&#039;t validated in this func either - if the remaining len following op_code is less than 2, a negative value will be used for the data copy to stack.&lt;br /&gt;
These were fixed by adding a bounds check for the size, size==0 is also checked for now.&lt;br /&gt;
| Bluetooth-sysmodule stack buffer overflow, with data received from a bluetooth message&lt;br /&gt;
| [[13.2.1]]&lt;br /&gt;
| [[13.2.1]]&lt;br /&gt;
| November 2021&lt;br /&gt;
| January 19, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Applet_Manager_services#IDisplayController|AM IDisplayController]] TakeScreenShotOfOwnLayer OOB&lt;br /&gt;
| The captureBuf is used as an array index without validation. Data used from this array includes calling a funcptr from the array entry, if set. Eventually this is also used to write bools into this array, one of which is from the command input.&lt;br /&gt;
With [5.0.0+] a func is eventually called to get a ptr determined by the input captureBuf, with nullptr being returned for captureBuf&amp;gt;=0x10. The caller will Abort if nullptr was returned.&lt;br /&gt;
| OOB array access&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[13.1.0]]&lt;br /&gt;
| ~July 31, 2019&lt;br /&gt;
| January 26, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Applet_Manager_services#IDisplayController|AM IDisplayController]] ClearCaptureBuffer OOB&lt;br /&gt;
| The captureBuf is used as an array index without proper validation. There is code validating it, but on failure it just skips over a code-block, with code using captureBuf still being used afterwards. Then this is used to write bools into a global array, one of which is from the command input.&lt;br /&gt;
This was fixed with [9.1.0+] by requiring captureBuf = 0-1.&lt;br /&gt;
| OOB bool writes into an array&lt;br /&gt;
| [[9.1.0]]&lt;br /&gt;
| [[13.1.0]]&lt;br /&gt;
| ~July 31, 2019&lt;br /&gt;
| January 26, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Sockets_services|bsdsockets]] ioctl SIOCGIFCONF infoleak&lt;br /&gt;
| Originally bsd ioctl SIOCGIFCONF was handled by setting the data in IPC outbuf0 to the size/addr of IPC outbuf1. These buffers are HipcAutoSelect, so if buf1 is small enough for HipcPointer (otherwise it would be HipcMapAlias) the IPC-buf-ptr leaked into outbuf0 would be located in the codebin-region. Since this is done before the actual ioctl-handling, it doesn&#039;t matter whether the fd is valid.&lt;br /&gt;
This was fixed in [5.0.0+] by using a tmp struct on stack instead of buf0.&lt;br /&gt;
| bsdsockets-sysmodule codebin-region addr infoleak, which allows defeating ASLR.&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[13.1.0]]&lt;br /&gt;
| February 14, 2022 (probably earlier)&lt;br /&gt;
| February 14, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]], probably others&lt;br /&gt;
|-&lt;br /&gt;
| [[Sockets_services|bsdsockets]] ioctl SIOCGIFMEDIA input can contain ptr&lt;br /&gt;
| Originally bsd ioctl SIOCGIFMEDIA used the user-specified ifmediareq structure directly from the input buffer. This includes a ptr. This ptr probably isn&#039;t actually used?&lt;br /&gt;
With [5.0.0+] the structure used as input for the ioctl was changed to using &amp;lt;code&amp;gt;int ifm_ulist[1]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;int *ifm_ulist&amp;lt;/code&amp;gt; (which is unused). The input structure is copied to a tmp struct which is used as the original ifmediareq structure, with ifm_ulist always NULL. The user can still specify a non-zero ifm_count value, however that&#039;s not useful with ifm_ulist being always NULL.&lt;br /&gt;
| Useless?&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[13.1.0]]&lt;br /&gt;
| February 14, 2022&lt;br /&gt;
| February 14, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]], probably others&lt;br /&gt;
|-&lt;br /&gt;
| Infoleak with [[Joy-Con]] HidCommand PairingIn&lt;br /&gt;
| The joycon protocol handler for PairingIn copies data from stack to the response cmd-buf for sending PairingOut. Only the first byte is set to a type value, the rest is uninitialized stack data.&lt;br /&gt;
&lt;br /&gt;
This was fixed with [15.0.0+] by directly writing to the response data without using stack data.&lt;br /&gt;
| Infoleak of hid stack via a bluetooth/uart message+response with a connected hid controller. This returns addrs for the main-codebin/stack, which allows defeating ASLR.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| September 4, 2020&lt;br /&gt;
| October 10, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Broken RNG for [[RO_services|ro]] ASLR&lt;br /&gt;
| The RNG used to determine where to randomly map NROs in the target process was TinyMT (nn::os::detail::RngManager output, seeded by 128 bits of entropy). However, TinyMT is not cryptographically secure (and can in fact be analytically solved). &lt;br /&gt;
&lt;br /&gt;
Thus, with a few NRO mapping addresses, one could learn the TinyMT state and derive all previous/future RNG outputs, breaking NRO aslr for all processes. &lt;br /&gt;
&lt;br /&gt;
With [15.0.0+] ro now uses csrng_GenerateRandomBytes to determine the random map address for NROs.&lt;br /&gt;
| Breaking ASLR for all NROs loaded in all processes, allowing predicting all NRO mappings for all processes until the next reboot.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| Late 2021/Early 2022&lt;br /&gt;
| October 11, 2022&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Broken RNG used by [[NS_Services|ns]]&lt;br /&gt;
| The code generating the sd seed and the data for the [[SD_Filesystem|sd]] private/private1 file, all use nn::os::GenerateRandomBytes, not csrng. The sd-seed is generated first, then private, then private1. This allows deriving sd-seed from private since this uses TinyMT, as long as the system shipped from factory on [2.0.0+]. private1 is only useful if the system shipped with [4.0.0+].&lt;br /&gt;
&lt;br /&gt;
There&#039;s various other code in ns using nn::os::GenerateRandomBytes as well. This includes the code generating ns_systemseed when it doesn&#039;t exist. ns_systemseed is generated at some point after the various sd-seed-related code (both are called from the same func). Hence, ns_systemseed can be recovered with the above method as well, if it wasn&#039;t recreated at some point without regenerating the above nand-save used with the above.&lt;br /&gt;
&lt;br /&gt;
With [15.0.0+] ns now uses csrng_GenerateRandomBytes for sd-seed/private and ns_systemseed, etc. This only matters when the file is newly generated, which is usually only for factory-fresh systems which ship with this version. This would also apply after being deleted during {System Settings -&amp;gt; Formatting Options -&amp;gt; Initialize Console}, and also with a refurbished console.&lt;br /&gt;
| Generation of a system&#039;s sd-seed allowing decryption of the NAX0 layer of data on [[SD_Filesystem|SD]], derived using the private file from SD. Applies to systems which factory-shipped with a system-version prior to [[15.0.0]] (that is, [2.0.0-14.1.2]).&lt;br /&gt;
| [[15.0.0]], for newly generated files&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| December ~12, 2021&lt;br /&gt;
| October 11, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] BSA bsa_sv_av_cback stack buffer overflow&lt;br /&gt;
| bsa_sv_av_cback checks for two input type values (0xC/0xD), on match it copies the input data to stack without size validation. Then it sends an internal request with this data (likewise when the type values don&#039;t match, except the input data is passed directly with a small size), then it returns.&lt;br /&gt;
This requires the AV functionality added with [13.0.0+], however this func is only reachable with [14.0.0+] where the required functionality was enabled.&lt;br /&gt;
&lt;br /&gt;
This requires message data that&#039;s larger than the MTU, so fragmentation must be used, or manually send the ACL data to bypass the MTU.&lt;br /&gt;
&lt;br /&gt;
This can be triggered via an AVRC message with opcode=0x0 (vendor). The above type 0xC is reached via AVRC ctype 0..4, while 0xD is reached with ctype&amp;gt;=0x9.&lt;br /&gt;
&lt;br /&gt;
With [15.0.0+] the size value for the memcpy (which is also written to the request struct) is clamped to a max value.&lt;br /&gt;
| Bluetooth-sysmodule stack buffer overflow on [14.0.0-14.1.2], with data received from an AVRC bluetooth message with a bluetooth-audio device.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| November 2021&lt;br /&gt;
| October 11, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[WLAN_services|wlan]] SetMulticastList heap buffer overflow&lt;br /&gt;
| The [[WLAN_services#SetMulticastList|SetMulticastList]] command allocates a 0x31-bytes sized buffer and copies to it as much [[WLAN_services#MacAddress|MacAddress]] values from the input [[WLAN_services#MulticastList|MulticastList]] as specified by the &amp;quot;Count&amp;quot; field, but this field is never validated. &lt;br /&gt;
&lt;br /&gt;
With [15.0.0+] error code 0x1906B is now returned if &amp;quot;Count&amp;quot; is larger than 8.&lt;br /&gt;
| wlan-sysmodule heap buffer overflow.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| June 6, 2022&lt;br /&gt;
| November 9, 2022&lt;br /&gt;
| [[User:Hexkyz|hexkyz]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] WriteGattCharacteristic/WriteGattDescriptor stack buffer overflow regression&lt;br /&gt;
| Originally btdrv WriteGattCharacteristic/WriteGattDescriptor (bt service LeClientWriteCharacteristic/LeClientWriteDescriptor are the same) validated the input buffer size. However the size check was removed with [12.0.0+] (which was also when bluetooth was refactored), hence stack buffer overflow. Anything with btdrv/bt services access can trigger it. While this is intended to require a BLE connection, it seems to be possible to trigger the buffer overflow without any BLE connection by passing ConnectionHandle=0xFFFFFFFF (handle not tested on hardware).&lt;br /&gt;
| Bluetooth-sysmodule stack buffer overflow on [12.0.0-15.0.1], with data from BLE IPC cmds.&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| December 10, 2021&lt;br /&gt;
| February 23, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[JIT_services|JIT]] usability issues&lt;br /&gt;
| CreateJitEnvironment will enter infinite-loops using nn::jitsrv::detail::AslrAllocator::GetAslrRegion when either of the input CodeMemory sizes are zero. Also the second CodeMemory is useless for the user-process since the second addr returned by GetCodeAddress is a dup of the first one, set during state init by CreateJitEnvironment.&lt;br /&gt;
With [14.0.0+] size=0 is now properly handled, and also the state for the second addr from GetCodeAddress is now properly initialized.&lt;br /&gt;
| Minor usability issues, not useful for exploitation (size=0 will cause jit-sysmodule to hang in a loop).&lt;br /&gt;
| [[14.0.0]]&lt;br /&gt;
| [[14.0.0]]&lt;br /&gt;
| October 1, 2020&lt;br /&gt;
| February 26, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[USB_services|usbhs]] uninitialized IClientEpSession&lt;br /&gt;
| usbhs IClientIfSession OpenUsbEp creates an IClientEpSession object. The allocated object from ExpHeap is not memset, only select fields are cleared. The rest of initialization is done by PopulateRing - however the user-process could skip using that if wanted (official sw always uses it).&lt;br /&gt;
&lt;br /&gt;
ShareReportRing maps tmem and writes the ring buffer/count field into object state. PopulateRing also eventually initializes these fields, with the buffer being allocated from ExpHeap instead of tmem. These fields are not cleared during object creation from OpenUsbEp.&lt;br /&gt;
&lt;br /&gt;
GetXferReport after validating the cmd input, just uses object state assuming it was initialized. This runs code which is the same as the user-process code handling the tmem ringbuf.&lt;br /&gt;
&lt;br /&gt;
Therefore, by skipping using PopulateRing and then using GetXferReport the sysmodule will use an uninitialized ringbuf ptr, and an uninitialized count field. If one could control these fields by doing ExpHeap allocations prior to OpenUsbEp so that {target fields} would be located at {IClientEpSession ring fields}, then one could read usb-sysmodule memory at the target buffer address.&lt;br /&gt;
&lt;br /&gt;
See [[USB_services#ShareReportRing|here]] for ringbuf format. The sysmodule will Abort if read_index is &amp;gt;= {ring count field from object state}. Otherwise it copies an entry from that index to output, and updates read_index.&lt;br /&gt;
&lt;br /&gt;
This is probably tricky to abuse as the ringbuf ptr has to be valid, and {see above} (likewise for write_index when the report-ringbuf-writing func runs).&lt;br /&gt;
&lt;br /&gt;
PostBufferAsync/BatchBufferAsync also use seperate object ring fields which are left uninitialized from OpenUsbEp. Targeting this would be tricky with the ring restrictions - this would allow writing data to a ring addr however.&lt;br /&gt;
&lt;br /&gt;
Pre-4.0.0 (only 2.0.0 checked) is not affected by these. The ring fields in the object are cleared during object creation (no memset of the entire object however). GetXferReport would null-deref if PopulateRing was skipped. PostBufferAsync/BatchBufferAsync will throw an error if PopulateRing was skipped. Pre-4.0.0 also has different ring handling as well.&lt;br /&gt;
&lt;br /&gt;
[16.0.0+] The IClientEpSession init func now clears the remaining previously uninitialized fields. The cmds using the ring fields still don&#039;t check for NULL, so using GetXferReport/PostBufferAsync/BatchBufferAsync without PopulateRing will just trigger null-deref. Even if the ptr were somehow valid but ring-count field was left at 0, this would then Abort due to: &amp;lt;code&amp;gt;if (ring_count &amp;lt;= index_loaded_from_ringptr) &amp;lt;Abort&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| [4.0.0-15.0.1] If one can trigger using {target values} as the unintialized fields: memory reads from the target addr with GetXferReport, and memory R/W with PostBufferAsync/BatchBufferAsync. This requires access to usb:hs, and an usb device must be connected which is not being used by {other sessions}. If successful, this might (?) result in usb-sysmodule compromise.&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| January 30, 2023&lt;br /&gt;
| February 26, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[NS_services|ns]] RequestMoveApplicationEntity/EstimateSizeToMove buffer overflow&lt;br /&gt;
| ns RequestMoveApplicationEntity eventually calls a func which: Loops through the input buffer. If any entry has value 6, it will call another func to copy data from state to output safely (uses the max_count param). Otherwise, it copies the input buffer to an outbuf (located on caller&#039;s stack) without any size validation (inlined memcpy), even though there is a max_count param.&lt;br /&gt;
&lt;br /&gt;
Additional memwrites are also done to the above outbuf following the initial memcopy. This can be avoided if the buffer doesn&#039;t contain bytes with values 3-6 (if using values in that range is really needed, the cmd input StorageId param can be set to the required value so that the specified value doesn&#039;t trigger the memwrite). Value 6 shouldn&#039;t be used anyway (see above).&lt;br /&gt;
&lt;br /&gt;
ns EstimateSizeToMove first calls the same func which does the copy above (outbuf is also located on stack), then it calls another func. Hence, same vuln here.&lt;br /&gt;
&lt;br /&gt;
By corrupting just the first byte of x29 with EstimateSizeToMove, one can obtain infoleaks. This method with x29 essentially only works with [15.0.0+]. Pre-15.0.0 would require a different method with partial overwrite of retaddr, however it&#039;s unknown whether this would actually work for infoleak (would require [12.0.0+] for the stack layout change).&lt;br /&gt;
With EstimateSizeToMove where x29 is overwritten, the output u64 is the leaked ptr (can be codebin-region). Note that the cmd has to return Result=0 for this to work. x29 is used to load the value which is copied to the cmdreply rawdata.&lt;br /&gt;
&lt;br /&gt;
As of [17.0.0+] an error is thrown if the input array count is larger than 8 (size of the stack dst-array).&lt;br /&gt;
| ns-sysmodule stack buffer overflow, allowing ns infoleak+ROP.&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| January 2, 2023&lt;br /&gt;
| October 17, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[PSC_services|ovln:snd]] OpenSender unvalidated count&lt;br /&gt;
| ovln:snd OpenSender has a count param. This count is used to allocate the specified number of objects in a linked-list for storing the data from Send. If count is 0, the linked-list is left empty, with ptrs to itself within the ISender object.&lt;br /&gt;
&lt;br /&gt;
ISender Send when the above linked-list is empty, runs a switch-statement with &amp;lt;code&amp;gt;(inval&amp;gt;&amp;gt;8)&amp;amp;0xFF&amp;lt;/code&amp;gt;. This uses another linked-list where the ptrs are initially {within ISender obj}.&lt;br /&gt;
No space is allocated in the ISender obj for the linked-list object-data. Therefore using Send with val 1&amp;lt;&amp;lt;8 or 2&amp;lt;&amp;lt;8 (other values throw error) results in the specified input struct being copied into the ISender obj, which then overwrites heap data OOB.&lt;br /&gt;
If for example one used OpenSender again right after the first OpenSender usage, then used Send as described above, this would corrupt the second ISender which includes overwriting the vtable.&lt;br /&gt;
If one would use Send twice in a row like this, the second one would use a corrupted linked-list (written from the first Send). If the linked-list ptrs would be valid (no crash triggered) this would allow one to copy the input data to a controlled addr, though it&#039;s restricted with the linked-list usage.&lt;br /&gt;
&lt;br /&gt;
Using GetUnreceivedMessageCount afterwards is of no interest.&lt;br /&gt;
&lt;br /&gt;
Besides ovln, the only other allocs on this heap is from IPmModule Initialize. This heap is also used for psc:* services (object allocs).&lt;br /&gt;
&lt;br /&gt;
In theory (untested) it may be possible to also use this to obtain infoleaks, however it would only return the high-u32 of ptrs not the low u32. Essentially, one would trigger object allocations so that ExpHeap has layout: {ISender} -&amp;gt; {RF chunk from freeing an object} -&amp;gt; {module object from IPmModule Initialize}. Then one would use the Send vuln to corrupt the RF chunk, changing the size to a larger value. Then one would trigger an object allocation (probably same object which was previously freed), then another object for overwriting the module object (ISender would work) with ptrs at the target offsets in the module object. Then once IPmModule GetRequest is used, the returned u32s would be the high-u32 from ptrs. Due to alignment requirements with each allocation, it isn&#039;t possible to shift the allocations in order to leak ptr low-u32.&lt;br /&gt;
&lt;br /&gt;
[17.0.0+] Now throws an error if the input count for OpenSender is 0.&lt;br /&gt;
| [[PSC_services|psc]]-sysmodule heap memory corruption ([[NS_services|ns]]-sysmodule on pre-8.0.0).&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| January 13, 2023&lt;br /&gt;
| October 20, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[NV_services|nv]] NVGPU_GPU_IOCTL_GET_CHARACTERISTICS Ioctl3 infoleak&lt;br /&gt;
| The handler code for NVGPU_GPU_IOCTL_GET_CHARACTERISTICS for Ioctl/Ioctl3 are essentially the same, except for the value used for the max-size clamp: Ioctl uses constant 0xA0, while Ioctl3 uses the outbuf1_size. So if one uses this with Ioctl3 and a large outbuf1, this will memcpy data OOB from the source buffer, hence infoleak.&lt;br /&gt;
With [17.0.0+] the second block of csel code which previouly essentially used the clamped size from above, was replaced with code which properly clamps to the max-size constant.&lt;br /&gt;
| nvservices-sysmodule infoleak, which allows defeating ASLR.&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| February 25, 2022&lt;br /&gt;
| October 24, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Audio_services|audctl]] GetTargetDeviceInfo infoleak&lt;br /&gt;
| audctl GetTargetDeviceInfo calls an impl func with a ptr to a stackbuf, then if successful memcpys the 0x100-bytes from that buffer to output. This stackbuf is not memset. This func (after doing various state checks) copies a string to output, other than always writing a NUL-terminator there&#039;s no clearing of the buffer.&lt;br /&gt;
&lt;br /&gt;
This will leak audio-sysmodule stack into the output buffer as long as the state/input checks pass (for the remainder of the buffer following the string NUL-terminator).&lt;br /&gt;
&lt;br /&gt;
With [18.0.0+] data is written directly to the outbuf instead of the stack tmpbuf.&lt;br /&gt;
| audio-sysmodule infoleak, which allows defeating ASLR.&lt;br /&gt;
| [[18.0.0]]&lt;br /&gt;
| [[18.0.0]]&lt;br /&gt;
| December 24, 2022&lt;br /&gt;
| March 26, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Audio_services|audctl]] GetSystemInformationForDebug infoleak / buffer overflow&lt;br /&gt;
| audctl GetSystemInformationForDebug calls a func with a 0x1000-byte stack tmpbuf, then afterwards that buffer is memcpy&#039;d into the cmd outbuf. This called func doesn&#039;t clear the buffer. This func eventually uses [[BTM_services|btm]] cmd75 with outarray={global ptr} and count=10. Then if the outcount is s32 &amp;gt;=1, it loops through the output using the outcount, without validating it besides the &amp;lt;1 check. Data from that outarray is copied into the array in the func output buffer (tmpbuf above).&lt;br /&gt;
&lt;br /&gt;
With btm comprimised, one could return a large output count and trigger a stack buffer overflow with data following that global array, however exploiting this would be difficult since that data would be uncontrolled (can&#039;t directly control it from this cmd at least).&lt;br /&gt;
&lt;br /&gt;
A stack infoleak can be obtained with this as well (assuming the above output array isn&#039;t full).&lt;br /&gt;
&lt;br /&gt;
Even though the name has &amp;quot;ForDebug&amp;quot;, there&#039;s no checks which would trigger an error / return early (this also always returns 0).&lt;br /&gt;
&lt;br /&gt;
[18.0.0+] now clears the output buffer, and also now prints strings into the buffer instead of writing binary data (overflow no longer possible).&lt;br /&gt;
| audio-sysmodule infoleak, which allows defeating ASLR. Also audio-sysmodule memory corruption, likely not useful unless there&#039;s a way to control the data.&lt;br /&gt;
| [[18.0.0]]&lt;br /&gt;
| [[18.0.0]]&lt;br /&gt;
| December 7, 2022&lt;br /&gt;
| March 27, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Migration_services|migration]] nn::migration::savedata::IServer cmd1 buffer overflow&lt;br /&gt;
| nn::migration::savedata::IServer cmd1 with [18.0.0-18.0.1] copies data from an array to the output ptr. As the output is an u64 field for the IPC cmd output, this is a field on stack. Hence, if more than 1 entry (8-bytes) are copied a stack buffer overflow will occur. Note that cmd3 loads the same data, except this has a proper output array.&lt;br /&gt;
It&#039;s unknown whether there&#039;s a way to actually control this data with a large enough enough size.&lt;br /&gt;
&lt;br /&gt;
See [[18.1.0]] for the diff/fix.&lt;br /&gt;
| [[Migration_services|migration]] stack buffer overflow, only on [18.0.0-18.0.1].&lt;br /&gt;
| [[18.1.0]]&lt;br /&gt;
| [[18.1.0]]&lt;br /&gt;
| June 11, 2024&lt;br /&gt;
| June 11, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]] (sysupdate diff)&lt;br /&gt;
|-&lt;br /&gt;
| [[SSL_services|ssl]] broken RNG&lt;br /&gt;
| [[SSL_services|ssl]] uses nn::os::GenerateRandomBytes, but not [[SPL_services|spl]] GenerateRandomBytes. See the RNG entries elsewhere. This is used to seed the NSS global RNG (drbg.c, RNG_GenerateGlobalRandomBytes etc).&lt;br /&gt;
&lt;br /&gt;
If one could somehow determine the data which was returned by nn::os::GenerateRandomBytes during seeding (which is likely difficult), the global RNG would be broken.&lt;br /&gt;
&lt;br /&gt;
With [19.0.0+] nn::os::GenerateRandomBytes usage was replaced with [[SPL_services|spl]] GenerateRandomBytes.&lt;br /&gt;
| Breaking [[SSL_services|ssl]] global RNG -&amp;gt; potentially predict RNG data (keys(?)) during TLS comms.&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| December 14, 2021&lt;br /&gt;
| October 8, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Audio_services|audren]] uncleared TransferMemory&lt;br /&gt;
| audren OpenAudioRenderer uses the input tmem as workmem. The IAudioRenderer dtor doesn&#039;t clear the workmem properly. Depending on input params, certain objects stored here have vtables - hence infoleak.&lt;br /&gt;
The exact location in the workmem will vary depending on the input params - these objects are dynamically allocated in the workmem.&lt;br /&gt;
The following will leak vtables: Sink, Effect.&lt;br /&gt;
&lt;br /&gt;
If the initialization func fails, the tmem is unmapped without clearing it first. It&#039;s unknown whether there&#039;s a way to actually trigger an infoleak with this however. With [19.0.0+] it&#039;s now cleared on failure.&lt;br /&gt;
&lt;br /&gt;
With [19.0.0+] the dtor now clears the workmem when needed.&lt;br /&gt;
| Reading leaked data/ptrs from TransferMemory -&amp;gt; defeating ASLR in [[Audio_services|audio]]-sysmodule.&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| December 17, 2022&lt;br /&gt;
| October 13, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Audio_services|audren]] UpdateMixes OOB mem-copy&lt;br /&gt;
| With nn::audio::server::InfoUpdater::UpdateMixes when nn::audio::server::BehaviorInfo::IsMixInParameterDirtyOnlyUpdateSupported() returns true (requires REV7, which is [7.0.0+]), the mix_id from user input is used without validation as input to &amp;lt;code&amp;gt;&amp;lt;nn::audio::server::MixContext::GetInfo(int) const&amp;gt;&amp;lt;/code&amp;gt;, instead of the counter from the for-loop. This allows one to control the destination MixInfo index which the user-input data is written into. If too large, this will trigger OOB data-copy. Note that the u8 at dest_MixInfo+12 must be non-zero.&lt;br /&gt;
Also note that a field is loaded from dest_MixInfo which is used as a splitter_id, so splitters need to be initialized where count is large enough for that id.&lt;br /&gt;
&lt;br /&gt;
With [19.0.0+] after getting the mix_id (loop-index/input) it now does: &amp;lt;code&amp;gt;if (mix_id &amp;lt; 0 || mix_id &amp;gt;= nn::audio::server::MixContext::GetCount()) continue;&amp;lt;/code&amp;gt;&lt;br /&gt;
| OOB mem-copy in [[Audio_services|audio]]-sysmodule, which for example can be used to overwrite a vtable used immediately after UpdateMixes.&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| December 19, 2022&lt;br /&gt;
| October 13, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bus_services|sasbus]] StartPeriodicReceiveMode infoleak&lt;br /&gt;
| StartPeriodicReceiveMode writes a vtable ptr into the mapped tmem at +0. The tmem is mapped RW in the user-process. There is no clearing of tmem during tmem cleanup. Hence, the user-process can read the tmem to obtain a Bus-sysmodule codebin-region infoleak. This vtable-ptr seems to be unused - it&#039;s also empty after the first two entries (stubbed incref/decref).&lt;br /&gt;
[20.0.0+] Removed the vtable ptr, with data intended for the user-process being moved from tmem+0x8 to +0x0. Also, instead of calling memset, funcs are called for manually clearing tmem.&lt;br /&gt;
| Bus-sysmodule infoleak, which allows defeating ASLR.&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| February 22, 2022&lt;br /&gt;
| May 3, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[NFC_services|nfc]] SendCommandByPassThrough buffer overflow&lt;br /&gt;
| SendCommandByPassThrough eventually copies the input buffer into a fixed-size heap buffer, without size validation.&lt;br /&gt;
This was fixed with [20.0.0+] by clamping the size.&lt;br /&gt;
| nfc-sysmodule heap buffer overflow.&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| Late November 2021&lt;br /&gt;
| May 3, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]] (maybe others?)&lt;br /&gt;
|-&lt;br /&gt;
| [[HID_services|hidbus]] EnableJoyPollingReceiveMode infoleak&lt;br /&gt;
| The tmem initialized by hidbus EnableJoyPollingReceiveMode contains a vtable ptr (tmem+0x10), hence infoleak. With [20.0.0+] the vtable ptr write was removed, and tmem is now memset starting at tmem+0x10 instead of +0x20.&lt;br /&gt;
| hid-sysmodule infoleak, which allows defeating ASLR.&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| March 2020&lt;br /&gt;
| May 4, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[SSL_services|ssl]] Certificate verification bypass&lt;br /&gt;
| The ssl sysmodule keeps a list of trusted certificates, that are imported by an app with ImportServerPki. During certificate verification, if the certificate that is provided by the server has the same subject key id as a trusted certificate, the certificate is accepted, even if self-signed. A blog post about this vulnerability can be found [https://reversing.live/sslbypass.html here].&lt;br /&gt;
| Man-in-the-middle for any connection that uses ImportServerPki.&lt;br /&gt;
| [[20.2.0]]&lt;br /&gt;
| [[20.2.0]]&lt;br /&gt;
| June 6, 2025&lt;br /&gt;
| August 8, 2025&lt;br /&gt;
| [https://github.com/kinnay Yannik]&lt;br /&gt;
|-&lt;br /&gt;
| [[LDN_services|ldn]] AdvertiseData OOB-memcpy with EncryptionType3 (AES-128-GCM) actionframes (ldnhax)&lt;br /&gt;
| The ldn action-frame parser object for AES-128-GCM (used with [[LDN_services|EncryptionType3]]), when it does validation once finished, only verifies that the sizes are within bounds of the input buffer. There&#039;s no validation against constants, which the other EncryptionType objects have. The caller code doesn&#039;t validate the size either.&lt;br /&gt;
&lt;br /&gt;
[21.0.0+] Now validates the advert-size with sizeof(NetworkInfo.AdvertiseData).&lt;br /&gt;
&lt;br /&gt;
For more details see [https://gist.github.com/yellows8/16bb56343d085d2db2ab0adc5d4cef99 here].&lt;br /&gt;
| Compromise of ldn starting from OOB-memcpy, even on S2: stack infoleak (ASLR defeat), arbitrary memory read/write (which also allows handle-leak), vfunc-calls with arbitrary [[Security_Mitigations|vtable]].&lt;br /&gt;
| [[21.0.0]]&lt;br /&gt;
| [[21.0.0]]&lt;br /&gt;
| June ~13, 2025&lt;br /&gt;
| November 11, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[HID_services|hid:dbg]] AttachHdlsVirtualDevice unvalidated DeviceTypeInternal&lt;br /&gt;
| hid:dbg AttachHdlsVirtualDevice eventually passes the input from HdlsDeviceInfo into a func without any validation. The DeviceTypeInternal field is used as the index for loading a ptr from a global array. The only validation occurs when the loaded ptr is NULL - this is just for initializing the ptr in the array when it&#039;s not already set.&lt;br /&gt;
&lt;br /&gt;
Since the highest DeviceTypeInternal is value 30, using &amp;gt;=31 will load an OOB ptr. This ptr is written to state, and also immediately passed to a called func. As long as ptr is valid it should be fine with this func.&lt;br /&gt;
&lt;br /&gt;
This functionality is also used eventually by ApplyHdlsNpadAssignmentState and ApplyHdlsStateList.&lt;br /&gt;
&lt;br /&gt;
It&#039;s unknown whether there&#039;s a way to exploit this. Also note that hid:dbg is not normally accessible to retail titles.&lt;br /&gt;
&lt;br /&gt;
[21.0.0+] Arrayindex=0 is now used when the input is invalid.&lt;br /&gt;
| Likely useless, even if reachable?&lt;br /&gt;
| [[21.0.0]]&lt;br /&gt;
| [[21.0.0]]&lt;br /&gt;
| June 3, 2024 (possibly eariler(?))&lt;br /&gt;
| November 14, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] BSA allowed ATT MTU is too large&lt;br /&gt;
| GATT-handler stack buffer overflows with a large input size are only possible if the payload_size (MTU) field in state is large enough. gatt_client_handle_server_rsp/gatt_server_handle_client_req will drop messages where the size is &amp;gt;= payload_size (though unless the request opcode matches certain values it will also send an error-response for invalid-PDU). Both of these handle updating this field when needed, however that&#039;s handled properly.&lt;br /&gt;
&lt;br /&gt;
With bluetooth-classic via L2CAP, a hard-coded MTU of 0x205 is sent in the configure request. However the code handling received configure requests will set payload_size to 0x2A0 if no MTU is specified, or the input MTU if it&#039;s within range 0x30..0x2A0. Hence, sending data large enough for buffer overflows requires bluetooth-classic via L2CAP + manually sending large ACL data.&lt;br /&gt;
&lt;br /&gt;
[15.0.0+] gatt_l2cif_config_ind_cback which handles the received configure-requests with bluetooth-classic mentioned above, now uses MTU range 0x30..0x205 with the default MTU being 0x205. It is therefore no longer possible to trigger the previously mentioned buffer-overflows with bluetooth-classic.&lt;br /&gt;
| Stack buffer overflows in bluetooth-sysmodule due to the allowed MTU for ATT being larger than the stack data.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| November 2021?&lt;br /&gt;
| November 26, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] BSA gatt_process_prep_write_rsp stack buffer overflow&lt;br /&gt;
| BSA gatt_process_prep_write_rsp memcpys to stack without size validation (the input len param which is subtracted to determine the copy-size is also unvalidated). Triggering this is only possible if the system sent ATT_PREPARE_WRITE_REQ, and then received ATT_PREPARE_WRITE_RSP with a large size.&lt;br /&gt;
&lt;br /&gt;
The size used with memcpy is (u16)(insize-4), so when insize is less than 4 the copy size will be {negative value masked to u16}. This will therefore eventually crash when the stacktop is reached during memcpy.&lt;br /&gt;
&lt;br /&gt;
[15.0.0+] Paritially fixed due to corrected MTU handling (doesn&#039;t apply to negative-copysize). [21.0.0+] Fully fixed with proper size validation.&lt;br /&gt;
| Stack buffer overflow in bluetooth-sysmodule when the required ATT messages are sent/received.&lt;br /&gt;
| [[21.0.0]]&lt;br /&gt;
| [[21.0.0]]&lt;br /&gt;
| November 2021?&lt;br /&gt;
| January 19, 2026&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Internet Browser == &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| CVE-2016-4657&lt;br /&gt;
| WebKit vuln discovered around August 2016. Most notably used in the iOS 9.3.X exploit. A simple PoC can be found [https://github.com/LiveOverflow/lo_nintendoswitch/blob/master/poc1.html here]. This was later exploited by [https://twitter.com/qwertyoruiopz Qwertyoruiop] using an adjusted version of his iOS 9.3 webkit exploit (others exploited this prior to then).&lt;br /&gt;
|&lt;br /&gt;
| [[2.1.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| Original: August 2016&lt;br /&gt;
Switch: March 3rd-4th 2017&lt;br /&gt;
|&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| CVE-2017-7005&lt;br /&gt;
| WebKit type confusion.&lt;br /&gt;
|&lt;br /&gt;
| [[3.0.1]]&lt;br /&gt;
| [[3.0.1]]&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| CVE-2016-4622&lt;br /&gt;
| WebKit memory corruption bug. This bug was incorrectly re-introduced in [[4.0.0]]. See [http://www.phrack.org/papers/attacking_javascript_engines.html here] for a detailed write-up from the author.&lt;br /&gt;
|&lt;br /&gt;
| [[6.1.0]]&lt;br /&gt;
| [[6.1.0]]&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| CVE-2018-4441&lt;br /&gt;
| WebKit memory corruption bug. See [https://bugs.chromium.org/p/project-zero/issues/detail?id=1685&amp;amp;desc=2 here].&lt;br /&gt;
|&lt;br /&gt;
| [[7.0.0]]&lt;br /&gt;
| [[7.0.0]]&lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Web-applets OpenSSL broken RNG&lt;br /&gt;
| [[SPL_services|csrng]] access was added to web-applets with [12.1.0+]. Prior to that, csrng and nn::os::GenerateRandomBytes were not used (besides sdk heap code).&lt;br /&gt;
nn::os::GetSystemTick is used to seed the OpenSSL RNG, among other data. Hence, it&#039;s probably (?) possible to bruteforce the RNG initial state, allowing predicting RNG output.&lt;br /&gt;
&lt;br /&gt;
The RNG code is wkcRandomNumbersPeer (peer_wkc nro), with the initialization code using GetSystemTick located in the func immediately before wkcGetTickCountPeer. The former is called from wkcOsslRandFilefReadPeer. wkcOsslRandFilefReadPeer is called for seeding the OpenSSL RNG.&lt;br /&gt;
&lt;br /&gt;
With [12.1.0+], wkcRandomNumberPeer/wkcRandomNumbersPeer wrap nn::os::GenerateRandomBytes. wkcCryptographicallyRandomValuesPeer was added which wraps nn::crypto::GenerateCryptographicallyRandomBytes. wkcOsslRandFilefReadPeer now calls nn::crypto::GenerateCryptographicallyRandomBytes instead of wkcRandomNumbersPeer.&lt;br /&gt;
| Breaking web-applets OpenSSL RNG -&amp;gt; potentially predict RNG data (keys(?)) during TLS comms.&lt;br /&gt;
| [[12.1.0]]&lt;br /&gt;
| [[12.1.0]]&lt;br /&gt;
| January 28, 2022&lt;br /&gt;
| October 8, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]], likely (?) others&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Whitelist ===&lt;br /&gt;
This section documents [[Internet_Browser|WebApplet]] whitelist issues in applications. These can be used to load your own browser content over plain HTTP, which then for example could be used for web-applet exploitation.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Application&lt;br /&gt;
!  Description&lt;br /&gt;
!  Fixed with app version&lt;br /&gt;
!  Newest app version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Sonic Mania&lt;br /&gt;
| Originally this game launched web-applet with a plain-http URL for displaying the manual, this was later changed to https. Originally the whitelist only had 1 entry for a http URL, this was later replaced with various https-only URLs.&lt;br /&gt;
| 1.04, unknown if fixed with an earlier update&lt;br /&gt;
| 1.04&lt;br /&gt;
| January (?) 2022&lt;br /&gt;
| February 23, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NintendoSDK ==&lt;br /&gt;
This section documents vulnerabilities for NSOs in NintendoSDK.&lt;br /&gt;
&lt;br /&gt;
=== nnSdk ===&lt;br /&gt;
This section documents vulnerabilities for nnSdk (sdknso).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in SDK [[System_Versions|version]]&lt;br /&gt;
!  Last SDK version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| [[HID_services|hidbus]] GetJoyPollingReceivedData buffer overflow&lt;br /&gt;
| hidbus GetJoyPollingReceivedData doesn&#039;t validate the u8 size used for memcpy, when copying the data to the output JoyPollingReceivedData. With 11.x, the size is now clamped to a maximum of 0x2C (regardless of polling-mode). Note that 0x2C is the data-size for JoyButtonOnlyPollingDataAccessor, the other polling-modes have a smaller size.&lt;br /&gt;
&lt;br /&gt;
The hid-sysmodule code which writes data here does handle it properly: size is clamped to a max size, and the data-read uses a fixed-size anyway (hence there&#039;s no way to trigger this sdknso vuln with the hid-sysmodule tmem writing code).&lt;br /&gt;
&lt;br /&gt;
This could only be exploited if one directly writes to the tmem when one has previously compromised hid-sysmodule, without using the normal tmem-writing func for this.&lt;br /&gt;
&lt;br /&gt;
There are only a few [[HID_services#ExternalDevices|apps]] which use hidbus.&lt;br /&gt;
| Triggering a buffer overflow in an application which uses hidbus GetJoyPollingReceivedData, from a previously compromised hid-sysmodule.&lt;br /&gt;
| 11.x.0&lt;br /&gt;
| 11.4.0&lt;br /&gt;
| March 2020&lt;br /&gt;
| December 3, 2020&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Profile_Selector|Profile Selector]] uninitialized input data&lt;br /&gt;
| Originally unused regions of [[Profile_Selector]] UiSettings/UserSelectionSettings were not cleared prior to being sent to the applet. With 1.x.x these are now properly memset().&lt;br /&gt;
| Stack infoleak from user-process, sent to the applet.&lt;br /&gt;
| 1.x.x&lt;br /&gt;
| 11.4.0&lt;br /&gt;
| November-December 2019&lt;br /&gt;
| December 31, 2020&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== NEX ===&lt;br /&gt;
This section documents client-side vulnerabilities for [https://github.com/Kinnay/NintendoClients/wiki/NEX-Overview-(Game-Servers) NEX].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in version&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Buffer overflow in StringConversion::T2Char8&lt;br /&gt;
| StringConversion::T2Char8 is used to convert IP addresses from a platform-specific encoding to UTF-8. On the 3DS and Switch, the implementation is simply a strcpy. By sending a long IP address string, a buffer overflow can be triggered on the stack. The vulnerability can be triggered through the NAT traversal protocol. A blog post about this vulnerable can be found [https://reversing.live/hacking-hundreds-of-wii-us-at-once.html here].&lt;br /&gt;
| Stack overflow in any game that uses NEX for matchmaking&lt;br /&gt;
| Fixed server-side&lt;br /&gt;
| December, 2022&lt;br /&gt;
| May, 2024&lt;br /&gt;
| [https://github.com/kinnay Yannik]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Pia ===&lt;br /&gt;
This section documents vulnerabilities for [https://github.com/Kinnay/NintendoClients/wiki/Pia-Overview Pia].&lt;br /&gt;
&lt;br /&gt;
In v5.11.3 (exact starting version unknown) the fixes aren&#039;t present for the below vulns which were fixed in v5.9.3, while in v5.18.98 these are present (exact starting version unknown). This probably indicates that the vuln fixes were backported from a newer Pia version to v5.9.3.&lt;br /&gt;
&lt;br /&gt;
The Pia packet handlers are only active when the game is using multiplayer. LanProtocol is only active in the games which are actively using the LAN-mode option (not Ldn) - only certain games support LAN-mode. The LanProtocol Pia packet handler can be reached while in a lobby or searching for one.&lt;br /&gt;
&lt;br /&gt;
Most Pia packets require an active StationProtocol connection to be active with {InetAddr which the packet was received from}, otherwise the packet is filtered out. The only protocols which don&#039;t use filtering are the following: NatTraversalProtocol, LanProtocol, StationProtocol, LocalProtocol.&lt;br /&gt;
&lt;br /&gt;
Note that broadcast IP-dest Pia packets are accepted - this can be used to target every device on the network which is using Pia (which is really only useful with {above protocols} due to the filtering mentioned above, unless one also handles StationProtocol).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in Pia version&lt;br /&gt;
!  Last Pia version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| nn::pia::session::RelayRouteManageJob::UpdateConnectionReport buffer overflow&lt;br /&gt;
| nn::pia::session::RelayRouteManageJob::UpdateConnectionReport() checks that the input size is at least {value}, but there&#039;s no max size check. This is used to memcpy from the input to elsewhere - hence buf-overflow if size is too large. The dst buffer is allocated on the pead heap - this buffer is probably small.&lt;br /&gt;
Note that there&#039;s various requirements before it would actually reach the memcpy, such as &amp;lt;code&amp;gt;&amp;lt;nn::pia::session::Mesh::IsHost() const&amp;gt;&amp;lt;/code&amp;gt; must return true.&lt;br /&gt;
&lt;br /&gt;
This is called from nn::pia::session::MeshProtocol::ParseConnectionReport().&lt;br /&gt;
&lt;br /&gt;
ParseConnectionReport uses a state ptr for object nn::pia::session::RelayRouteManageJob, it will return if not set. nn::pia::session::Mesh::Initialize handles setup for this, depending on an input field from nn::pia::session::Mesh::Setting. These settings originate from &amp;lt;code&amp;gt;&amp;lt;nn::pia::session::Session::CreateInstance(nn::pia::session::Session::Setting const&amp;amp;)&amp;gt;&amp;lt;/code&amp;gt;, which is called by user-code with the needed settings.&lt;br /&gt;
ParseConnectionReport is therefore only usable if the game explicitly enables the Relay functionality.&lt;br /&gt;
&lt;br /&gt;
In fixed versions immediately after the StationIndex validation it now does: &amp;lt;code&amp;gt;if(statefield+0x10&amp;lt;input_size) return;&amp;lt;/code&amp;gt;&lt;br /&gt;
| Heap buffer overflow triggered by a Pia MeshProtocol message sent to a host device.&lt;br /&gt;
| v5.9.3, see above.&lt;br /&gt;
| v5.9.1/v5.9.2/v5.9.3&lt;br /&gt;
| November 11, 2022&lt;br /&gt;
| November 15, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| nn::pia::lan::LanProtocol::ParseSessionMessage buffer overflow&lt;br /&gt;
| nn::pia::lan::LanProtocol::ParseSessionMessage() calls nn::pia::lan::LanSessionMessage::Deserialize() to deserialize the message payload data buffer into the LanSessionMessage object on stack. LanSessionMessage::Deserialize (among other things) memcpys data from the input buffer to the object, using an u32 from the input buffer - there is no size validation in Deserialize itself.&lt;br /&gt;
There is a size check immediately after calling Deserialize() to verify &amp;lt;code&amp;gt;payloadsize=={u32val}+{constant}&amp;lt;/code&amp;gt;, returning on fail - but this doesn&#039;t matter for too-large-size.&lt;br /&gt;
&lt;br /&gt;
In fixed versions Deserialize now does bounds checking, both for the minimum message size and clamping the memcpy size to a constant. An error is thrown if the clamped memcpy size is larger than the message size. The caller now checks the ret properly, previously it was ignored.&lt;br /&gt;
&lt;br /&gt;
Following the size check in ParseSessionMessage() it calls &amp;lt;code&amp;gt;&amp;lt;nn::pia::session::Mesh::IsProcessingLeaveMesh() const&amp;gt;&amp;lt;/code&amp;gt;, returning if ret is false.&lt;br /&gt;
&lt;br /&gt;
Then it calls nn::pia::lan::LanProtocol::ReceivedFragmentData::Receive(), with the memcpy&#039;d buffer/size from the above LanSessionMessage, and other fields from LanSessionMessage. This eventually memcpys the input buffer to object+{offset}+{chunksize_field}*inputu8, there is no validation for size or inputu8 (except for the above size check). Hence, if the u8 is large enough, this would result in a heap buffer overflow.&lt;br /&gt;
&lt;br /&gt;
In fixed versions ReceivedFragmentData::Receive added a bunch of validation before the memcpy.&lt;br /&gt;
| Stack/heap buffer overflow triggered by a Pia LanProtocol message.&lt;br /&gt;
| v5.9.3, see above.&lt;br /&gt;
| v5.9.1/v5.9.2/v5.9.3&lt;br /&gt;
| November 14, 2022&lt;br /&gt;
| November 15, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| nn::pia::session::SessionProtocol::ParseLeaveMeshInvitation buffer overflow&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nn::pia::session::SessionProtocol::ParseLeaveMeshInvitation(nn::pia::transport::ReceivedMessageAccessor const&amp;amp;)&amp;gt;&amp;lt;/code&amp;gt; This immediately returns if *(ReceivedMessageAccessor+16) is 0. Then the input data is deserialized. The input u64 array is deserialized to stack, the u8 arraycount field from input is not validated.&lt;br /&gt;
&lt;br /&gt;
Hence, stack buffer overflow. Note that there&#039;s similar loop code in nearby funcs, which do validate the count properly.&lt;br /&gt;
&lt;br /&gt;
In fixed versions the arraycount field is now validated.&lt;br /&gt;
&lt;br /&gt;
SessionProtocol uses ReliableSlidingWindow MessageHeader, with a maximum message size of 0x100. The allocated size used for the above u64 array is also 0x100-bytes. Hence, when triggering a buf overflow the data after the buffer is uncontrolled data from the SessionProtocol object.&lt;br /&gt;
| Stack buffer overflow triggered by a Pia SessionProtocol message.&lt;br /&gt;
| v5.9.3, see above.&lt;br /&gt;
| v5.9.1/v5.9.2/v5.9.3&lt;br /&gt;
| November 14, 2022&lt;br /&gt;
| November 15, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Optional Pia packet encryption&lt;br /&gt;
| Pia packet encryption is optional. If the encryption flag is disabled, the packet handler will accept it and skip crypto.&lt;br /&gt;
In fixed versions immediately after grabbing a packet, it now checks the crypto flag. If it&#039;s plaintext the packet is dropped.&lt;br /&gt;
&lt;br /&gt;
This can be used to send a plaintext Pia packet without needing to handle encryption, especially useful if the session-key can&#039;t be obtained (online-play matchmaking). This could be combined with other vulns if wanted.&lt;br /&gt;
| Sending a plaintext Pia packet without needing to handle encryption.&lt;br /&gt;
| v5.9.3, see above.&lt;br /&gt;
| v5.9.3 (and later versions)&lt;br /&gt;
| &lt;br /&gt;
| November 19, 2022&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| nn::pia::session::{JoinMeshJob/ProcessUpdateMeshJob}::SetStationDataList OOB read/write/vfunc-call&lt;br /&gt;
| &amp;lt;code&amp;gt;nn::pia::session::JoinMeshJob::SetStationDataList&amp;lt;/code&amp;gt;is called by &amp;lt;code&amp;gt;nn::pia::session::MeshProtocol::ParseJoinResponse(nn::pia::transport::ReceivedMessageAccessor const&amp;amp;)&amp;gt;&amp;lt;/code&amp;gt; with the ReceivedMessageAccessor buffer.&lt;br /&gt;
SetStationDataList will update state and immediately return if the join was denied. It will also validate the num_mesh_stations field against state. ParseJoinResponse also essentially verifies that the message was received from the host device.&lt;br /&gt;
&lt;br /&gt;
The input buffer size is ignored.&lt;br /&gt;
&lt;br /&gt;
The num_fragments field must be value 1 or &amp;lt;=3 otherwise it will return, there&#039;s two seperate code blocks handling these.&lt;br /&gt;
&lt;br /&gt;
Other than the checks at the start, there&#039;s no validation for the index fields. So large enough values could result in OOB-reads.&lt;br /&gt;
&lt;br /&gt;
When handling multiple fragments, it will loop through the stationinfo list. There is no validation for the u8 count field or the baseindex field. It calls a vfunc from obj baseptr+index*{entrysize} with data from the buffer, where index starts with the above baseindex field. Afterwards, an u8 is copied into an u32 array (with certain versions an u16 is deserialized into an u16 array).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nn::pia::session::ProcessUpdateMeshJob::UpdateStationDataList&amp;lt;/code&amp;gt; is (eventually) called from &amp;lt;code&amp;gt;nn::pia::session::MeshProtocol::ParseUpdateMesh&amp;lt;/code&amp;gt;, which has similar issues to the above.&lt;br /&gt;
&lt;br /&gt;
Note that ParseJoinResponse/ParseUpdateMesh essentially require the message to be received from the host device.&lt;br /&gt;
&lt;br /&gt;
With fixed versions (v5.18.98, exact version unknown) various validation was added. Additional/updated validation was added in a later version (v5.31.0, exact version unknown).&lt;br /&gt;
| OOB read/write / vfunc call where the object is selected by an OOB index, triggered by a Pia MeshProtocol message.&lt;br /&gt;
| v5.18.98 and v5.31.0 (exact versions unknown).&lt;br /&gt;
| v5.31.0&lt;br /&gt;
| November 18, 2022&lt;br /&gt;
| November 21, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Insecure encryption&lt;br /&gt;
| Originally Pia packets used AES-ECB encryption. As documented [https://github.com/Kinnay/NintendoClients/wiki/Pia-Overview here] it was later changed with v5.7.0 to AES-GCM. Each 0x10-byte block would have the same encrypted block output where the plaintext 0x10-byte data is the same.&lt;br /&gt;
The mechanism for generating the Pia SessionKey for LAN has also changed over time.&lt;br /&gt;
&lt;br /&gt;
The [https://github.com/Kinnay/NintendoClients/wiki/LAN-Protocol LAN] non-Pia-encapsulated packets were also originally sent in plaintext, however at some point it was changed to mostly encrypted.&lt;br /&gt;
| &lt;br /&gt;
| AES-GCM fix: v5.7.0&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| nn::pia::transport::UnreliableProtocol::Dispatch buffer overflow&lt;br /&gt;
| &amp;lt;code&amp;gt;nn::pia::transport::UnreliableProtocol::Dispatch&amp;lt;/code&amp;gt; memcpys data from the message into a list entry, without size validation. If the pia packet is the max size, it will only overwrite the 0xC-bytes which were written to immediately before the memcpy: the u32 size and the 8-byte StationAddress (depending on the version there can also be 4-byte padding after the size for alignment).&lt;br /&gt;
However, nn::pia::transport::UnreliableProtocol::Receive will clamp the size from the list entry to the outbuf size when doing the memcpy. So this is probably useless.&lt;br /&gt;
&lt;br /&gt;
It&#039;s unknown whether there&#039;s a version where more data could be overwritten, and whether that would be useful.&lt;br /&gt;
&lt;br /&gt;
This is fixed in v5.31.0, exact version unknown. The message is dropped if too large in Dispatch.&lt;br /&gt;
| Small buffer overflow triggered by a Pia UnreliableProtocol message.&lt;br /&gt;
| v5.31.0, exact version unknown.&lt;br /&gt;
| v5.18.98/v5.31.0&lt;br /&gt;
| November 2022&lt;br /&gt;
| November 29, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Uncleared input structs for [[LDN_services|LDN]]&lt;br /&gt;
| The Pia code using ldn CreateNetwork*/ConnectNetwork*/Scan doesn&#039;t properly memset the input data for SecurityConfig/ScanFilter (when keysize is less than 0x40 for the former). Hence, infoleak from games is sent to ldn (structs are located on stack, so stack data is leaked). This requires ldn compromise/mitm to obtain the leaked data - these are not sent over the network.&lt;br /&gt;
With v6.20.1 (exact version unknown - fix isn&#039;t present in v5.32.0), the code using Scan* now clears the input ScanFilter properly. With v6.25.1 (exact version unknown - fix isn&#039;t present in v6.23.3), the code using CreateNetwork*/ConnectNetwork* now clears the input SecurityConfig properly.&lt;br /&gt;
| Infoleak from games with LDN cmds, requires compromised sysmodule/mitm.&lt;br /&gt;
| v6.20.1 and v6.25.1, exact versions unknown.&lt;br /&gt;
| v5.32.0/v6.20.1/v6.23.3/v6.25.1&lt;br /&gt;
| &lt;br /&gt;
| December 7, 2022&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== ENL ===&lt;br /&gt;
This section documents vulnerabilities for [https://github.com/kinnay/NintendoClients/wiki/ENL-Protocol ENL].&lt;br /&gt;
A framework used by Nintendo games including Mario Kart 8 Deluxe, Splatoon 2 / 3, Mario Maker 2, and more.&lt;br /&gt;
&lt;br /&gt;
Fun fact, this library appears to re-use network code and concepts from older Nintendo titles such as Mario Kart 7 and some Wii multiplayer games.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in Enl version&lt;br /&gt;
!  Last Enl version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| enl::TransportManager::updateReceiveBuffer_() nullptr deref&lt;br /&gt;
| enl::TransportManager::updateReceiveBuffer_() is called when the ENL framework receives a PIA packet from a client, it will fully trust the ENL header which includes a &amp;quot;ContentTransporter&amp;quot; type (ID) and a length.&lt;br /&gt;
The function will try to fetch the content transporter by ID using &amp;lt;code&amp;gt;enl::TransportManager::getContentTransporter(unsigned char const &amp;amp;)&amp;lt;/code&amp;gt;, it returns NULL if there&#039;s no content transporter with the same ID&lt;br /&gt;
&lt;br /&gt;
*NOTE: The function may be inlined&lt;br /&gt;
&lt;br /&gt;
Then it will try to call a virtual method: &amp;lt;code&amp;gt;virtual size_t readyReceiveStream(enl::RamReadStream&amp;amp;, enl::Buffer*, size_t)&amp;lt;/code&amp;gt;, dereferencing the pointer to fetch the vtable ptr&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/Rambo6Glaz/c088e2ed7a12db08f6322e9f7a3c4911 Pseudocode of the function before it was fixed]&lt;br /&gt;
&lt;br /&gt;
| nullptr dereference triggered by an invalid content transporter type in the ENL header (it will crash the game/process)&lt;br /&gt;
| Unknown&lt;br /&gt;
| Depends on the game&lt;br /&gt;
| Early April 2022&lt;br /&gt;
| November 16, 2022&lt;br /&gt;
| [[User:Rambo6Glaz|Rambo6Glaz]], [https://github.com/kinnay Yannik] (massive RE help)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There&#039;s another one more interesting but it will have to wait a bit :)&lt;br /&gt;
&lt;br /&gt;
== Games ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Game&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Impact&lt;br /&gt;
!  Fixed in version&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Mario Kart World&lt;br /&gt;
| ASLR leak in application data&lt;br /&gt;
| A memory address can be leaked by changing your username to something short, and hosting a network session in LAN mode (press L + R + Left Stick on the main menu to enable this). The memory address can be found in bytes 12 - 19 of the application data that is transmitted after receiving a browse request.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note:&#039;&#039;&#039; there is more uninitialized data in the packet, but the memory address is probably the most interesting part. The vulnerability was fixed by clearing the application data with zeros, before filling in the information.&lt;br /&gt;
&lt;br /&gt;
[https://hackerone.com/reports/3463719 HackerOne report]&lt;br /&gt;
| A memory address can leaked (this is a requirement for many types of attacks).&lt;br /&gt;
| 1.5.0&lt;br /&gt;
| December 12, 2025&lt;br /&gt;
| February 19, 2026&lt;br /&gt;
| [https://github.com/kinnay Yannik]&lt;br /&gt;
|-&lt;br /&gt;
| Splatoon 3&lt;br /&gt;
| Predictable seed in anti-cheat system?&lt;br /&gt;
| [https://hackerone.com/reports/3042475 HackerOne report]&lt;br /&gt;
| Relatively easy way to bypass anti-cheat.&lt;br /&gt;
| ?&lt;br /&gt;
| Reported on March 17, 2025&lt;br /&gt;
| February 19, 2026&lt;br /&gt;
| hana2736&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Yannik</name></author>
	</entry>
	<entry>
		<id>https://switchbrew.org/w/index.php?title=Switch_System_Flaws&amp;diff=14185</id>
		<title>Switch System Flaws</title>
		<link rel="alternate" type="text/html" href="https://switchbrew.org/w/index.php?title=Switch_System_Flaws&amp;diff=14185"/>
		<updated>2025-12-13T17:20:06Z</updated>

		<summary type="html">&lt;p&gt;Yannik: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page is a list of publicly known Switch / Switch 2 (S2) flaws.&lt;br /&gt;
&lt;br /&gt;
= Hardware =&lt;br /&gt;
Flaws in this category pertain to the underlying hardware that powers the Switch.&lt;br /&gt;
&lt;br /&gt;
This includes components shared across Tegra based devices such as the [[TSEC]], the [[Security_Engine|Security Engine]], the [[GPU]] and so on.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Fixed with hardware model/revision&lt;br /&gt;
!  Newest hardware model/revision this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| GMMU DMA attack&lt;br /&gt;
| The Switch&#039;s GPU includes a separate MMU (GMMU) that is allowed to bypass the system&#039;s IOMMU (SMMU). By accessing the GPU&#039;s MMIO region and manipulating the page table entries in the GMMU, an attacker can read/write any portion of the DRAM (except memory carveouts).&lt;br /&gt;
&lt;br /&gt;
[5.0.0+] Works around this hardware flaw by using memory pool partitioning. You can no longer escalate into sysmodules with GPU DMA because all their memory is allocated using heap that&#039;s carved out.&lt;br /&gt;
&lt;br /&gt;
HAC-001-01 (Mariko/Tegra214/Tegra210b01): Fixes this by adding a new register which restricts what memory untranslated DMA requests may access. Untranslated GPU DMA may now only access the GPU carveout (physmem 0x80002000-0x80006000), which the GPU already has legitimate and exclusive access to.&lt;br /&gt;
| HAC-001-01 (Mariko/Tegra214/Tegra210b01)&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| Summer 2017&lt;br /&gt;
| December 28, 2017&lt;br /&gt;
| [[User:hexkyz|hexkyz]], [[User:SciresM|SciresM]] and [[User:qlutoo|qlutoo]]&lt;br /&gt;
|-&lt;br /&gt;
| Weak Security Engine context validation&lt;br /&gt;
| The Tegra X1 supports a &amp;quot;deep sleep&amp;quot; feature, where everything but DRAM and the PMC registers lose their content (and the SoC loses power). Upon awaking, the bootrom re-executes, restoring system state. Among these stored states is the Security Engine&#039;s saved state, which uses AES-128-CBC with a random key and all-zeroes IV. However, the bootrom doesn&#039;t perform a MAC on this data, and only validates the last block. This allows one to control most of security engine&#039;s state upon wakeup, if one has a way to modify the encrypted state buffer.&lt;br /&gt;
&lt;br /&gt;
With a way to modify the encrypted state buffer, one can thus dump keys from &amp;quot;write-only&amp;quot; keyslots, etc.&lt;br /&gt;
&lt;br /&gt;
This also bypasses the SBK protection of the bootROM: indeed, at warmboot, bootROM will always clear keyslot 0xE to prevent malicious code from saving the SBK. Moving the SBK to another keyslot in the saved context renders this protection moot.&lt;br /&gt;
&lt;br /&gt;
HAC-001-01 (Mariko/Tegra214/Tegra210b01): Fixes this by streamlining the context save process; security engine contexts are now saved to protected memory which the CPU cannot access or modify.&lt;br /&gt;
| HAC-001-01 (Mariko/Tegra214/Tegra210b01)&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| December 2017&lt;br /&gt;
| January 20, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]] and [[User:motezazer|motezazer]]&lt;br /&gt;
|-&lt;br /&gt;
| Security Engine keyslots vulnerable to partial overwrite attack&lt;br /&gt;
| &lt;br /&gt;
The Tegra X1 security engine supports writing keyslot data to the engine with syntax as follows: &lt;br /&gt;
&lt;br /&gt;
SECURITY_ENGINE-&amp;gt;AES_KEYTABLE_ADDR = (keyslot &amp;lt;&amp;lt; 4) | (dword_index_in_keyslot); &lt;br /&gt;
&lt;br /&gt;
SECURITY_ENGINE-&amp;gt;AES_KEYTABLE_DATA = readle32(key, dword_index_in_keyslot * 4); &lt;br /&gt;
&lt;br /&gt;
However, the Security Engine flushes writes to the internal key tables immediately when AES_KEYTABLE_DATA is written -- this allows one to overwrite a single dword of a key at a time, and thus brute force the contents of keyslots in time (2^32 * 8) = 2^35 instead of 2^256.&lt;br /&gt;
| None&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| Theorized Summer 2017 due to suggestive syntax, confirmed April 9, 2018&lt;br /&gt;
| April 9, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]], almost surely others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| CVE-2018-6242 (leveraged by the ShofEL2 and Fusée Gelée exploits)&lt;br /&gt;
| The USB software stack provided inside the boot instruction rom (IROM/bootROM) contains a copy operation whose length can be controlled by an attacker. By carefully constructing a USB control request, an attacker can leverage this vulnerability to copy the contents of an attacker-controlled buffer over the active execution stack, gaining control of the Boot and Power Management processor (BPMP) before any lock-outs or privilege reductions occur. This execution can then be used to exfiltrate secrets and to load arbitrary code onto the main CPU Complex (CCPLEX) &amp;quot;application processors&amp;quot; at the highest possible level of privilege (typically as the TrustZone Secure Monitor at PL3/EL3).&lt;br /&gt;
| HAC-001-01 (Mariko/Tegra214/Tegra210b01) (also fixed independently on Tegra186).&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| January 2018&lt;br /&gt;
| April 23, 2018&lt;br /&gt;
| [[User:Shuffle2|shuffle2]] and fail0verflow (originally),&amp;lt;br&amp;gt; [[User:Ktemkin|ktemkin]] and ReSwitched Team (independently),&amp;lt;br&amp;gt; [[User:Naehrwert|naehrwert]] (independently),&amp;lt;br&amp;gt; [[User:Hexkyz|hexkyz]] (independently),&amp;lt;br&amp;gt; st4rk with [[User:Shinyquagsire23|Shiny Quagsire]] and Dazzozo (independently),&amp;lt;br&amp;gt; and many others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| Poor validation of bootrom SDRAM configuration parameters leads to arbitrary writes in bootrom&lt;br /&gt;
| &lt;br /&gt;
The Tegra X1 bootrom supports saving SDRAM parameters to scratch registers, and using the saved configuration to enable DRAM during warmboot.&lt;br /&gt;
&lt;br /&gt;
The code that parses these parameters does if (params-&amp;gt;EmcBctSpareN) *params-&amp;gt;EmcBctSpareN = params-&amp;gt;EmcBctSpareNPlusOne for most N, without validating either the address or value written to it.&lt;br /&gt;
There are other arbitrary writes in this code, as well (e.g. BootromPatch parameters intended for patching MISC registers do not check a relative offset to 0x7000000, etc).&lt;br /&gt;
&lt;br /&gt;
This allows a user with access to the PMC registers (via pre-sleep bpmp execution, or otherwise) to gain arbitrary bootrom code execution.&lt;br /&gt;
&lt;br /&gt;
HAC-001-01 (Mariko/Tegra214/Tegra210b01): Fixes this by validating that the spare writes/bootrom patch before performing them.&lt;br /&gt;
| HAC-001-01 (Mariko/Tegra214/Tegra210b01)&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| 2017&lt;br /&gt;
| December 16, 2018&lt;br /&gt;
| Everyone (independently).&lt;br /&gt;
|-&lt;br /&gt;
| TSEC ROM does not clear crypto registers after signature verification&lt;br /&gt;
|&lt;br /&gt;
TSEC supports executing signed-microcode at a greater privilege level than normal payloads.&lt;br /&gt;
&lt;br /&gt;
When jumping to signed microcode, the caller is expected to load hardware crypto register $c6 = &amp;lt;signature&amp;gt;, $c7 = &amp;lt;seed (zero for all officially-signed microcode)&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
TSEC ROM then calculates the expected signature and compares it to the user-supplied one in $c6. On match, the secure payload is executed, and on failure an exception is raised.&lt;br /&gt;
&lt;br /&gt;
However, TSEC ROM fails to clear the crypto registers used to calculate the expected signature in either of the success/failure cases.&lt;br /&gt;
&lt;br /&gt;
Thus, with some way of obtaining the contents of crypto registers (e.g. ROP under some secure payload), an attacker can dump intermediary values from signature calculation.&lt;br /&gt;
&lt;br /&gt;
With enough data/trial/error, this is enough to reconstruct the signature algorithm:&lt;br /&gt;
* mac = &amp;lt;davies meyer hash of (page || address of page) for each 0x100 page in the payload&amp;gt;&lt;br /&gt;
* key = AES-ENCRYPT(hardware csecret 0x1, seed)&lt;br /&gt;
* signature = AES-ENCRYPT(key, mac)&lt;br /&gt;
&lt;br /&gt;
| None&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| Late 2018/Early 2019&lt;br /&gt;
| August 2020&lt;br /&gt;
| [[User:qlutoo|qlutoo]]/[[User:Hexkyz|hexkyz]]/[[User:Shuffle2|shuffle2]], [[User:SciresM|SciresM]]/[[User:motezazer|motezazer]] (independently).&lt;br /&gt;
|-&lt;br /&gt;
| TSEC signature validation design flaw leads to fake-signing&lt;br /&gt;
|&lt;br /&gt;
As mentioned above, when jumping to signed microcode the caller is expected to load hardware crypto register $c6 = &amp;lt;signature&amp;gt;, $c7 = &amp;lt;seed (zero for all officially-signed microcode)&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
However, TSEC ROM performs no validation on the input seed used to generate the signing key.&lt;br /&gt;
&lt;br /&gt;
This leads to the following attack:&lt;br /&gt;
* Attacker gains rop under any secure microcode payload with signature = S.&lt;br /&gt;
* Attacker uses the &amp;quot;csigenc&amp;quot; instruction to obtain K = AES-ENCRYPT(hardware csecret 0x1, S).&lt;br /&gt;
* Attacker jumps to their own microcode with $c6 = &amp;lt;signature calculated on pc using K&amp;gt;, $c7 = S&lt;br /&gt;
* TSEC ROM calculates key = AES-ENCRYPT(hardware csecret 0x1, S) = K, and the signature check passes.&lt;br /&gt;
* Attackers microcode is executed in secure mode as though it were signed by NVidia.&lt;br /&gt;
&lt;br /&gt;
Thus an attacker who has exploited *any* secure payload may use this to obtain a &amp;quot;fake signature key&amp;quot;, which can be used to sign and execute arbitrary microcode in secure mode.&lt;br /&gt;
&lt;br /&gt;
Note: this does not break the TSEC cryptosystem, as the csigenc mechanism relies on the signature of the executing microcode, and fakesigning produces different signatures from NVidia that cannot be controlled.&lt;br /&gt;
| None&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| Late 2018/Early 2019&lt;br /&gt;
| August 2020&lt;br /&gt;
| [[User:qlutoo|qlutoo]]/[[User:Hexkyz|hexkyz]]/[[User:Shuffle2|shuffle2]], [[User:SciresM|SciresM]]/[[User:motezazer|motezazer]] (independently).&lt;br /&gt;
|-&lt;br /&gt;
| ROP under TSEC secure bootrom via DMA engine stack overwrite (--xploit)&lt;br /&gt;
| TSEC DMA engine does not stop when entering TSEC secure bootrom. By pointing TSEC DMA to current stack before secure bootrom entry, stack can be controlled. &lt;br /&gt;
&lt;br /&gt;
One can then use blind ROP against the TSEC secure bootrom (which is execute only, and cannot be dumped).&lt;br /&gt;
&lt;br /&gt;
With sufficient effort, an attacker can construct a ROP chain that leads to csigcmp being executed with fully controlled arguments.&lt;br /&gt;
&lt;br /&gt;
This allows for arbitrary heavy secure mode code execution with the current signature set to an arbitrary value.&lt;br /&gt;
&lt;br /&gt;
This completely breaks the TSEC cryptosystem, by allowing one to obtain the result of csigenc with signature = &amp;lt;any desired value&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
This has many uses/results, notably including dumping the &amp;quot;true&amp;quot; signature key (set signature = zeroes, perform csigenc using csecret 0x1).&lt;br /&gt;
| None&lt;br /&gt;
| TSEC for all Tegra devices&lt;br /&gt;
| Late 2018&lt;br /&gt;
| January 2021&lt;br /&gt;
| [[User:Hexkyz|hexkyz]]/[[User:SciresM|SciresM]], [[User:Vale|Vale]]/[[User:Thog|Thog]] (independently), [[User:Tatsuko|Tatsuko]] (independently), possibly others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| Boot straps are not relatched on watchdog resets (strapwn)&lt;br /&gt;
| On boot, the BOOTSELECT, RCM and RAM_CODE straps are latched from external GPIO to determine which boot medium to use and verify from in bootrom. However, APB_MISC_PP_STRAPPING_OPT_A can be overwritten with arbitrary values following bootrom. Write access to PP_STRAPPING_OPT_A would otherwise be mundane, however these straps are not relatched during a watchdog reset (despite being latched during other software resets), allowing for arbitrary straps to be selected and executed in bootrom.&lt;br /&gt;
&lt;br /&gt;
This allows setting NVPROD_UART on some hardware configurations where it would normally be unavailable (ie on Jetson Nano boards), but is otherwise mostly useless and/or useful for testing unintended boot options (such as USB Mass Storage boot) without having to move boot strap resistors.&lt;br /&gt;
| Unknown&lt;br /&gt;
| HAC-001 (Tegra210)&lt;br /&gt;
| May 2020&lt;br /&gt;
| April 30, 2021&lt;br /&gt;
| [[User:Shinyquagsire23|Shiny Quagsire]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Firmware =&lt;br /&gt;
Flaws in this category pertain to the firmware running on hardware devices, such as wifi/bluetooth, etc. Firmware is generally uploaded by sysmodules.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Broadpwn (CVE-2017-9417)&lt;br /&gt;
| See [https://blog.exodusintel.com/2017/07/26/broadpwn/ here] and [https://www.blackhat.com/docs/us-17/thursday/us-17-Artenstein-Broadpwn-Remotely-Compromising-Android-And-iOS-Via-A-Bug-In-Broadcoms-Wifi-Chipsets.pdf here].&lt;br /&gt;
| Code execution on the wifi controller (untested on Switch).&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| Switch: July 2022&lt;br /&gt;
| Switch: July 30, 2022&lt;br /&gt;
| Switch: [[User:Yellows8|yellows8]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Software =&lt;br /&gt;
== Bootloader ==&lt;br /&gt;
Flaws in this category pertain to any bootloader component such as the [[Package1#Package1ldr|package1ldr]], the [[Package1#Section_1|NX bootloader]] or the [[Package1#Section_0|warmboot binary]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Null-dereference in panic()&lt;br /&gt;
| The Switch&#039;s stage 1 bootloader, on panic(), clears the stack and then attempts to clear the Security Engine. However, it does so by dereferencing a pointer to the SE in .bss (initially NULL), and this pointer doesn&#039;t get initialized until partway into the bootloader&#039;s main() after several functions that might panic() are called. Thus, a panic() caused prior to SE initialization would result in the SE pointer still being NULL when dereferenced. &lt;br /&gt;
The BPMP doesn&#039;t have an active MPU and the bus won&#039;t data abort on an invalid address, so no exception will be entered: it&#039;ll end up overwriting some exception vectors with NULL before halting.&lt;br /&gt;
&lt;br /&gt;
In 3.0.0, this was fixed by moving the security engine initialization earlier in main(), before the first function that could potentially panic().&lt;br /&gt;
| Some exception vectors overwritten with NULL, before SBK/other keyslots are cleared. Probably useless for anything more interesting.&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| Early July, 2017&lt;br /&gt;
| July 30, 2017&lt;br /&gt;
| Everyone who diff&#039;d 2.3.0 and 3.0.0 Package1&lt;br /&gt;
|-&lt;br /&gt;
| FUSE_DIS_PGM not written by package1 &lt;br /&gt;
| The switch&#039;s hardware fuse driver contains a write-once bit in a register called &amp;quot;FUSE_DIS_PGM&amp;quot;, which disables burning fuses until the next reboot. While Nintendo&#039;s bootloader code for waking up from sleep writes this on all firmware, the actual package1 initial bootloader forgets to write to it on cold reboot. &lt;br /&gt;
&lt;br /&gt;
This isn&#039;t too big of a problem because another fuse is burnt on retail devices (production mode), which prevents burning *all* fuses other than ODM_RESERVED ones in hardware.&lt;br /&gt;
&lt;br /&gt;
This was fixed in 3.0.0 by writing to the register on cold boot (although the write happens in TZ instead of package1 where it should take place, possibly to obfuscate the fact that they made this mistake).&lt;br /&gt;
| Burning arbitrary ODM reserved fuses with TZ code execution, which should never be possible for non-bootloader code.&lt;br /&gt;
&lt;br /&gt;
Warning: one could irreparably brick one&#039;s console by playing with this.&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| Late summer/early fall 2017&lt;br /&gt;
| December 31, 2017&lt;br /&gt;
| [[User:SciresM|SciresM]], [[User:motezazer|motezazer]]&lt;br /&gt;
|-&lt;br /&gt;
| maconstack (TSEC firmware leaves MAC on the stack)&lt;br /&gt;
| Package1ldr loads a firmware blob into TSEC early on boot. This piece of code runs on the TSEC in Authenticated Mode and has the sole purpose of generating the per-console TSEC key (see [[Cryptosystem]]).&lt;br /&gt;
&lt;br /&gt;
As a way to mitigate attacks, the TSEC firmware blob is split into 3 stages: [[TSEC_Firmware#Boot|Boot]] which is unencrypted and unsigned, [[TSEC_Firmware#KeygenLdr|KeygenLdr]] which is unencrypted but signed and [[TSEC_Firmware#Keygen|Keygen]] which is encrypted and signed.&lt;br /&gt;
Boot loads a static pre-generated signature into the Falcon&#039;s CPU crypto registers, loads KeygenLdr into the Falcon&#039;s CODE region and jumps to it. Execution will proceed into KeygenLdr in Heavy Secure Mode if, and only if, the loaded signature matches the one Falcon calculates internally for KeygenLdr.&lt;br /&gt;
&lt;br /&gt;
Among various things, KeygenLdr will attempt to do a &amp;quot;backwards&amp;quot; security check by calculating a CMAC over Boot and comparing it with a known hash stored in the TSEC firmware&#039;s key data (a small buffer stored after Boot&#039;s code). If the hashes don&#039;t match, execution aborts.&lt;br /&gt;
&lt;br /&gt;
KeygenLdr stores the calculated Boot&#039;s CMAC in the stack, but forgets to clear it. Since the stack is located in Falcon&#039;s DATA region, loading the TSEC firmware blob and dumping the DATA region afterwards (via MMIO) will reveal the calculated hash.&lt;br /&gt;
This allows using KeygenLdr as an oracle to generate a valid CMAC for arbitrary Boot code. Replacing the CMAC in the TSEC firmware&#039;s key data region results in KeygenLdr accepting any Boot code, thus rendering this security measure useless.&lt;br /&gt;
&lt;br /&gt;
Additionally, since signed Falcon code can&#039;t be revoked without an hardware revision, an attacker can always reuse the flawed KeygenLdr code even if a fix is issued.&lt;br /&gt;
| Running TSEC firmware&#039;s KeygenLdr in a user controlled environment.&lt;br /&gt;
| None&lt;br /&gt;
| [[5.0.2]]&lt;br /&gt;
| January 2018&lt;br /&gt;
| April 29, 2018&lt;br /&gt;
| [[User:Hexkyz|hexkyz]], [[User:Rei|Reisyukaku]] (independently), probably others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| pk1ldrhax&lt;br /&gt;
| Package1ldr decrypts and verifies the keyblob inside of the current BCT in order to get the package1 key, and then uses the package1 key to decrypt package1. It then validates package1 before jumping to it by checking the PK11 magic number, and that the section sizes sum to the expected size (and are individually less than the expected size). &lt;br /&gt;
&lt;br /&gt;
However, package1ldr does not actually validate the package1 key against a fixed vector (much like kernel9loader forgot to do so on the 3ds). This would normally not matter, as keyblobs are validated -- however, with bootrom code execution one can dump SBK and forge keyblobs, and thus control the package1 key. &lt;br /&gt;
&lt;br /&gt;
Thus (&#039;&#039;&#039;in theory, but not in practice due to the size of the brute force required&#039;&#039;&#039;) one can replace the package1 key with garbage, causing package1 to decrypt into garbage, and hope that this garbage passes validation checks and that package1ldr jumping into the garbage will do something useful.&lt;br /&gt;
&lt;br /&gt;
This was fixed incidentally in [[6.2.0]], as pk1ldr does not use keyblob data to decrypt package1 any more.&lt;br /&gt;
&lt;br /&gt;
| With a large enough brute force: arbitrary package1 code execution from coldboot.&lt;br /&gt;
&lt;br /&gt;
However, a usable brute force is on the order of &amp;gt;= ~2^80, so &#039;&#039;&#039;this is almost certainly not actually usable in any meaningful context&#039;&#039;&#039;.&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| Early 2017 (as soon as plaintext package1ldr was first dumped)&lt;br /&gt;
| November 20, 2018&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Stack smash in TSEC firmware&#039;s KeygenLdr&lt;br /&gt;
| Given that we can control the [[TSEC_Firmware#Key_data|key data]] (which is not authenticated) and the [[TSEC_Firmware#Boot|Boot]] blob (see &amp;quot;maconstack&amp;quot;), as well as the fact Non-secure and Heavy Secure code share the same stack, we can use this to attack KeygenLdr. KeygenLdr uses memcpy to copy over a payload to DMEM to verify it, which can be abused to smash the stack (in DMEM) and write over the return address of said function.&lt;br /&gt;
| ROP under KeygenLdr in Heavy Secure mode.&lt;br /&gt;
| None&lt;br /&gt;
| [[8.0.1]]&lt;br /&gt;
| Early 2018&lt;br /&gt;
| May 21, 2019&lt;br /&gt;
| Everyone (independently).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== TrustZone ==&lt;br /&gt;
Flaws in this category pertain exclusively to the [[Package1#Section_2|Secure Monitor]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Non-atomic mutexes&lt;br /&gt;
| When an [[SMC]] is called, TrustZone sets a global variable to mark that an SMC is in progress, so that two SMCs using shared resources (like the security engine) do not trample on one another. On 1.0.0, this global variable was written using non-atomic writes, and thus a race condition is possible.&lt;br /&gt;
&lt;br /&gt;
However, the SMC handler enforces that all SMCs must be called from core #3, unless the top-level handler ID is 1 (SMCs internal to the kernel). Thus, the only SMCs that can be run side-by-side are [any userland smc] and smcGetRandomBytesForKernel, and this turns out to not really be abusable.&lt;br /&gt;
| Mostly useless. Maybe some oob-write into unused (and thus useless) memory if running smcGetRandomBytesForKernel and smcGetRandomBytesForUser at the same time.&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| December 2017 (Probably earlier by others)&lt;br /&gt;
| January 18, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]], probably others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| jamais vu (non-secure world access to PMC MMIO and pre-deep sleep firmware)&lt;br /&gt;
| On [[1.0.0]], one could map in the PMC registers in userland. In addition, [[AM_services|am]] ran a little-kernel based firmware on the BPMP at runtime. With code execution under am, one could modify the BPMP&#039;s little-kernel firmware to hook deep sleep entry, and modify TrustZone/Security engine state. &lt;br /&gt;
&lt;br /&gt;
This was fixed in [[2.0.0]] by making the PMC secure-world only, blacklisting the BPMP&#039;s exception vectors from being mapped, and thoroughly checking for malicious behavior on deep sleep entry.&lt;br /&gt;
| Arbitrary TrustZone code execution.&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| December, 2017&lt;br /&gt;
| January 20, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]] and [[User:motezazer|motezazer]]&lt;br /&gt;
|-&lt;br /&gt;
| Missed BPMP Exception Vector Writes&lt;br /&gt;
| Starting in [[2.0.0]], the BPMP is asleep at runtime, and is turned on by TrustZone during [[SMC|smcCpuSuspend]] in order to initiate the deep sleep process. When it does so, it is held in RESET, and TrustZone attempts to write to the BPMP exception vectors at 0x6000F200 to register EVP_RESET = lp0_entry_fw_crt0, and all other EVPs to a function that simply reboots. However, while they successfully write EVP_RESET, they miss all the other vectors, accidentally writing to the 0x6000F004-0x6000F020 region instead of the 0x6000F204-0x6000F220 region they want to write to. This results in all the exception vectors for the BPMP other than RESET being &amp;quot;undefined&amp;quot; (attacker controlled).&lt;br /&gt;
&lt;br /&gt;
With some way of causing an exception vector to be taken at the right time, this would give pre-sleep code execution (and thus arbitrary TrustZone code execution, via the security engine flaw). However, none of the abort vectors are really triggerable, and interrupts are disabled for the BPMP when it is taken out of reset. Thus, this is useless in practice.&lt;br /&gt;
&lt;br /&gt;
This was fixed in [[4.0.0]] by writing to the correct registers.&lt;br /&gt;
| Theoretically: Arbitrary TrustZone code execution. In practice: Useless.&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| January, 2018&lt;br /&gt;
| February 23, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]] and [[User:motezazer|motezazer]], [[User:Naehrwert|naehrwert]], [[User:Hexkyz|hexkyz]], probably others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| TSEC has access to the secure kernel carveout &lt;br /&gt;
| TrustZone is responsible for managing security carveouts to prevent DMA controllers from accessing the carveout which contains the kernel, sysmodules, and other critical operating system data.&lt;br /&gt;
&lt;br /&gt;
Until [[8.0.0]], the list of devices that could access the carveout included the TSEC. However, the TSEC can bypass the SMMU when in authenticated mode by writing to a certain register. Thus, pwning nvservices would allow one to take over the TSEC, and use it to write to normally protected mmio/memory.&lt;br /&gt;
&lt;br /&gt;
In [[8.0.0]], this was fixed by removing TSEC access, and adding TSECB access (TSECB cannot bypass the SMMU).&lt;br /&gt;
| With access to the TSEC mmio (nvservices ROP) and code execution in TSEC Heavy Secure mode, kernel code execution, probably.&lt;br /&gt;
| [[8.0.0]]&lt;br /&gt;
| [[8.0.0]]&lt;br /&gt;
| 2017 (when TrustZone code plaintext was first obtained).&lt;br /&gt;
| April 15, 2019&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| deja vu (insufficient system state validation on suspend leads to pre-sleep BPMP code execution)&lt;br /&gt;
| Jamais Vu was fixed in [[2.0.0]] by making the PMC secure-world only, blacklisting the BPMP&#039;s exception vectors from being mapped, and thoroughly checking for malicious behavior on deep sleep entry, since gaining pre-sleep code execution on the BPMP compromises the system.&lt;br /&gt;
&lt;br /&gt;
However, the state validation performed by Nintendo&#039;s Secure Monitor was insufficient to prevent pre-sleep execution from being obtained.&lt;br /&gt;
&lt;br /&gt;
Prior to [[6.0.0]], one could use a DMA controller that had access to IRAM and was not held in reset (there were multiple) to race TrustZone&#039;s writes to the BPMP firmware in IRAM, and thus overwrite Nintendo&#039;s firmware with an attacker&#039;s to gain pre-sleep code execution.&lt;br /&gt;
&lt;br /&gt;
[[6.0.0]] addressed this by performing TrustZone state MAC writes and locking PMC scratch *before* turning on the BPMP, fixing the original Jamais Vu exploit entirely. In addition, the BPMP firmware in TrustZone&#039;s .rodata is now memcmp&#039;d to the actual data after it is written to IRAM. This mitigates race attacks that modify the firmware.&lt;br /&gt;
&lt;br /&gt;
However, Nintendo both forgot to validate the BPMP exception vectors after writing them, and forgot to hold in reset a DMA controller that can write to the BPMP&#039;s exception vectors.&lt;br /&gt;
&lt;br /&gt;
AHB-DMA is not blacklisted by kernel mapping whitelist (Nintendo probably forgot it, because the TX1 TRM does not really document that it&#039;s present, although the MMIO works as documented in older (Tegra 3 and before) TRMs).&lt;br /&gt;
&lt;br /&gt;
Thus, with kernel code execution (or some other way of accessing AHB-DMA, e.g. nspwn on &amp;lt;= 4.1.0, TSEC hax, or other arbitrary mmio access flaws), one can DMA to the BPMP&#039;s exception vectors as they are written, causing TrustZone to start the BPMP executing an attacker&#039;s firmware at a different location than TrustZone intends/validates.&lt;br /&gt;
&lt;br /&gt;
This was fixed in [[8.0.0]] by blocking AHB-DMA arbitration and verifying it is held in reset during suspend, and thus there are no more devices that can write to the relevant MMIO at the right time.&lt;br /&gt;
&lt;br /&gt;
| Arbitrary TrustZone/BootROM code execution, by using either the original Jamais Vu flaw (prior to [[6.0.0]] or a warmboot bootrom exploit (any firmware where pre-sleep execution can be gained).&lt;br /&gt;
| [[8.0.0]]&lt;br /&gt;
| [[8.0.0]]&lt;br /&gt;
| December 2017&lt;br /&gt;
| April 15, 2019&lt;br /&gt;
| [[User:SciresM|SciresM]], [[User:motezazer|motezazer]] and ktemkin,  [[User:Naehrwert|naehrwert]] (independently), almost certainly others (independently)&lt;br /&gt;
|-&lt;br /&gt;
| TrustZone allows using imported RSA exponents with arbitrary modulus&lt;br /&gt;
| TrustZone supports &amp;quot;importing&amp;quot; RSA private exponents for use by userland -- these are stored encrypted with TrustZone only keydata in NAND, and decrypted only to TZRAM. This prevents a console that has compromised userland from learning the private exponents of these keys and doing calculations with them offline. In practice, this is used for FS (gamecard communications), ES (drm), and SSL (console client cert communications).&lt;br /&gt;
&lt;br /&gt;
However, the actual SMC API only imports the RSA exponent, and not the modulus, which is passed separately by userland in each call. There is no validation done on the modulus passed in -- this means that userland can pass in any message and modulus it chooses, and obtain the result of (message ^ private exponent) % modulus back from the secure monitor.&lt;br /&gt;
&lt;br /&gt;
By choosing a prime number modulus P such that P has &amp;quot;smooth&amp;quot; order (totient(P) == P-1 is divisible only by &amp;quot;small&amp;quot; primes), one can efficiently use the [[wikipedia:Pohlig-Hellman algorithm|Pohlig-Hellman algorithm]] to calculate the discrete logarithm of such a result directly, and thus obtain the private exponent.&lt;br /&gt;
&lt;br /&gt;
This is mostly useless in practice, given the general availability of other exploits to obtain these decrypted exponents.&lt;br /&gt;
&lt;br /&gt;
This was fixed in 10.0.0 by importing the modulus in addition to the exponent for the ES device key and ES client cert key. For backwards compatibility reasons the SSL key and Lotus key still only import the exponent.&lt;br /&gt;
&lt;br /&gt;
StorageExpMod also now validates that the exponentiation of &amp;quot;DDDDD...&amp;quot; about the provided modulus by the imported exponent and then the fixed public exponent returns &amp;quot;DDDDD...&amp;quot;, and returns invalid argument if validation fails.&lt;br /&gt;
| With userland privileges sufficient to use an imported RSA key: obtaining that RSA key&#039;s private exponent.&lt;br /&gt;
| [[10.0.0]]&lt;br /&gt;
| [[10.0.0]]&lt;br /&gt;
| August 14, 2019&lt;br /&gt;
| August 14, 2019&lt;br /&gt;
| [[User:SciresM|SciresM]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Kernel ==&lt;br /&gt;
Flaws in this category pertain exclusively to the [[Package2#Section_0|HorizonOS Kernel]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Syscall Infoleaks&lt;br /&gt;
| Many syscalls leaked kernel pointers on sad paths (for example svcSetHeapSize and svcQueryMemory), until they landed a bunch of fixes in 2.0.0.&lt;br /&gt;
| Nothing really.&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| &lt;br /&gt;
| 2017&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| svcWaitSynchronization/svcReplyAndReceive bad cleanup on error&lt;br /&gt;
| If there is a page fault when fetching handles from the userspace array, it cleans up by dereferencing all objects despite having only loaded first N. Allows the attacker to make arbitrary decrefs on any kernel synchronization object, and thus can be used to get UAF. Haven&#039;t actually been tried on real HW though, but should work (tm).&lt;br /&gt;
| Kernel code execution&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| &lt;br /&gt;
| April 24, 2017&lt;br /&gt;
| [[User:qlutoo|qlutoo]]&lt;br /&gt;
|-&lt;br /&gt;
| Bad irq_id check in CreateInterruptEvent&lt;br /&gt;
| CreateInterruptEvent syscall is designed to work only for irq_id &amp;gt;= 32. All irq_ids &amp;lt; 32 are &amp;quot;per-core&amp;quot; and reserved for kernel use (watchdog/scheduling/core communications).&lt;br /&gt;
On 1.0.0 you could supply irq_id &amp;lt; 32 and it would write outside the SharedIrqs table.&lt;br /&gt;
| You can register irq&#039;s in the Core3Irqs table, and thus register per-core irqs for core3, that are normally reserved for kernel. Useless.&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| October 2017&lt;br /&gt;
| October 17, 2017&lt;br /&gt;
| [[User:qlutoo|qlutoo]]&lt;br /&gt;
|-&lt;br /&gt;
| Kernel .text mapped executable in usermode&lt;br /&gt;
| Prior to [[3.0.2]] the kernel .text was [[Memory_layout|mapped]] in usermode as executable. This can be used for usermode ROP for bypassing ASLR, but SVCs/IPC are not usable by running kernel .text in usermode.&lt;br /&gt;
| Executing kernel .text in usermode&lt;br /&gt;
| [[3.0.2]]&lt;br /&gt;
| [[3.0.2]]&lt;br /&gt;
| &lt;br /&gt;
| December 28, 2017 (34c3)&lt;br /&gt;
| [[User:qlutoo|qlutoo]]&lt;br /&gt;
|-&lt;br /&gt;
| Memory Controller not properly secured&lt;br /&gt;
| The Switch OS originally had the memory controller not set to be accessible only by the secure-world, which was problematic because insecure access can compromise the kernel.&lt;br /&gt;
&lt;br /&gt;
This was fixed partially in [[2.0.0]] by blacklisting the memory controller from being mapped by user-processes, and was fixed entirely in [[4.0.0]] by making the memory controller TZ-only and making all kernel accesses go through [[SMC|smcReadWriteRegister]].&lt;br /&gt;
| With some way to access the memory controller MMIO, arbitrary kernel code execution.&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| January 2018&lt;br /&gt;
| January 2018&lt;br /&gt;
| [[User:SciresM|SciresM]], [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Potential [[SVC|svcWaitForAddress]] thread use-after-free&lt;br /&gt;
| Between [[4.0.0]], where svcWaitForAddress was introduced, and [[7.0.0]], there was a second intrusive rbtree node in KThread for the WaitForAddress tree (the key being (address, priority), sorted lexicographically). Unlike the WaitProcessWideKeyAtomic tree, the kernel forgot to reinsert the WaitForAddress node when the thread&#039;s priority changed (priority inheritance and/or SetPriority), breaking the rbtree invariants; and since the kernel walks through the entire tree to remove intrusive nodes, you could cause threads to stay in the tree even after their deletion.&lt;br /&gt;
&lt;br /&gt;
[[7.0.0]] fixed the issue by using the same intrusive node for both trees. The thread/node knows which tree it is in, and the latter is correctly updated when thread priority changes.&lt;br /&gt;
| It unluckily didn&#039;t look exploitable&lt;br /&gt;
| [[7.0.0]]&lt;br /&gt;
| [[7.0.0]]&lt;br /&gt;
| July 2018&lt;br /&gt;
| February 2019&lt;br /&gt;
| [[User:TuxSH|TuxSH]]&lt;br /&gt;
|-&lt;br /&gt;
| Kernel RWX identity mapping never unmapped&lt;br /&gt;
| During init, the kernel binary is identity-mapped as RWX at 0x80060000; this is necessary to facilitate the transitionary period while the MMU is being enabled but mappings for e.g. KASLR are not yet determined, and also to enable smooth MMU enable transition during wake-from-sleep.&lt;br /&gt;
&lt;br /&gt;
However, the identity mapping was never unmapped, and thus the whole kernel code bin remained permanently mapped as RWX for all kernel threads (any thread which does not have an owner process and thus uses the KSupervisorPageTable TTBR0).&lt;br /&gt;
&lt;br /&gt;
Thus, any theoretical exploit which would give kernel memory corruption or ROP under a kernel thread would allow making use of this mapping to modify kernel text + bypass KASLR.&lt;br /&gt;
&lt;br /&gt;
This was fixed in [[16.0.0]] by unmapping the identity-mapping during init, and re identity-mapping only the very first page of kernel .text as R-X (for use by wake-from-sleep), which fixes the shellcode problem and mostly fixes the ROP problem, since this page mostly lacks interesting gadgets.&lt;br /&gt;
| In theory, with another exploitable kernel memory corruption (or ROP under kernel thread) bug: bypassing KASLR + modifying kernel .text. &lt;br /&gt;
&lt;br /&gt;
However, no such bugs are known.&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| Summer 2018&lt;br /&gt;
| February 2023&lt;br /&gt;
| Everyone&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== BootImagePackage System Modules ==&lt;br /&gt;
Flaws in this category pertain to any of the [[Package2#Section_1|built-in system modules]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Service access control bypass (sm:h, smhax, probably other names)&lt;br /&gt;
| Prior to [[3.0.1]], the &#039;&#039;service manager&#039;&#039; (sm) built-in system module treats a user as though it has full permissions if the user creates a new &amp;quot;sm:&amp;quot; port session but bypasses [[Services_API#Initialize|initialization]]. This is due to the other sm commands skipping the service ACL check for Pids &amp;lt;= 7 (i.e. all kernel bundled modules) and that skipping the initialization command leaves the Pid field uninitialized.&lt;br /&gt;
In [[3.0.1]], sm returns error code 0x415 if [[Services_API#Initialize|Initialize]] has not been called yet.&lt;br /&gt;
| Acquiring, registering, and unregistering arbitrary services&lt;br /&gt;
| [[3.0.1]]&lt;br /&gt;
| [[3.0.1]]&lt;br /&gt;
| May 2017&lt;br /&gt;
| August 17, 2017&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Overly permissive SPL service&lt;br /&gt;
| The concept behind the switch&#039;s [[SMC|Secure Monitor]] is that all cryptographic keydata is located in userspace, but stored as &amp;quot;access keys&amp;quot; encrypted with &amp;quot;keks&amp;quot; that never leave TrustZone. The [[SPL services|spl]] (&amp;quot;security processor liaison&amp;quot;?) service serves as an interface between the rest of the system and the secure monitor. Prior to [[4.0.0]], spl exposed only a single service &amp;quot;spl:&amp;quot;, which provided all TrustZone wrapper functions to all sysmodules with access to it. Thus anyone with access to the spl: service (via smhax or by pwning a sysmodule with access) could do crypto with any access keys they knew. &lt;br /&gt;
&lt;br /&gt;
This was fixed in [[4.0.0]] by splitting spl: into spl:, spl:mig, spl:ssl, spl:es, and spl:fs.&lt;br /&gt;
| Arbitrary spl: crypto with any access keys one knows. For example, one could use the SSL module&#039;s access keys to decrypt their console&#039;s SSL certificate private key without having to pwn the SSL sysmodule.&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| Summer 2017 (after smhax was discovered).&lt;br /&gt;
| December 23, 2017&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Single session services not really single session&lt;br /&gt;
| Several &amp;quot;critical&amp;quot; services (like fsp-ldr, fsp-pr, sm:m, etc) are meant to only ever hold a single session with a specific sysmodule. However, when a sysmodule dies, all its service session handles are released -- and thus killing the holder of a single session handle would allow one (via sm:hax etc) to get access to that service. &lt;br /&gt;
&lt;br /&gt;
This was fixed in [[4.0.0]] by adding a semaphore to these critical single-session services, so that even if one gets access to them an error code will be returned when attempting to use any of their commands.&lt;br /&gt;
| With some way to access these services and kill their session holders (like expLDR): dumping sysmodule code, arbitrary service access, elevated filesystem permissions, etc.&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| [[4.0.0]]&lt;br /&gt;
| May/June 2017 (basically immediately after smhax was discovered)&lt;br /&gt;
| December 30, 2017&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| nspwn&lt;br /&gt;
| fsp-ldr command 0 &amp;quot;MountCode&amp;quot; takes in a Content Path (retrieved from NCM by Loader), and returns an IFileSystem for the resulting ExeFS. These content paths, are normally NCAs, but MountCode also supports a number of other formats, including &amp;quot;.nsp&amp;quot; -- which is just a PFS0.&lt;br /&gt;
&lt;br /&gt;
When a path ending in &amp;quot;.nsp&amp;quot; is parsed by MountCode, the PFS0 is treated as a raw ExeFS. Because there is no NCA header, the ACID signatures are not validated -- and because there are no other signatures in a PFS0, this results in no signature checking happening at all.&lt;br /&gt;
&lt;br /&gt;
The actual .nsp handling is eventually done by {content mounting function} called by MountCode and other FS commands.&lt;br /&gt;
&lt;br /&gt;
Thus, by placing an ExeFS (NSOs + &amp;quot;main.npdm&amp;quot;) and setting one&#039;s desired title ID to &amp;quot;@Sdcard:/some_title.nsp&amp;quot; or &amp;quot;@User:/some_title.nsp&amp;quot; etc one can launch arbitrary unsigned code, with arbitrary unsigned NPDMs.&lt;br /&gt;
&lt;br /&gt;
This appears to have been fixed by only allowing .nsp when the input fstype==7 for the internal content-mounting function, returning 0x2EE202 otherwise.&lt;br /&gt;
| With access to &amp;quot;lr&amp;quot;: Arbitrary code execution with full system privileges.&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| Late 2017&lt;br /&gt;
| April 23, 2018&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Single null-byte stack overflow in Loader ContentPath parsing&lt;br /&gt;
| Previously, loader content path parsing looked like this, where path_from_lr was up to 0x300 bytes and not necessarily null-terminated:&lt;br /&gt;
&lt;br /&gt;
  char nca_path[0x300] = {0};&lt;br /&gt;
  strcat(nca_path, path_from_lr);&lt;br /&gt;
  for (int i = 0; nca_path[i]; i++) {&lt;br /&gt;
      if (nca_path[i] == &#039;\\&#039;) { nca_path[i] = &#039;/&#039;); }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Thus, a content path of the maximum length (0x300 bytes) would result in strcat writing a NULL terminator past the end of the nca_path buffer.&lt;br /&gt;
&lt;br /&gt;
This was fixed in [[6.0.0]], the new code looks like this:&lt;br /&gt;
&lt;br /&gt;
  char nca_path[0x300];&lt;br /&gt;
  strncpy(nca_path, path_from_lr, sizeof(nca_path));&lt;br /&gt;
  for (int i = 0; i  &amp;lt; sizeof(nca_path) &amp;amp;&amp;amp; nca_path[i]; i++) {&lt;br /&gt;
      if (nca_path[i] == &#039;\\&#039;) { nca_path[i] = &#039;/&#039;); }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
| With access to &amp;quot;lr&amp;quot;: single null-byte stack overflow in Loader. Maybe (but probably not) loader code execution.&lt;br /&gt;
| [[6.0.0]]&lt;br /&gt;
| [[6.0.0]]&lt;br /&gt;
| September 2, 2018&lt;br /&gt;
| September 19, 2018&lt;br /&gt;
| [[User:SciresM|SciresM]]&lt;br /&gt;
|-&lt;br /&gt;
| System modules vulnerable to selective downgrade attacks&lt;br /&gt;
| Horizon has no mechanism for specifying the specific title version to Loader on process creation.&lt;br /&gt;
&lt;br /&gt;
Observing this, one can note that after a system update one could install a downgraded version of a specific system module (e.g. nvservices) while leaving the rest of the OS at the same version.&lt;br /&gt;
&lt;br /&gt;
Unless there was some breaking API change, this allows one to make a console vulnerable once more to an exploit in a sysmodule by downgrading it and nothing else.&lt;br /&gt;
&lt;br /&gt;
This was fixed in [[8.1.0]] by incrementing a version field in NPDM, and checking it against a hardcoded list for certain titles in Loader&#039;s process creation func.&lt;br /&gt;
| With access to content installation commands (or a vulnerable lower version to selectively install newer titles), reintroducing bugs in vulnerable system modules on newer firmware versions.&lt;br /&gt;
| [[8.1.0]]&lt;br /&gt;
| [[8.1.0]]&lt;br /&gt;
| When FIRM was first dumped in 2017.&lt;br /&gt;
| June 17, 2019&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Broken RNG for [[Loader_services|Loader]] ASLR&lt;br /&gt;
| The RNG used for generating the ASLR slide is only seeded with 32bits, with the data from [[SVC|svcGetInfo]]. Hence, one could bruteforce the seed if one has infoleaks from any programs. This can be successfully bruteforced with at least 2 sample codebin addrs from different programs (with only 1 sample a lot of invalid seeds are found), however in some cases more than 1 seed might be found.&lt;br /&gt;
&lt;br /&gt;
With [15.0.0+] Loader now uses csrng_GenerateRandomBytes for determining the ASLR slide.&lt;br /&gt;
&lt;br /&gt;
See also [https://github.com/switchbrew/loader-aslr-solver loader-aslr-solver].&lt;br /&gt;
| Breaking ASLR for all non-KIP processes, allowing predicting the main-codebin base addr for all non-KIP processes until the next reboot.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| January 30, 2022 (presumably found much earlier?)&lt;br /&gt;
| October 11, 2022&lt;br /&gt;
| Everyone&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== System Modules ==&lt;br /&gt;
Flaws in this category pertain to any non-built-in system module.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| OOB Read in NS system module (pl:utoohax, pl:utonium, maybe other names)&lt;br /&gt;
| Prior to [[3.0.0]], pl:u (Shared Font services implemented in the NS sysmodule) service commands 1,2,3 took in a signed 32-bit index and returned that index of an array but did not check that index at all. This allowed for an arbitrary read within a 34-bit range (33-bit signed) from NS .bss. In [[3.0.0]], sending out of range indexes causes error code 0x60A to be returned.&lt;br /&gt;
| Dumping full NS .text, .rodata and .data, infoleak, etc&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| [[3.0.0]]&lt;br /&gt;
| April 2017&lt;br /&gt;
| June 19, 2017&lt;br /&gt;
| [[User:qlutoo|qlutoo]], ReSwitched Team (independently)&lt;br /&gt;
|-&lt;br /&gt;
| Unchecked domain ID in common IPC code&lt;br /&gt;
| Prior to [[2.0.0]], object IDs in [[IPC_Marshalling#Domain_message|domain messages]] are not bounds checked. This out-of-bounds read could be exploited to brute-force ASLR and get PC control in some services that support domain messages.&lt;br /&gt;
|&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| July 2017&lt;br /&gt;
| July 20, 2017‎&lt;br /&gt;
| [[User:hthh|hthh]]&lt;br /&gt;
|-&lt;br /&gt;
| Out-of-bounds array read for [[BCAT_Content_Container]] secret-data index&lt;br /&gt;
| The [[BCAT_Content_Container]] secret-data index is not validated at all. This is handled before the RSA-signature(?) is ever used. Since the field is an u8, a total of 0x800-bytes relative to the array start can be accessed.&lt;br /&gt;
This is not useful since the string loaded from this array is only involved with key-generation.&lt;br /&gt;
| &lt;br /&gt;
| Unknown&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| August 4, 2017&lt;br /&gt;
| August 6, 2017&lt;br /&gt;
| [[User: shinyquagsire23|Shiny Quagsire]], [[User:Yellows8|yellows8]] (independently)&lt;br /&gt;
|-&lt;br /&gt;
| expLDR (sysmodule handle table exhaustion)&lt;br /&gt;
| Most sysmodules share common template code to handle IPC control messages. The command DuplicateSession (type 5 command 2)&#039;s template code will abort() if it fails to duplicate a session&#039;s handle for the requester. Because many sysmodules have limited handle table size (smaller than the browser/other entrypoints), repeatedly requesting to duplicate one&#039;s session will cause the sysmodule to run out of handle table space and abort, causing the service to release all its handles cleanly.&lt;br /&gt;
| Sysmodule crashes.  Most usefully, crashing ldr allows access to fsp-ldr and crashing pm allows access to fsp-pr. Useless after [[4.0.0]], which mitigated a number of single-session service access issues.&lt;br /&gt;
| Unfixed&lt;br /&gt;
| [[4.1.0]]&lt;br /&gt;
| June 24, 2017&lt;br /&gt;
| March 8, 2018&lt;br /&gt;
| [[User:daeken|daeken]]&lt;br /&gt;
|-&lt;br /&gt;
| Transfer Memory leak in nvservices system module&lt;br /&gt;
| The nvservices sysmodule does not clear most of its transfer memory prior to release.&lt;br /&gt;
| The calling process can read key bits of memory, including breaking ASLR (by revealing the image base) and exposing the address of other transfer memory to set up attacks. More details here: [https://daeken.svbtle.com/nintendo-switch-nvservices-info-leak transfermeme (nvservices info leak)] by [[User:daeken|daeken]]&lt;br /&gt;
| [[6.0.0]]&lt;br /&gt;
| [[6.0.0]]&lt;br /&gt;
| June 2017&lt;br /&gt;
| October 16, 2018&lt;br /&gt;
| [[User:qlutoo|qlutoo]] and [[User:hexkyz|hexkyz]],&lt;br /&gt;
[[User:daeken|daeken]] (independently)&lt;br /&gt;
|-&lt;br /&gt;
| OOB write in audio system module&lt;br /&gt;
| Prior to [[2.0.0]], the [[Audio_services#audout:u|AppendAudioOutBuffer]] and [[Audio_services#audin:u|AppendAudioInBuffer]] IPC commands would blindly increment the appended buffers&#039; count while using said count value as an index to where the user data should be copied into. This resulted in an 0x28 bytes, user controlled, out-of-bounds memory write into the [[Audio_services|audio]] sysmodule&#039;s memory space.&lt;br /&gt;
Combined with the [[Audio_services#audout:u|GetReleasedAudioOutBuffer]] or [[Audio_services#audin:u|GetReleasedAudioInBuffer]] commands, this could also be used as an 8 byte infoleak.&lt;br /&gt;
&lt;br /&gt;
In [[2.0.0]], the commands now return error code 0x1099 if the number of unreleased buffers exceeds 0x1F.&lt;br /&gt;
| Code execution under audio sysmodule&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| &lt;br /&gt;
| November 2, 2018&lt;br /&gt;
| [[User:hexkyz|hexkyz]], probably others (independently).&lt;br /&gt;
|-&lt;br /&gt;
| Infoleak in nvservices system module&lt;br /&gt;
| The [[NV_services|nvservices]] ioctl [[NV_services#NVMAP_IOC_ALLOC|NVMAP_IOC_ALLOC]] takes an optional argument &amp;quot;addr&amp;quot; which allows the calling process to pass a pointer to user allocated memory for backing a nvmap object. If &amp;quot;addr&amp;quot; is left as 0, nvservices uses the transfer memory region (donated by the user during initialization) instead, when allocating memory for the nvmap object.&lt;br /&gt;
By design, freeing the nvmap object by calling the ioctl [[NV_services#NVMAP_IOC_FREE|NVMAP_IOC_FREE]] returns, in its &amp;quot;refcount&amp;quot; argument, the user address previously supplied if the reference count reaches 0.&lt;br /&gt;
However, prior to [[6.2.0]], the case where the transfer memory region is used to allocate the nvmap object was not taken into account, thus resulting in [[NV_services#NVMAP_IOC_FREE|NVMAP_IOC_FREE]] leaking back an address from within the transfer memory region mapped in nvservices&#039; memory space.&lt;br /&gt;
&lt;br /&gt;
In [[6.2.0]], [[NV_services#NVMAP_IOC_FREE|NVMAP_IOC_FREE]] no longer returns the address when the transfer memory region is used instead of user supplied memory.&lt;br /&gt;
| Combined with other vulnerabilities: Defeating ASLR in nvservices sysmodule.&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| April 2017&lt;br /&gt;
| November 24, 2018&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| nvhax (memory corruption in nvservices system module)&lt;br /&gt;
| Prior to [[6.2.0]], the [[NV_services|nvservices]] ioctl [[NV_services#.2Fdev.2Fnvhost-ctrl-gpu|NVGPU_GPU_IOCTL_WAIT_FOR_PAUSE]] would take a single &amp;quot;pwarpstate&amp;quot; argument which would be interpreted by nvservices as a memory pointer for writing 2 &amp;quot;warpstate&amp;quot; structs (one for each Streaming Multiprocessor).&lt;br /&gt;
This resulted in nvservices attempting to blindly memcpy into this user supplied address and trigger a crash. However, if paired with an infoleak, this could be used to arbitrarily write 0x30 bytes anywhere in nvservices&#039; memory space.&lt;br /&gt;
Additionally, the &amp;quot;warpstate&amp;quot; struct itself was never initialized, which means nvservices would leak the 0x30 bytes from the stack. By invoking other ioctls it was also possible to partially control the stack contents and achieve a usable arbitrary memory write primitive.&lt;br /&gt;
&lt;br /&gt;
In [[6.2.0]], [[NV_services#.2Fdev.2Fnvhost-ctrl-gpu|NVGPU_GPU_IOCTL_WAIT_FOR_PAUSE]] now takes 2 inline &amp;quot;warpstate&amp;quot; structs instead of a &amp;quot;pwarpstate&amp;quot; pointer, thus effectively avoiding the bad memcpy.&lt;br /&gt;
| Code execution under nvservices sysmodule&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| [[6.2.0]]&lt;br /&gt;
| April 5, 2017&lt;br /&gt;
| November 24, 2018&lt;br /&gt;
| [[User:hexkyz|hexkyz]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Applet_Manager_services#IStorage|AM IStorage]] infoleak&lt;br /&gt;
| Originally the buffer allocated by [[Applet_Manager_services#CreateStorage|CreateStorage]] using the specified input size was not cleared. With [8.0.0+] this was fixed by adding a memset() for the buffer after successful allocation.&lt;br /&gt;
&lt;br /&gt;
Hence, IStorage-&amp;gt;IStorageAccessor-&amp;gt;Read will return uninitialized memory when the Write cmd was not previously used with the specified region.&lt;br /&gt;
| Infoleak from the main [[Applet_Manager_services#IStorage|AM]] heap, allowing defeating ASLR by reading addresses from previously allocated objects.&lt;br /&gt;
| [[8.0.0]]&lt;br /&gt;
| [[8.1.0]]&lt;br /&gt;
| December 2018&lt;br /&gt;
| August 9, 2019&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[HID_services#hid:sys|hid:sys]] ButtonConfig s32 array-index not validated&lt;br /&gt;
| The input s32 array-index for [[HID_services#hid:sys|hid:sys]] ButtonConfig cmds 1255-1270 was originally not validated. Using a negative or &amp;gt;=5 index results in accessing out-of-bounds data, with an array stored on stack.&lt;br /&gt;
[10.1.0-10.2.0] Each of these cmds will now Abort if the s32 is negative or &amp;gt;=5. [11.0.0+] Now an unsigned compare is used, with 0 or an error being immediately returned when the value is invalid.&lt;br /&gt;
| hid infoleak, out-of-bounds mem-write anywhere in hid address-space relative to the stack array (with constraints on the data).&lt;br /&gt;
| [[10.1.0]]&lt;br /&gt;
| [[11.0.1]]&lt;br /&gt;
| April 18, 2020&lt;br /&gt;
| July 14, 2020&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|Bluetooth]] sdp_server.cc process_service_search() continuation request p_req validation&lt;br /&gt;
| With [5.0.0+], the following was added to the if-block prior to loading cont_offset from p_req: &amp;lt;code&amp;gt;(p_req + sizeof(cont_offset) &amp;gt; p_req_end)&amp;lt;/code&amp;gt; (which verifies that cont_offset is within message bounds).&lt;br /&gt;
| Bluetooth-sysmodule out-of-bounds read from heap, probably not useful since the read value must match a state field, etc.&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[11.0.0]]&lt;br /&gt;
| Switch: December 2020&lt;br /&gt;
| Switch: December 25, 2020&lt;br /&gt;
| Switch: [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|Bluetooth]] A-63146698&lt;br /&gt;
| [https://android.googlesource.com/platform/system/bt/+/226ea26684d4cd609a5b456d3d2cc762453c2d75 A-63146698] / CVE-2017-0785. See also [https://info.armis.com/rs/645-PDC-047/images/BlueBorne%20Technical%20White%20Paper_20171130.pdf here].&lt;br /&gt;
| Bluetooth-sysmodule stack infoleak, which allows defeating ASLR (note: not tested on hw).&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[11.0.0]]&lt;br /&gt;
| Switch: December 2020&lt;br /&gt;
| Switch: December 25, 2020&lt;br /&gt;
| Switch: [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] GetAdapterProperty/SetAdapterProperty unchecked memcpy size&lt;br /&gt;
| GetAdapterProperty copies data from stack to the output buffer using the buffer size, without checking the size (when not handling the Name type). SetAdapterProperty copies data to stack from the input buffer using the buffer size, without checking the size.&lt;br /&gt;
This requires access to the btdrv service, only hid and btm have access.&lt;br /&gt;
&lt;br /&gt;
This was fixed with [[12.0.0]] by replacing the buffer data with a fixed-size-struct.&lt;br /&gt;
| Stack infoleak with GetAdapterProperty, stack buffer overflow (and hence ROP) with SetAdapterProperty.&lt;br /&gt;
| [[12.0.0]]&lt;br /&gt;
| [[12.0.0]]&lt;br /&gt;
| July 17, 2020&lt;br /&gt;
| April 7, 2021&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] stack buffer overflow with HID DATA packets&lt;br /&gt;
| The BSA (bt-stack) func bta_hh_co_data copies data from a HID DATA packet to stack without checking the size, then sends it over Uipc. [7.0.0+] The user Uipc callback also copies the input data to stack without checking the size, then sends it to the sharedmem CircularBuffer.&lt;br /&gt;
With [12.0.2+] this was fixed in bta_hh_co_data by clamping the size to a maximum of 0x2BB. The aforementioned buffer overflow in the Uipc callback can&#039;t be triggered since at that point the size was already clamped.&lt;br /&gt;
&lt;br /&gt;
Before this bta_hh_co_data func is reached, there is no validation of the size (such as comparing against the L2CAP MTU) when Basic Mode is being used.&lt;br /&gt;
&lt;br /&gt;
Actually triggering this requires using a data-size larger than the normal L2CAP MTU. This can be done by for example, using raw HCI to send the packet from the remote bluetooth device.&lt;br /&gt;
&lt;br /&gt;
Note that when the remote device is configured as an audio device for [12.0.0+] where [[Settings_services#BluetoothDevicesSettings|BluetoothDevicesSettings]].TrustedServices was only ever set for audio since system-boot, it is not possible for the remote device to connect to the Switch for HID.&lt;br /&gt;
| ROP under [[Bluetooth_Driver_services|bluetooth]] via HID DATA packet sent by a paired HID bluetooth device. This can be triggered at any time while not in sleep-mode, when not in airplane-mode. The earliest is while the Nintendo Switch logo screen is displayed during system boot.&lt;br /&gt;
| [[12.0.2]]&lt;br /&gt;
| [[12.0.2]]&lt;br /&gt;
| July-August 2020&lt;br /&gt;
| May 11, 2021&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] WriteHidData/WriteHidData2/SetHidReport unchecked memcpy size&lt;br /&gt;
| WriteHidData/SetHidReport copies the input struct to stack, then passes it to the funcptr/vfunc call. WriteHidData2 passes the input buffer addr directly to the funcptr/vfunc call. The called func eventually copies the input data to the stack struct using the specified size without validating it.&lt;br /&gt;
This requires access to the btdrv service, only hid and btm have access.&lt;br /&gt;
&lt;br /&gt;
This was fixed with [[12.1.0]] in WriteHidData/SetHidReport by doing a fixed-size copy into another tmp struct, with the size field being clamped to a maximum of 0x2BB afterwards. This struct is then used when calling the vfunc. The vfuncs called by WriteHidData/WriteHidData2/SetHidReport were also updated to clamp the size to the required maximum value.&lt;br /&gt;
| Stack buffer overflow&lt;br /&gt;
| [[12.1.0]]&lt;br /&gt;
| [[12.1.0]]&lt;br /&gt;
| July 16, 2020&lt;br /&gt;
| July 6, 2021&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Infoleak with [[HID_services|hid:sys]] SetButtonConfigStorage{name}Deprecated&lt;br /&gt;
| These cmds pass a stack ptr for the StorageName when calling the internal func. Nothing is written to this StorageName. Hence, stack infoleak (data is copied as a NUL-terminated string), which can be later read by the GetButtonConfigStorage{name} cmds.&lt;br /&gt;
&lt;br /&gt;
This was fixed by removing the Deprecated cmds in [[13.0.0]].&lt;br /&gt;
| Infoleak of hid stack from a StorageName readable via GetButtonConfigStorage{name}, up to the NUL-terminator.&lt;br /&gt;
| [[13.0.0]]&lt;br /&gt;
| [[13.0.0]]&lt;br /&gt;
| December 11, 2020&lt;br /&gt;
| September 27, 2021&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] EventInfo infoleak&lt;br /&gt;
| The various funcs which send messages to the thread which handles writing to EventInfo, didn&#039;t clear the stack msgbuf. Hence, the various get-EventInfo cmds could return leaked stack data. This likely affected most (?) get-EventInfo cmds, besides CircularBuffer-GetHidReportEventInfo.&lt;br /&gt;
&lt;br /&gt;
This only matters for events where there&#039;s uninitialized regions of the EventInfo, such as events with variable-size data without a memset.&lt;br /&gt;
&lt;br /&gt;
This was fixed by clearing the msgbuf in a number of funcs.&lt;br /&gt;
| Bluetooth-sysmodule stack infoleak, which allows defeating ASLR&lt;br /&gt;
| [[13.0.0]]&lt;br /&gt;
| [[13.1.0]]&lt;br /&gt;
| &lt;br /&gt;
| During initial [[13.0.0|diff]]. Added to this page on: December 12, 2021&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[SSL_services|ssl]] CVE-2021-43527&lt;br /&gt;
| CVE-2021-43527, see also [https://bugs.chromium.org/p/project-zero/issues/detail?id=2237 here] and [https://googleprojectzero.blogspot.com/2021/12/this-shouldnt-have-happened.html here].&lt;br /&gt;
Using BigSig where the server cert sig is RSA-PSS results in the remote server throwing {no shared cipher} error when Switch connects. If however one creates a rootCA using BigSig (RSA-PSS), which then signs a server cert where the server key is RSA (not PSS), the vuln can be triggered (if the rootCA is trusted, via using the import service-cmd). It&#039;s unknown whether there&#039;s other ways to trigger the vuln.&lt;br /&gt;
&lt;br /&gt;
The crash occurs in VFY_Begin when using the previously overwritten data. A bitsize of &amp;lt;code&amp;gt;$((16384 + 32 + 64 + 64 + 64))&amp;lt;/code&amp;gt; is only enough to overwrite cx-&amp;gt;hashcx, to fully overwrite cx-&amp;gt;hashobj an additional 0xC-bytes (additional 96 bits) is needed.&lt;br /&gt;
Note that partial overwrite isn&#039;t an option: this is the func that initializes those fields to begin with, it just does deinit first before initializing hashcx/hashobj (prior to that these fields would be all-zero when not overwritten by the buf-overflow).&lt;br /&gt;
| Heap buffer overflow in [[SSL_services|ssl]], overwriting data including a ptr to an object which is later used to load a funcptr.&lt;br /&gt;
| [[13.2.1]]&lt;br /&gt;
| [[13.2.1]]&lt;br /&gt;
| Switch: December 1-2, 2021&lt;br /&gt;
| Switch: January 19, 2022&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] BSA gatt_process_notification stack buffer overflow&lt;br /&gt;
| gatt_process_notification is the GATT handler for processing notification/indication messages. gatt_process_notification does memcpy to stack from the input bt msg data, without size validation. The input len param isn&#039;t validated in this func either - if the remaining len following op_code is less than 2, a negative value will be used for the data copy to stack.&lt;br /&gt;
These were fixed by adding a bounds check for the size, size==0 is also checked for now.&lt;br /&gt;
| Bluetooth-sysmodule stack buffer overflow, with data received from a bluetooth message&lt;br /&gt;
| [[13.2.1]]&lt;br /&gt;
| [[13.2.1]]&lt;br /&gt;
| November 2021&lt;br /&gt;
| January 19, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Applet_Manager_services#IDisplayController|AM IDisplayController]] TakeScreenShotOfOwnLayer OOB&lt;br /&gt;
| The captureBuf is used as an array index without validation. Data used from this array includes calling a funcptr from the array entry, if set. Eventually this is also used to write bools into this array, one of which is from the command input.&lt;br /&gt;
With [5.0.0+] a func is eventually called to get a ptr determined by the input captureBuf, with nullptr being returned for captureBuf&amp;gt;=0x10. The caller will Abort if nullptr was returned.&lt;br /&gt;
| OOB array access&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[13.1.0]]&lt;br /&gt;
| ~July 31, 2019&lt;br /&gt;
| January 26, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Applet_Manager_services#IDisplayController|AM IDisplayController]] ClearCaptureBuffer OOB&lt;br /&gt;
| The captureBuf is used as an array index without proper validation. There is code validating it, but on failure it just skips over a code-block, with code using captureBuf still being used afterwards. Then this is used to write bools into a global array, one of which is from the command input.&lt;br /&gt;
This was fixed with [9.1.0+] by requiring captureBuf = 0-1.&lt;br /&gt;
| OOB bool writes into an array&lt;br /&gt;
| [[9.1.0]]&lt;br /&gt;
| [[13.1.0]]&lt;br /&gt;
| ~July 31, 2019&lt;br /&gt;
| January 26, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Sockets_services|bsdsockets]] ioctl SIOCGIFCONF infoleak&lt;br /&gt;
| Originally bsd ioctl SIOCGIFCONF was handled by setting the data in IPC outbuf0 to the size/addr of IPC outbuf1. These buffers are HipcAutoSelect, so if buf1 is small enough for HipcPointer (otherwise it would be HipcMapAlias) the IPC-buf-ptr leaked into outbuf0 would be located in the codebin-region. Since this is done before the actual ioctl-handling, it doesn&#039;t matter whether the fd is valid.&lt;br /&gt;
This was fixed in [5.0.0+] by using a tmp struct on stack instead of buf0.&lt;br /&gt;
| bsdsockets-sysmodule codebin-region addr infoleak, which allows defeating ASLR.&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[13.1.0]]&lt;br /&gt;
| February 14, 2022 (probably earlier)&lt;br /&gt;
| February 14, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]], probably others&lt;br /&gt;
|-&lt;br /&gt;
| [[Sockets_services|bsdsockets]] ioctl SIOCGIFMEDIA input can contain ptr&lt;br /&gt;
| Originally bsd ioctl SIOCGIFMEDIA used the user-specified ifmediareq structure directly from the input buffer. This includes a ptr. This ptr probably isn&#039;t actually used?&lt;br /&gt;
With [5.0.0+] the structure used as input for the ioctl was changed to using &amp;lt;code&amp;gt;int ifm_ulist[1]&amp;lt;/code&amp;gt; instead of &amp;lt;code&amp;gt;int *ifm_ulist&amp;lt;/code&amp;gt; (which is unused). The input structure is copied to a tmp struct which is used as the original ifmediareq structure, with ifm_ulist always NULL. The user can still specify a non-zero ifm_count value, however that&#039;s not useful with ifm_ulist being always NULL.&lt;br /&gt;
| Useless?&lt;br /&gt;
| [[5.0.0]]&lt;br /&gt;
| [[13.1.0]]&lt;br /&gt;
| February 14, 2022&lt;br /&gt;
| February 14, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]], probably others&lt;br /&gt;
|-&lt;br /&gt;
| Infoleak with [[Joy-Con]] HidCommand PairingIn&lt;br /&gt;
| The joycon protocol handler for PairingIn copies data from stack to the response cmd-buf for sending PairingOut. Only the first byte is set to a type value, the rest is uninitialized stack data.&lt;br /&gt;
&lt;br /&gt;
This was fixed with [15.0.0+] by directly writing to the response data without using stack data.&lt;br /&gt;
| Infoleak of hid stack via a bluetooth/uart message+response with a connected hid controller. This returns addrs for the main-codebin/stack, which allows defeating ASLR.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| September 4, 2020&lt;br /&gt;
| October 10, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Broken RNG for [[RO_services|ro]] ASLR&lt;br /&gt;
| The RNG used to determine where to randomly map NROs in the target process was TinyMT (nn::os::detail::RngManager output, seeded by 128 bits of entropy). However, TinyMT is not cryptographically secure (and can in fact be analytically solved). &lt;br /&gt;
&lt;br /&gt;
Thus, with a few NRO mapping addresses, one could learn the TinyMT state and derive all previous/future RNG outputs, breaking NRO aslr for all processes. &lt;br /&gt;
&lt;br /&gt;
With [15.0.0+] ro now uses csrng_GenerateRandomBytes to determine the random map address for NROs.&lt;br /&gt;
| Breaking ASLR for all NROs loaded in all processes, allowing predicting all NRO mappings for all processes until the next reboot.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| Late 2021/Early 2022&lt;br /&gt;
| October 11, 2022&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Broken RNG used by [[NS_Services|ns]]&lt;br /&gt;
| The code generating the sd seed and the data for the [[SD_Filesystem|sd]] private/private1 file, all use nn::os::GenerateRandomBytes, not csrng. The sd-seed is generated first, then private, then private1. This allows deriving sd-seed from private since this uses TinyMT, as long as the system shipped from factory on [2.0.0+]. private1 is only useful if the system shipped with [4.0.0+].&lt;br /&gt;
&lt;br /&gt;
There&#039;s various other code in ns using nn::os::GenerateRandomBytes as well. This includes the code generating ns_systemseed when it doesn&#039;t exist. ns_systemseed is generated at some point after the various sd-seed-related code (both are called from the same func). Hence, ns_systemseed can be recovered with the above method as well, if it wasn&#039;t recreated at some point without regenerating the above nand-save used with the above.&lt;br /&gt;
&lt;br /&gt;
With [15.0.0+] ns now uses csrng_GenerateRandomBytes for sd-seed/private and ns_systemseed, etc. This only matters when the file is newly generated, which is usually only for factory-fresh systems which ship with this version. This would also apply after being deleted during {System Settings -&amp;gt; Formatting Options -&amp;gt; Initialize Console}, and also with a refurbished console.&lt;br /&gt;
| Generation of a system&#039;s sd-seed allowing decryption of the NAX0 layer of data on [[SD_Filesystem|SD]], derived using the private file from SD. Applies to systems which factory-shipped with a system-version prior to [[15.0.0]] (that is, [2.0.0-14.1.2]).&lt;br /&gt;
| [[15.0.0]], for newly generated files&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| December ~12, 2021&lt;br /&gt;
| October 11, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] BSA bsa_sv_av_cback stack buffer overflow&lt;br /&gt;
| bsa_sv_av_cback checks for two input type values (0xC/0xD), on match it copies the input data to stack without size validation. Then it sends an internal request with this data (likewise when the type values don&#039;t match, except the input data is passed directly with a small size), then it returns.&lt;br /&gt;
This requires the AV functionality added with [13.0.0+], however this func is only reachable with [14.0.0+] where the required functionality was enabled.&lt;br /&gt;
&lt;br /&gt;
This requires message data that&#039;s larger than the MTU, so fragmentation must be used, or manually send the ACL data to bypass the MTU.&lt;br /&gt;
&lt;br /&gt;
This can be triggered via an AVRC message with opcode=0x0 (vendor). The above type 0xC is reached via AVRC ctype 0..4, while 0xD is reached with ctype&amp;gt;=0x9.&lt;br /&gt;
&lt;br /&gt;
With [15.0.0+] the size value for the memcpy (which is also written to the request struct) is clamped to a max value.&lt;br /&gt;
| Bluetooth-sysmodule stack buffer overflow on [14.0.0-14.1.2], with data received from an AVRC bluetooth message with a bluetooth-audio device.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| November 2021&lt;br /&gt;
| October 11, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[WLAN_services|wlan]] SetMulticastList heap buffer overflow&lt;br /&gt;
| The [[WLAN_services#SetMulticastList|SetMulticastList]] command allocates a 0x31-bytes sized buffer and copies to it as much [[WLAN_services#MacAddress|MacAddress]] values from the input [[WLAN_services#MulticastList|MulticastList]] as specified by the &amp;quot;Count&amp;quot; field, but this field is never validated. &lt;br /&gt;
&lt;br /&gt;
With [15.0.0+] error code 0x1906B is now returned if &amp;quot;Count&amp;quot; is larger than 8.&lt;br /&gt;
| wlan-sysmodule heap buffer overflow.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| June 6, 2022&lt;br /&gt;
| November 9, 2022&lt;br /&gt;
| [[User:Hexkyz|hexkyz]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] WriteGattCharacteristic/WriteGattDescriptor stack buffer overflow regression&lt;br /&gt;
| Originally btdrv WriteGattCharacteristic/WriteGattDescriptor (bt service LeClientWriteCharacteristic/LeClientWriteDescriptor are the same) validated the input buffer size. However the size check was removed with [12.0.0+] (which was also when bluetooth was refactored), hence stack buffer overflow. Anything with btdrv/bt services access can trigger it. While this is intended to require a BLE connection, it seems to be possible to trigger the buffer overflow without any BLE connection by passing ConnectionHandle=0xFFFFFFFF (handle not tested on hardware).&lt;br /&gt;
| Bluetooth-sysmodule stack buffer overflow on [12.0.0-15.0.1], with data from BLE IPC cmds.&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| December 10, 2021&lt;br /&gt;
| February 23, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[JIT_services|JIT]] usability issues&lt;br /&gt;
| CreateJitEnvironment will enter infinite-loops using nn::jitsrv::detail::AslrAllocator::GetAslrRegion when either of the input CodeMemory sizes are zero. Also the second CodeMemory is useless for the user-process since the second addr returned by GetCodeAddress is a dup of the first one, set during state init by CreateJitEnvironment.&lt;br /&gt;
With [14.0.0+] size=0 is now properly handled, and also the state for the second addr from GetCodeAddress is now properly initialized.&lt;br /&gt;
| Minor usability issues, not useful for exploitation (size=0 will cause jit-sysmodule to hang in a loop).&lt;br /&gt;
| [[14.0.0]]&lt;br /&gt;
| [[14.0.0]]&lt;br /&gt;
| October 1, 2020&lt;br /&gt;
| February 26, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[USB_services|usbhs]] uninitialized IClientEpSession&lt;br /&gt;
| usbhs IClientIfSession OpenUsbEp creates an IClientEpSession object. The allocated object from ExpHeap is not memset, only select fields are cleared. The rest of initialization is done by PopulateRing - however the user-process could skip using that if wanted (official sw always uses it).&lt;br /&gt;
&lt;br /&gt;
ShareReportRing maps tmem and writes the ring buffer/count field into object state. PopulateRing also eventually initializes these fields, with the buffer being allocated from ExpHeap instead of tmem. These fields are not cleared during object creation from OpenUsbEp.&lt;br /&gt;
&lt;br /&gt;
GetXferReport after validating the cmd input, just uses object state assuming it was initialized. This runs code which is the same as the user-process code handling the tmem ringbuf.&lt;br /&gt;
&lt;br /&gt;
Therefore, by skipping using PopulateRing and then using GetXferReport the sysmodule will use an uninitialized ringbuf ptr, and an uninitialized count field. If one could control these fields by doing ExpHeap allocations prior to OpenUsbEp so that {target fields} would be located at {IClientEpSession ring fields}, then one could read usb-sysmodule memory at the target buffer address.&lt;br /&gt;
&lt;br /&gt;
See [[USB_services#ShareReportRing|here]] for ringbuf format. The sysmodule will Abort if read_index is &amp;gt;= {ring count field from object state}. Otherwise it copies an entry from that index to output, and updates read_index.&lt;br /&gt;
&lt;br /&gt;
This is probably tricky to abuse as the ringbuf ptr has to be valid, and {see above} (likewise for write_index when the report-ringbuf-writing func runs).&lt;br /&gt;
&lt;br /&gt;
PostBufferAsync/BatchBufferAsync also use seperate object ring fields which are left uninitialized from OpenUsbEp. Targeting this would be tricky with the ring restrictions - this would allow writing data to a ring addr however.&lt;br /&gt;
&lt;br /&gt;
Pre-4.0.0 (only 2.0.0 checked) is not affected by these. The ring fields in the object are cleared during object creation (no memset of the entire object however). GetXferReport would null-deref if PopulateRing was skipped. PostBufferAsync/BatchBufferAsync will throw an error if PopulateRing was skipped. Pre-4.0.0 also has different ring handling as well.&lt;br /&gt;
&lt;br /&gt;
[16.0.0+] The IClientEpSession init func now clears the remaining previously uninitialized fields. The cmds using the ring fields still don&#039;t check for NULL, so using GetXferReport/PostBufferAsync/BatchBufferAsync without PopulateRing will just trigger null-deref. Even if the ptr were somehow valid but ring-count field was left at 0, this would then Abort due to: &amp;lt;code&amp;gt;if (ring_count &amp;lt;= index_loaded_from_ringptr) &amp;lt;Abort&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
| [4.0.0-15.0.1] If one can trigger using {target values} as the unintialized fields: memory reads from the target addr with GetXferReport, and memory R/W with PostBufferAsync/BatchBufferAsync. This requires access to usb:hs, and an usb device must be connected which is not being used by {other sessions}. If successful, this might (?) result in usb-sysmodule compromise.&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| [[16.0.0]]&lt;br /&gt;
| January 30, 2023&lt;br /&gt;
| February 26, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[NS_services|ns]] RequestMoveApplicationEntity/EstimateSizeToMove buffer overflow&lt;br /&gt;
| ns RequestMoveApplicationEntity eventually calls a func which: Loops through the input buffer. If any entry has value 6, it will call another func to copy data from state to output safely (uses the max_count param). Otherwise, it copies the input buffer to an outbuf (located on caller&#039;s stack) without any size validation (inlined memcpy), even though there is a max_count param.&lt;br /&gt;
&lt;br /&gt;
Additional memwrites are also done to the above outbuf following the initial memcopy. This can be avoided if the buffer doesn&#039;t contain bytes with values 3-6 (if using values in that range is really needed, the cmd input StorageId param can be set to the required value so that the specified value doesn&#039;t trigger the memwrite). Value 6 shouldn&#039;t be used anyway (see above).&lt;br /&gt;
&lt;br /&gt;
ns EstimateSizeToMove first calls the same func which does the copy above (outbuf is also located on stack), then it calls another func. Hence, same vuln here.&lt;br /&gt;
&lt;br /&gt;
By corrupting just the first byte of x29 with EstimateSizeToMove, one can obtain infoleaks. This method with x29 essentially only works with [15.0.0+]. Pre-15.0.0 would require a different method with partial overwrite of retaddr, however it&#039;s unknown whether this would actually work for infoleak (would require [12.0.0+] for the stack layout change).&lt;br /&gt;
With EstimateSizeToMove where x29 is overwritten, the output u64 is the leaked ptr (can be codebin-region). Note that the cmd has to return Result=0 for this to work. x29 is used to load the value which is copied to the cmdreply rawdata.&lt;br /&gt;
&lt;br /&gt;
As of [17.0.0+] an error is thrown if the input array count is larger than 8 (size of the stack dst-array).&lt;br /&gt;
| ns-sysmodule stack buffer overflow, allowing ns infoleak+ROP.&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| January 2, 2023&lt;br /&gt;
| October 17, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[PSC_services|ovln:snd]] OpenSender unvalidated count&lt;br /&gt;
| ovln:snd OpenSender has a count param. This count is used to allocate the specified number of objects in a linked-list for storing the data from Send. If count is 0, the linked-list is left empty, with ptrs to itself within the ISender object.&lt;br /&gt;
&lt;br /&gt;
ISender Send when the above linked-list is empty, runs a switch-statement with &amp;lt;code&amp;gt;(inval&amp;gt;&amp;gt;8)&amp;amp;0xFF&amp;lt;/code&amp;gt;. This uses another linked-list where the ptrs are initially {within ISender obj}.&lt;br /&gt;
No space is allocated in the ISender obj for the linked-list object-data. Therefore using Send with val 1&amp;lt;&amp;lt;8 or 2&amp;lt;&amp;lt;8 (other values throw error) results in the specified input struct being copied into the ISender obj, which then overwrites heap data OOB.&lt;br /&gt;
If for example one used OpenSender again right after the first OpenSender usage, then used Send as described above, this would corrupt the second ISender which includes overwriting the vtable.&lt;br /&gt;
If one would use Send twice in a row like this, the second one would use a corrupted linked-list (written from the first Send). If the linked-list ptrs would be valid (no crash triggered) this would allow one to copy the input data to a controlled addr, though it&#039;s restricted with the linked-list usage.&lt;br /&gt;
&lt;br /&gt;
Using GetUnreceivedMessageCount afterwards is of no interest.&lt;br /&gt;
&lt;br /&gt;
Besides ovln, the only other allocs on this heap is from IPmModule Initialize. This heap is also used for psc:* services (object allocs).&lt;br /&gt;
&lt;br /&gt;
In theory (untested) it may be possible to also use this to obtain infoleaks, however it would only return the high-u32 of ptrs not the low u32. Essentially, one would trigger object allocations so that ExpHeap has layout: {ISender} -&amp;gt; {RF chunk from freeing an object} -&amp;gt; {module object from IPmModule Initialize}. Then one would use the Send vuln to corrupt the RF chunk, changing the size to a larger value. Then one would trigger an object allocation (probably same object which was previously freed), then another object for overwriting the module object (ISender would work) with ptrs at the target offsets in the module object. Then once IPmModule GetRequest is used, the returned u32s would be the high-u32 from ptrs. Due to alignment requirements with each allocation, it isn&#039;t possible to shift the allocations in order to leak ptr low-u32.&lt;br /&gt;
&lt;br /&gt;
[17.0.0+] Now throws an error if the input count for OpenSender is 0.&lt;br /&gt;
| [[PSC_services|psc]]-sysmodule heap memory corruption ([[NS_services|ns]]-sysmodule on pre-8.0.0).&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| January 13, 2023&lt;br /&gt;
| October 20, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[NV_services|nv]] NVGPU_GPU_IOCTL_GET_CHARACTERISTICS Ioctl3 infoleak&lt;br /&gt;
| The handler code for NVGPU_GPU_IOCTL_GET_CHARACTERISTICS for Ioctl/Ioctl3 are essentially the same, except for the value used for the max-size clamp: Ioctl uses constant 0xA0, while Ioctl3 uses the outbuf1_size. So if one uses this with Ioctl3 and a large outbuf1, this will memcpy data OOB from the source buffer, hence infoleak.&lt;br /&gt;
With [17.0.0+] the second block of csel code which previouly essentially used the clamped size from above, was replaced with code which properly clamps to the max-size constant.&lt;br /&gt;
| nvservices-sysmodule infoleak, which allows defeating ASLR.&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| [[17.0.0]]&lt;br /&gt;
| February 25, 2022&lt;br /&gt;
| October 24, 2023&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Audio_services|audctl]] GetTargetDeviceInfo infoleak&lt;br /&gt;
| audctl GetTargetDeviceInfo calls an impl func with a ptr to a stackbuf, then if successful memcpys the 0x100-bytes from that buffer to output. This stackbuf is not memset. This func (after doing various state checks) copies a string to output, other than always writing a NUL-terminator there&#039;s no clearing of the buffer.&lt;br /&gt;
&lt;br /&gt;
This will leak audio-sysmodule stack into the output buffer as long as the state/input checks pass (for the remainder of the buffer following the string NUL-terminator).&lt;br /&gt;
&lt;br /&gt;
With [18.0.0+] data is written directly to the outbuf instead of the stack tmpbuf.&lt;br /&gt;
| audio-sysmodule infoleak, which allows defeating ASLR.&lt;br /&gt;
| [[18.0.0]]&lt;br /&gt;
| [[18.0.0]]&lt;br /&gt;
| December 24, 2022&lt;br /&gt;
| March 26, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Audio_services|audctl]] GetSystemInformationForDebug infoleak / buffer overflow&lt;br /&gt;
| audctl GetSystemInformationForDebug calls a func with a 0x1000-byte stack tmpbuf, then afterwards that buffer is memcpy&#039;d into the cmd outbuf. This called func doesn&#039;t clear the buffer. This func eventually uses [[BTM_services|btm]] cmd75 with outarray={global ptr} and count=10. Then if the outcount is s32 &amp;gt;=1, it loops through the output using the outcount, without validating it besides the &amp;lt;1 check. Data from that outarray is copied into the array in the func output buffer (tmpbuf above).&lt;br /&gt;
&lt;br /&gt;
With btm comprimised, one could return a large output count and trigger a stack buffer overflow with data following that global array, however exploiting this would be difficult since that data would be uncontrolled (can&#039;t directly control it from this cmd at least).&lt;br /&gt;
&lt;br /&gt;
A stack infoleak can be obtained with this as well (assuming the above output array isn&#039;t full).&lt;br /&gt;
&lt;br /&gt;
Even though the name has &amp;quot;ForDebug&amp;quot;, there&#039;s no checks which would trigger an error / return early (this also always returns 0).&lt;br /&gt;
&lt;br /&gt;
[18.0.0+] now clears the output buffer, and also now prints strings into the buffer instead of writing binary data (overflow no longer possible).&lt;br /&gt;
| audio-sysmodule infoleak, which allows defeating ASLR. Also audio-sysmodule memory corruption, likely not useful unless there&#039;s a way to control the data.&lt;br /&gt;
| [[18.0.0]]&lt;br /&gt;
| [[18.0.0]]&lt;br /&gt;
| December 7, 2022&lt;br /&gt;
| March 27, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Migration_services|migration]] nn::migration::savedata::IServer cmd1 buffer overflow&lt;br /&gt;
| nn::migration::savedata::IServer cmd1 with [18.0.0-18.0.1] copies data from an array to the output ptr. As the output is an u64 field for the IPC cmd output, this is a field on stack. Hence, if more than 1 entry (8-bytes) are copied a stack buffer overflow will occur. Note that cmd3 loads the same data, except this has a proper output array.&lt;br /&gt;
It&#039;s unknown whether there&#039;s a way to actually control this data with a large enough enough size.&lt;br /&gt;
&lt;br /&gt;
See [[18.1.0]] for the diff/fix.&lt;br /&gt;
| [[Migration_services|migration]] stack buffer overflow, only on [18.0.0-18.0.1].&lt;br /&gt;
| [[18.1.0]]&lt;br /&gt;
| [[18.1.0]]&lt;br /&gt;
| June 11, 2024&lt;br /&gt;
| June 11, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]] (sysupdate diff)&lt;br /&gt;
|-&lt;br /&gt;
| [[SSL_services|ssl]] broken RNG&lt;br /&gt;
| [[SSL_services|ssl]] uses nn::os::GenerateRandomBytes, but not [[SPL_services|spl]] GenerateRandomBytes. See the RNG entries elsewhere. This is used to seed the NSS global RNG (drbg.c, RNG_GenerateGlobalRandomBytes etc).&lt;br /&gt;
&lt;br /&gt;
If one could somehow determine the data which was returned by nn::os::GenerateRandomBytes during seeding (which is likely difficult), the global RNG would be broken.&lt;br /&gt;
&lt;br /&gt;
With [19.0.0+] nn::os::GenerateRandomBytes usage was replaced with [[SPL_services|spl]] GenerateRandomBytes.&lt;br /&gt;
| Breaking [[SSL_services|ssl]] global RNG -&amp;gt; potentially predict RNG data (keys(?)) during TLS comms.&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| December 14, 2021&lt;br /&gt;
| October 8, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Audio_services|audren]] uncleared TransferMemory&lt;br /&gt;
| audren OpenAudioRenderer uses the input tmem as workmem. The IAudioRenderer dtor doesn&#039;t clear the workmem properly. Depending on input params, certain objects stored here have vtables - hence infoleak.&lt;br /&gt;
The exact location in the workmem will vary depending on the input params - these objects are dynamically allocated in the workmem.&lt;br /&gt;
The following will leak vtables: Sink, Effect.&lt;br /&gt;
&lt;br /&gt;
If the initialization func fails, the tmem is unmapped without clearing it first. It&#039;s unknown whether there&#039;s a way to actually trigger an infoleak with this however. With [19.0.0+] it&#039;s now cleared on failure.&lt;br /&gt;
&lt;br /&gt;
With [19.0.0+] the dtor now clears the workmem when needed.&lt;br /&gt;
| Reading leaked data/ptrs from TransferMemory -&amp;gt; defeating ASLR in [[Audio_services|audio]]-sysmodule.&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| December 17, 2022&lt;br /&gt;
| October 13, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Audio_services|audren]] UpdateMixes OOB mem-copy&lt;br /&gt;
| With nn::audio::server::InfoUpdater::UpdateMixes when nn::audio::server::BehaviorInfo::IsMixInParameterDirtyOnlyUpdateSupported() returns true (requires REV7, which is [7.0.0+]), the mix_id from user input is used without validation as input to &amp;lt;code&amp;gt;&amp;lt;nn::audio::server::MixContext::GetInfo(int) const&amp;gt;&amp;lt;/code&amp;gt;, instead of the counter from the for-loop. This allows one to control the destination MixInfo index which the user-input data is written into. If too large, this will trigger OOB data-copy. Note that the u8 at dest_MixInfo+12 must be non-zero.&lt;br /&gt;
Also note that a field is loaded from dest_MixInfo which is used as a splitter_id, so splitters need to be initialized where count is large enough for that id.&lt;br /&gt;
&lt;br /&gt;
With [19.0.0+] after getting the mix_id (loop-index/input) it now does: &amp;lt;code&amp;gt;if (mix_id &amp;lt; 0 || mix_id &amp;gt;= nn::audio::server::MixContext::GetCount()) continue;&amp;lt;/code&amp;gt;&lt;br /&gt;
| OOB mem-copy in [[Audio_services|audio]]-sysmodule, which for example can be used to overwrite a vtable used immediately after UpdateMixes.&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| [[19.0.0]]&lt;br /&gt;
| December 19, 2022&lt;br /&gt;
| October 13, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bus_services|sasbus]] StartPeriodicReceiveMode infoleak&lt;br /&gt;
| StartPeriodicReceiveMode writes a vtable ptr into the mapped tmem at +0. The tmem is mapped RW in the user-process. There is no clearing of tmem during tmem cleanup. Hence, the user-process can read the tmem to obtain a Bus-sysmodule codebin-region infoleak. This vtable-ptr seems to be unused - it&#039;s also empty after the first two entries (stubbed incref/decref).&lt;br /&gt;
[20.0.0+] Removed the vtable ptr, with data intended for the user-process being moved from tmem+0x8 to +0x0. Also, instead of calling memset, funcs are called for manually clearing tmem.&lt;br /&gt;
| Bus-sysmodule infoleak, which allows defeating ASLR.&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| February 22, 2022&lt;br /&gt;
| May 3, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[NFC_services|nfc]] SendCommandByPassThrough buffer overflow&lt;br /&gt;
| SendCommandByPassThrough eventually copies the input buffer into a fixed-size heap buffer, without size validation.&lt;br /&gt;
This was fixed with [20.0.0+] by clamping the size.&lt;br /&gt;
| nfc-sysmodule heap buffer overflow.&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| Late November 2021&lt;br /&gt;
| May 3, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]] (maybe others?)&lt;br /&gt;
|-&lt;br /&gt;
| [[HID_services|hidbus]] EnableJoyPollingReceiveMode infoleak&lt;br /&gt;
| The tmem initialized by hidbus EnableJoyPollingReceiveMode contains a vtable ptr (tmem+0x10), hence infoleak. With [20.0.0+] the vtable ptr write was removed, and tmem is now memset starting at tmem+0x10 instead of +0x20.&lt;br /&gt;
| hid-sysmodule infoleak, which allows defeating ASLR.&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| March 2020&lt;br /&gt;
| May 4, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[SSL_services|ssl]] Certificate verification bypass&lt;br /&gt;
| The ssl sysmodule keeps a list of trusted certificates, that are imported by an app with ImportServerPki. During certificate verification, if the certificate that is provided by the server has the same subject key id as a trusted certificate, the certificate is accepted, even if self-signed. A blog post about this vulnerability can be found [https://reversing.live/sslbypass.html here].&lt;br /&gt;
| Man-in-the-middle for any connection that uses ImportServerPki.&lt;br /&gt;
| [[20.2.0]]&lt;br /&gt;
| [[20.2.0]]&lt;br /&gt;
| June 6, 2025&lt;br /&gt;
| August 8, 2025&lt;br /&gt;
| Yannik&lt;br /&gt;
|-&lt;br /&gt;
| [[LDN_services|ldn]] AdvertiseData OOB-memcpy with EncryptionType3 (AES-128-GCM) actionframes (ldnhax)&lt;br /&gt;
| The ldn action-frame parser object for AES-128-GCM (used with [[LDN_services|EncryptionType3]]), when it does validation once finished, only verifies that the sizes are within bounds of the input buffer. There&#039;s no validation against constants, which the other EncryptionType objects have. The caller code doesn&#039;t validate the size either.&lt;br /&gt;
&lt;br /&gt;
[21.0.0+] Now validates the advert-size with sizeof(NetworkInfo.AdvertiseData).&lt;br /&gt;
&lt;br /&gt;
For more details see [https://gist.github.com/yellows8/16bb56343d085d2db2ab0adc5d4cef99 here].&lt;br /&gt;
| Compromise of ldn starting from OOB-memcpy, even on S2: stack infoleak (ASLR defeat), arbitrary memory read/write (which also allows handle-leak), vfunc-calls with arbitrary [[Security_Mitigations|vtable]].&lt;br /&gt;
| [[21.0.0]]&lt;br /&gt;
| [[21.0.0]]&lt;br /&gt;
| June ~13, 2025&lt;br /&gt;
| November 11, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[HID_services|hid:dbg]] AttachHdlsVirtualDevice unvalidated DeviceTypeInternal&lt;br /&gt;
| hid:dbg AttachHdlsVirtualDevice eventually passes the input from HdlsDeviceInfo into a func without any validation. The DeviceTypeInternal field is used as the index for loading a ptr from a global array. The only validation occurs when the loaded ptr is NULL - this is just for initializing the ptr in the array when it&#039;s not already set.&lt;br /&gt;
&lt;br /&gt;
Since the highest DeviceTypeInternal is value 30, using &amp;gt;=31 will load an OOB ptr. This ptr is written to state, and also immediately passed to a called func. As long as ptr is valid it should be fine with this func.&lt;br /&gt;
&lt;br /&gt;
This functionality is also used eventually by ApplyHdlsNpadAssignmentState and ApplyHdlsStateList.&lt;br /&gt;
&lt;br /&gt;
It&#039;s unknown whether there&#039;s a way to exploit this. Also note that hid:dbg is not normally accessible to retail titles.&lt;br /&gt;
&lt;br /&gt;
[21.0.0+] Arrayindex=0 is now used when the input is invalid.&lt;br /&gt;
| Likely useless, even if reachable?&lt;br /&gt;
| [[21.0.0]]&lt;br /&gt;
| [[21.0.0]]&lt;br /&gt;
| June 3, 2024 (possibly eariler(?))&lt;br /&gt;
| November 14, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Bluetooth_Driver_services|bluetooth]] BSA allowed ATT MTU is too large&lt;br /&gt;
| GATT-handler stack buffer overflows with a large input size are only possible if the payload_size (MTU) field in state is large enough. gatt_client_handle_server_rsp/gatt_server_handle_client_req will drop messages where the size is &amp;gt;= payload_size (though unless the request opcode matches certain values it will also send an error-response for invalid-PDU). Both of these handle updating this field when needed, however that&#039;s handled properly.&lt;br /&gt;
&lt;br /&gt;
With bluetooth-classic via L2CAP, a hard-coded MTU of 0x205 is sent in the configure request. However the code handling received configure requests will set payload_size to 0x2A0 if no MTU is specified, or the input MTU if it&#039;s within range 0x30..0x2A0. Hence, sending data large enough for buffer overflows requires bluetooth-classic via L2CAP + manually sending large ACL data.&lt;br /&gt;
&lt;br /&gt;
[15.0.0+] gatt_l2cif_config_ind_cback which handles the received configure-requests with bluetooth-classic mentioned above, now uses MTU range 0x30..0x205 with the default MTU being 0x205. It is therefore no longer possible to trigger the previously mentioned buffer-overflows with bluetooth-classic.&lt;br /&gt;
| Stack buffer overflows in bluetooth-sysmodule due to the allowed MTU for ATT being larger than the stack data.&lt;br /&gt;
| [[15.0.0]]&lt;br /&gt;
| [[20.0.0]]&lt;br /&gt;
| November 2021?&lt;br /&gt;
| November 26, 2025&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Internet Browser == &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in system version&lt;br /&gt;
!  Last system version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| CVE-2016-4657&lt;br /&gt;
| WebKit vuln discovered around August 2016. Most notably used in the iOS 9.3.X exploit. A simple PoC can be found [https://github.com/LiveOverflow/lo_nintendoswitch/blob/master/poc1.html here]. This was later exploited by [https://twitter.com/qwertyoruiopz Qwertyoruiop] using an adjusted version of his iOS 9.3 webkit exploit (others exploited this prior to then).&lt;br /&gt;
|&lt;br /&gt;
| [[2.1.0]]&lt;br /&gt;
| [[2.0.0]]&lt;br /&gt;
| Original: August 2016&lt;br /&gt;
Switch: March 3rd-4th 2017&lt;br /&gt;
|&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| CVE-2017-7005&lt;br /&gt;
| WebKit type confusion.&lt;br /&gt;
|&lt;br /&gt;
| [[3.0.1]]&lt;br /&gt;
| [[3.0.1]]&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| CVE-2016-4622&lt;br /&gt;
| WebKit memory corruption bug. This bug was incorrectly re-introduced in [[4.0.0]]. See [http://www.phrack.org/papers/attacking_javascript_engines.html here] for a detailed write-up from the author.&lt;br /&gt;
|&lt;br /&gt;
| [[6.1.0]]&lt;br /&gt;
| [[6.1.0]]&lt;br /&gt;
|&lt;br /&gt;
| &lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| CVE-2018-4441&lt;br /&gt;
| WebKit memory corruption bug. See [https://bugs.chromium.org/p/project-zero/issues/detail?id=1685&amp;amp;desc=2 here].&lt;br /&gt;
|&lt;br /&gt;
| [[7.0.0]]&lt;br /&gt;
| [[7.0.0]]&lt;br /&gt;
| &lt;br /&gt;
|&lt;br /&gt;
| Everyone&lt;br /&gt;
|-&lt;br /&gt;
| Web-applets OpenSSL broken RNG&lt;br /&gt;
| [[SPL_services|csrng]] access was added to web-applets with [12.1.0+]. Prior to that, csrng and nn::os::GenerateRandomBytes were not used (besides sdk heap code).&lt;br /&gt;
nn::os::GetSystemTick is used to seed the OpenSSL RNG, among other data. Hence, it&#039;s probably (?) possible to bruteforce the RNG initial state, allowing predicting RNG output.&lt;br /&gt;
&lt;br /&gt;
The RNG code is wkcRandomNumbersPeer (peer_wkc nro), with the initialization code using GetSystemTick located in the func immediately before wkcGetTickCountPeer. The former is called from wkcOsslRandFilefReadPeer. wkcOsslRandFilefReadPeer is called for seeding the OpenSSL RNG.&lt;br /&gt;
&lt;br /&gt;
With [12.1.0+], wkcRandomNumberPeer/wkcRandomNumbersPeer wrap nn::os::GenerateRandomBytes. wkcCryptographicallyRandomValuesPeer was added which wraps nn::crypto::GenerateCryptographicallyRandomBytes. wkcOsslRandFilefReadPeer now calls nn::crypto::GenerateCryptographicallyRandomBytes instead of wkcRandomNumbersPeer.&lt;br /&gt;
| Breaking web-applets OpenSSL RNG -&amp;gt; potentially predict RNG data (keys(?)) during TLS comms.&lt;br /&gt;
| [[12.1.0]]&lt;br /&gt;
| [[12.1.0]]&lt;br /&gt;
| January 28, 2022&lt;br /&gt;
| October 8, 2024&lt;br /&gt;
| [[User:Yellows8|yellows8]], likely (?) others&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Whitelist ===&lt;br /&gt;
This section documents [[Internet_Browser|WebApplet]] whitelist issues in applications. These can be used to load your own browser content over plain HTTP, which then for example could be used for web-applet exploitation.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Application&lt;br /&gt;
!  Description&lt;br /&gt;
!  Fixed with app version&lt;br /&gt;
!  Newest app version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Sonic Mania&lt;br /&gt;
| Originally this game launched web-applet with a plain-http URL for displaying the manual, this was later changed to https. Originally the whitelist only had 1 entry for a http URL, this was later replaced with various https-only URLs.&lt;br /&gt;
| 1.04, unknown if fixed with an earlier update&lt;br /&gt;
| 1.04&lt;br /&gt;
| January (?) 2022&lt;br /&gt;
| February 23, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NintendoSDK ==&lt;br /&gt;
This section documents vulnerabilities for NSOs in NintendoSDK.&lt;br /&gt;
&lt;br /&gt;
=== nnSdk ===&lt;br /&gt;
This section documents vulnerabilities for nnSdk (sdknso).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in SDK [[System_Versions|version]]&lt;br /&gt;
!  Last SDK version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| [[HID_services|hidbus]] GetJoyPollingReceivedData buffer overflow&lt;br /&gt;
| hidbus GetJoyPollingReceivedData doesn&#039;t validate the u8 size used for memcpy, when copying the data to the output JoyPollingReceivedData. With 11.x, the size is now clamped to a maximum of 0x2C (regardless of polling-mode). Note that 0x2C is the data-size for JoyButtonOnlyPollingDataAccessor, the other polling-modes have a smaller size.&lt;br /&gt;
&lt;br /&gt;
The hid-sysmodule code which writes data here does handle it properly: size is clamped to a max size, and the data-read uses a fixed-size anyway (hence there&#039;s no way to trigger this sdknso vuln with the hid-sysmodule tmem writing code).&lt;br /&gt;
&lt;br /&gt;
This could only be exploited if one directly writes to the tmem when one has previously compromised hid-sysmodule, without using the normal tmem-writing func for this.&lt;br /&gt;
&lt;br /&gt;
There are only a few [[HID_services#ExternalDevices|apps]] which use hidbus.&lt;br /&gt;
| Triggering a buffer overflow in an application which uses hidbus GetJoyPollingReceivedData, from a previously compromised hid-sysmodule.&lt;br /&gt;
| 11.x.0&lt;br /&gt;
| 11.4.0&lt;br /&gt;
| March 2020&lt;br /&gt;
| December 3, 2020&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| [[Profile_Selector|Profile Selector]] uninitialized input data&lt;br /&gt;
| Originally unused regions of [[Profile_Selector]] UiSettings/UserSelectionSettings were not cleared prior to being sent to the applet. With 1.x.x these are now properly memset().&lt;br /&gt;
| Stack infoleak from user-process, sent to the applet.&lt;br /&gt;
| 1.x.x&lt;br /&gt;
| 11.4.0&lt;br /&gt;
| November-December 2019&lt;br /&gt;
| December 31, 2020&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== NEX ===&lt;br /&gt;
This section documents client-side vulnerabilities for [https://github.com/Kinnay/NintendoClients/wiki/NEX-Overview-(Game-Servers) NEX].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in version&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| Buffer overflow in StringConversion::T2Char8&lt;br /&gt;
| StringConversion::T2Char8 is used to convert IP addresses from a platform-specific encoding to UTF-8. On the 3DS and Switch, the implementation is simply a strcpy. By sending a long IP address string, a buffer overflow can be triggered on the stack. The vulnerability can be triggered through the NAT traversal protocol. A blog post about this vulnerable can be found [https://reversing.live/hacking-hundreds-of-wii-us-at-once.html here].&lt;br /&gt;
| Stack overflow in any game that uses NEX for matchmaking&lt;br /&gt;
| Fixed server-side&lt;br /&gt;
| December, 2022&lt;br /&gt;
| May, 2024&lt;br /&gt;
| Yannik&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Pia ===&lt;br /&gt;
This section documents vulnerabilities for [https://github.com/Kinnay/NintendoClients/wiki/Pia-Overview Pia].&lt;br /&gt;
&lt;br /&gt;
In v5.11.3 (exact starting version unknown) the fixes aren&#039;t present for the below vulns which were fixed in v5.9.3, while in v5.18.98 these are present (exact starting version unknown). This probably indicates that the vuln fixes were backported from a newer Pia version to v5.9.3.&lt;br /&gt;
&lt;br /&gt;
The Pia packet handlers are only active when the game is using multiplayer. LanProtocol is only active in the games which are actively using the LAN-mode option (not Ldn) - only certain games support LAN-mode. The LanProtocol Pia packet handler can be reached while in a lobby or searching for one.&lt;br /&gt;
&lt;br /&gt;
Most Pia packets require an active StationProtocol connection to be active with {InetAddr which the packet was received from}, otherwise the packet is filtered out. The only protocols which don&#039;t use filtering are the following: NatTraversalProtocol, LanProtocol, StationProtocol, LocalProtocol.&lt;br /&gt;
&lt;br /&gt;
Note that broadcast IP-dest Pia packets are accepted - this can be used to target every device on the network which is using Pia (which is really only useful with {above protocols} due to the filtering mentioned above, unless one also handles StationProtocol).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in Pia version&lt;br /&gt;
!  Last Pia version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| nn::pia::session::RelayRouteManageJob::UpdateConnectionReport buffer overflow&lt;br /&gt;
| nn::pia::session::RelayRouteManageJob::UpdateConnectionReport() checks that the input size is at least {value}, but there&#039;s no max size check. This is used to memcpy from the input to elsewhere - hence buf-overflow if size is too large. The dst buffer is allocated on the pead heap - this buffer is probably small.&lt;br /&gt;
Note that there&#039;s various requirements before it would actually reach the memcpy, such as &amp;lt;code&amp;gt;&amp;lt;nn::pia::session::Mesh::IsHost() const&amp;gt;&amp;lt;/code&amp;gt; must return true.&lt;br /&gt;
&lt;br /&gt;
This is called from nn::pia::session::MeshProtocol::ParseConnectionReport().&lt;br /&gt;
&lt;br /&gt;
ParseConnectionReport uses a state ptr for object nn::pia::session::RelayRouteManageJob, it will return if not set. nn::pia::session::Mesh::Initialize handles setup for this, depending on an input field from nn::pia::session::Mesh::Setting. These settings originate from &amp;lt;code&amp;gt;&amp;lt;nn::pia::session::Session::CreateInstance(nn::pia::session::Session::Setting const&amp;amp;)&amp;gt;&amp;lt;/code&amp;gt;, which is called by user-code with the needed settings.&lt;br /&gt;
ParseConnectionReport is therefore only usable if the game explicitly enables the Relay functionality.&lt;br /&gt;
&lt;br /&gt;
In fixed versions immediately after the StationIndex validation it now does: &amp;lt;code&amp;gt;if(statefield+0x10&amp;lt;input_size) return;&amp;lt;/code&amp;gt;&lt;br /&gt;
| Heap buffer overflow triggered by a Pia MeshProtocol message sent to a host device.&lt;br /&gt;
| v5.9.3, see above.&lt;br /&gt;
| v5.9.1/v5.9.2/v5.9.3&lt;br /&gt;
| November 11, 2022&lt;br /&gt;
| November 15, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| nn::pia::lan::LanProtocol::ParseSessionMessage buffer overflow&lt;br /&gt;
| nn::pia::lan::LanProtocol::ParseSessionMessage() calls nn::pia::lan::LanSessionMessage::Deserialize() to deserialize the message payload data buffer into the LanSessionMessage object on stack. LanSessionMessage::Deserialize (among other things) memcpys data from the input buffer to the object, using an u32 from the input buffer - there is no size validation in Deserialize itself.&lt;br /&gt;
There is a size check immediately after calling Deserialize() to verify &amp;lt;code&amp;gt;payloadsize=={u32val}+{constant}&amp;lt;/code&amp;gt;, returning on fail - but this doesn&#039;t matter for too-large-size.&lt;br /&gt;
&lt;br /&gt;
In fixed versions Deserialize now does bounds checking, both for the minimum message size and clamping the memcpy size to a constant. An error is thrown if the clamped memcpy size is larger than the message size. The caller now checks the ret properly, previously it was ignored.&lt;br /&gt;
&lt;br /&gt;
Following the size check in ParseSessionMessage() it calls &amp;lt;code&amp;gt;&amp;lt;nn::pia::session::Mesh::IsProcessingLeaveMesh() const&amp;gt;&amp;lt;/code&amp;gt;, returning if ret is false.&lt;br /&gt;
&lt;br /&gt;
Then it calls nn::pia::lan::LanProtocol::ReceivedFragmentData::Receive(), with the memcpy&#039;d buffer/size from the above LanSessionMessage, and other fields from LanSessionMessage. This eventually memcpys the input buffer to object+{offset}+{chunksize_field}*inputu8, there is no validation for size or inputu8 (except for the above size check). Hence, if the u8 is large enough, this would result in a heap buffer overflow.&lt;br /&gt;
&lt;br /&gt;
In fixed versions ReceivedFragmentData::Receive added a bunch of validation before the memcpy.&lt;br /&gt;
| Stack/heap buffer overflow triggered by a Pia LanProtocol message.&lt;br /&gt;
| v5.9.3, see above.&lt;br /&gt;
| v5.9.1/v5.9.2/v5.9.3&lt;br /&gt;
| November 14, 2022&lt;br /&gt;
| November 15, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| nn::pia::session::SessionProtocol::ParseLeaveMeshInvitation buffer overflow&lt;br /&gt;
| &amp;lt;code&amp;gt;&amp;lt;nn::pia::session::SessionProtocol::ParseLeaveMeshInvitation(nn::pia::transport::ReceivedMessageAccessor const&amp;amp;)&amp;gt;&amp;lt;/code&amp;gt; This immediately returns if *(ReceivedMessageAccessor+16) is 0. Then the input data is deserialized. The input u64 array is deserialized to stack, the u8 arraycount field from input is not validated.&lt;br /&gt;
&lt;br /&gt;
Hence, stack buffer overflow. Note that there&#039;s similar loop code in nearby funcs, which do validate the count properly.&lt;br /&gt;
&lt;br /&gt;
In fixed versions the arraycount field is now validated.&lt;br /&gt;
&lt;br /&gt;
SessionProtocol uses ReliableSlidingWindow MessageHeader, with a maximum message size of 0x100. The allocated size used for the above u64 array is also 0x100-bytes. Hence, when triggering a buf overflow the data after the buffer is uncontrolled data from the SessionProtocol object.&lt;br /&gt;
| Stack buffer overflow triggered by a Pia SessionProtocol message.&lt;br /&gt;
| v5.9.3, see above.&lt;br /&gt;
| v5.9.1/v5.9.2/v5.9.3&lt;br /&gt;
| November 14, 2022&lt;br /&gt;
| November 15, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Optional Pia packet encryption&lt;br /&gt;
| Pia packet encryption is optional. If the encryption flag is disabled, the packet handler will accept it and skip crypto.&lt;br /&gt;
In fixed versions immediately after grabbing a packet, it now checks the crypto flag. If it&#039;s plaintext the packet is dropped.&lt;br /&gt;
&lt;br /&gt;
This can be used to send a plaintext Pia packet without needing to handle encryption, especially useful if the session-key can&#039;t be obtained (online-play matchmaking). This could be combined with other vulns if wanted.&lt;br /&gt;
| Sending a plaintext Pia packet without needing to handle encryption.&lt;br /&gt;
| v5.9.3, see above.&lt;br /&gt;
| v5.9.3 (and later versions)&lt;br /&gt;
| &lt;br /&gt;
| November 19, 2022&lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| nn::pia::session::{JoinMeshJob/ProcessUpdateMeshJob}::SetStationDataList OOB read/write/vfunc-call&lt;br /&gt;
| &amp;lt;code&amp;gt;nn::pia::session::JoinMeshJob::SetStationDataList&amp;lt;/code&amp;gt;is called by &amp;lt;code&amp;gt;nn::pia::session::MeshProtocol::ParseJoinResponse(nn::pia::transport::ReceivedMessageAccessor const&amp;amp;)&amp;gt;&amp;lt;/code&amp;gt; with the ReceivedMessageAccessor buffer.&lt;br /&gt;
SetStationDataList will update state and immediately return if the join was denied. It will also validate the num_mesh_stations field against state. ParseJoinResponse also essentially verifies that the message was received from the host device.&lt;br /&gt;
&lt;br /&gt;
The input buffer size is ignored.&lt;br /&gt;
&lt;br /&gt;
The num_fragments field must be value 1 or &amp;lt;=3 otherwise it will return, there&#039;s two seperate code blocks handling these.&lt;br /&gt;
&lt;br /&gt;
Other than the checks at the start, there&#039;s no validation for the index fields. So large enough values could result in OOB-reads.&lt;br /&gt;
&lt;br /&gt;
When handling multiple fragments, it will loop through the stationinfo list. There is no validation for the u8 count field or the baseindex field. It calls a vfunc from obj baseptr+index*{entrysize} with data from the buffer, where index starts with the above baseindex field. Afterwards, an u8 is copied into an u32 array (with certain versions an u16 is deserialized into an u16 array).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;nn::pia::session::ProcessUpdateMeshJob::UpdateStationDataList&amp;lt;/code&amp;gt; is (eventually) called from &amp;lt;code&amp;gt;nn::pia::session::MeshProtocol::ParseUpdateMesh&amp;lt;/code&amp;gt;, which has similar issues to the above.&lt;br /&gt;
&lt;br /&gt;
Note that ParseJoinResponse/ParseUpdateMesh essentially require the message to be received from the host device.&lt;br /&gt;
&lt;br /&gt;
With fixed versions (v5.18.98, exact version unknown) various validation was added. Additional/updated validation was added in a later version (v5.31.0, exact version unknown).&lt;br /&gt;
| OOB read/write / vfunc call where the object is selected by an OOB index, triggered by a Pia MeshProtocol message.&lt;br /&gt;
| v5.18.98 and v5.31.0 (exact versions unknown).&lt;br /&gt;
| v5.31.0&lt;br /&gt;
| November 18, 2022&lt;br /&gt;
| November 21, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Insecure encryption&lt;br /&gt;
| Originally Pia packets used AES-ECB encryption. As documented [https://github.com/Kinnay/NintendoClients/wiki/Pia-Overview here] it was later changed with v5.7.0 to AES-GCM. Each 0x10-byte block would have the same encrypted block output where the plaintext 0x10-byte data is the same.&lt;br /&gt;
The mechanism for generating the Pia SessionKey for LAN has also changed over time.&lt;br /&gt;
&lt;br /&gt;
The [https://github.com/Kinnay/NintendoClients/wiki/LAN-Protocol LAN] non-Pia-encapsulated packets were also originally sent in plaintext, however at some point it was changed to mostly encrypted.&lt;br /&gt;
| &lt;br /&gt;
| AES-GCM fix: v5.7.0&lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
| &lt;br /&gt;
|-&lt;br /&gt;
| nn::pia::transport::UnreliableProtocol::Dispatch buffer overflow&lt;br /&gt;
| &amp;lt;code&amp;gt;nn::pia::transport::UnreliableProtocol::Dispatch&amp;lt;/code&amp;gt; memcpys data from the message into a list entry, without size validation. If the pia packet is the max size, it will only overwrite the 0xC-bytes which were written to immediately before the memcpy: the u32 size and the 8-byte StationAddress (depending on the version there can also be 4-byte padding after the size for alignment).&lt;br /&gt;
However, nn::pia::transport::UnreliableProtocol::Receive will clamp the size from the list entry to the outbuf size when doing the memcpy. So this is probably useless.&lt;br /&gt;
&lt;br /&gt;
It&#039;s unknown whether there&#039;s a version where more data could be overwritten, and whether that would be useful.&lt;br /&gt;
&lt;br /&gt;
This is fixed in v5.31.0, exact version unknown. The message is dropped if too large in Dispatch.&lt;br /&gt;
| Small buffer overflow triggered by a Pia UnreliableProtocol message.&lt;br /&gt;
| v5.31.0, exact version unknown.&lt;br /&gt;
| v5.18.98/v5.31.0&lt;br /&gt;
| November 2022&lt;br /&gt;
| November 29, 2022&lt;br /&gt;
| [[User:Yellows8|yellows8]]&lt;br /&gt;
|-&lt;br /&gt;
| Uncleared input structs for [[LDN_services|LDN]]&lt;br /&gt;
| The Pia code using ldn CreateNetwork*/ConnectNetwork*/Scan doesn&#039;t properly memset the input data for SecurityConfig/ScanFilter (when keysize is less than 0x40 for the former). Hence, infoleak from games is sent to ldn (structs are located on stack, so stack data is leaked). This requires ldn compromise/mitm to obtain the leaked data - these are not sent over the network.&lt;br /&gt;
With v6.20.1 (exact version unknown - fix isn&#039;t present in v5.32.0), the code using Scan* now clears the input ScanFilter properly. With v6.25.1 (exact version unknown - fix isn&#039;t present in v6.23.3), the code using CreateNetwork*/ConnectNetwork* now clears the input SecurityConfig properly.&lt;br /&gt;
| Infoleak from games with LDN cmds, requires compromised sysmodule/mitm.&lt;br /&gt;
| v6.20.1 and v6.25.1, exact versions unknown.&lt;br /&gt;
| v5.32.0/v6.20.1/v6.23.3/v6.25.1&lt;br /&gt;
| &lt;br /&gt;
| December 7, 2022&lt;br /&gt;
| &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== ENL ===&lt;br /&gt;
This section documents vulnerabilities for [https://github.com/kinnay/NintendoClients/wiki/ENL-Protocol ENL].&lt;br /&gt;
A framework used by Nintendo games including Mario Kart 8 Deluxe, Splatoon 2 / 3, Mario Maker 2, and more.&lt;br /&gt;
&lt;br /&gt;
Fun fact, this library appears to re-use network code and concepts from older Nintendo titles such as Mario Kart 7 and some Wii multiplayer games.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
!  Summary&lt;br /&gt;
!  Description&lt;br /&gt;
!  Successful exploitation result&lt;br /&gt;
!  Fixed in Enl version&lt;br /&gt;
!  Last Enl version this flaw was checked for&lt;br /&gt;
!  Timeframe this was discovered&lt;br /&gt;
!  Public disclosure timeframe&lt;br /&gt;
!  Discovered by&lt;br /&gt;
|-&lt;br /&gt;
| enl::TransportManager::updateReceiveBuffer_() nullptr deref&lt;br /&gt;
| enl::TransportManager::updateReceiveBuffer_() is called when the ENL framework receives a PIA packet from a client, it will fully trust the ENL header which includes a &amp;quot;ContentTransporter&amp;quot; type (ID) and a length.&lt;br /&gt;
The function will try to fetch the content transporter by ID using &amp;lt;code&amp;gt;enl::TransportManager::getContentTransporter(unsigned char const &amp;amp;)&amp;lt;/code&amp;gt;, it returns NULL if there&#039;s no content transporter with the same ID&lt;br /&gt;
&lt;br /&gt;
*NOTE: The function may be inlined&lt;br /&gt;
&lt;br /&gt;
Then it will try to call a virtual method: &amp;lt;code&amp;gt;virtual size_t readyReceiveStream(enl::RamReadStream&amp;amp;, enl::Buffer*, size_t)&amp;lt;/code&amp;gt;, dereferencing the pointer to fetch the vtable ptr&lt;br /&gt;
&lt;br /&gt;
[https://gist.github.com/Rambo6Glaz/c088e2ed7a12db08f6322e9f7a3c4911 Pseudocode of the function before it was fixed]&lt;br /&gt;
&lt;br /&gt;
| nullptr dereference triggered by an invalid content transporter type in the ENL header (it will crash the game/process)&lt;br /&gt;
| Unknown&lt;br /&gt;
| Depends on the game&lt;br /&gt;
| Early April 2022&lt;br /&gt;
| November 16, 2022&lt;br /&gt;
| [[User:Rambo6Glaz|Rambo6Glaz]], Yannik (massive RE help)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
There&#039;s another one more interesting but it will have to wait a bit :)&lt;/div&gt;</summary>
		<author><name>Yannik</name></author>
	</entry>
	<entry>
		<id>https://switchbrew.org/w/index.php?title=ETicket_services&amp;diff=13907</id>
		<title>ETicket services</title>
		<link rel="alternate" type="text/html" href="https://switchbrew.org/w/index.php?title=ETicket_services&amp;diff=13907"/>
		<updated>2025-09-23T10:44:36Z</updated>

		<summary type="html">&lt;p&gt;Yannik: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= es =&lt;br /&gt;
This is &amp;quot;nn::es::IETicketService&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Cmd || Name&lt;br /&gt;
|-&lt;br /&gt;
| 1 || ImportTicket&lt;br /&gt;
|-&lt;br /&gt;
| 2 || ImportTicketCertificateSet&lt;br /&gt;
|-&lt;br /&gt;
| 3 || DeleteTicket&lt;br /&gt;
|-&lt;br /&gt;
| 4 || [1.0.0-13.2.1] DeletePersonalizedTicket&lt;br /&gt;
|-&lt;br /&gt;
| 5 || DeleteAllCommonTicket&lt;br /&gt;
|-&lt;br /&gt;
| 6 || DeleteAllPersonalizedTicket&lt;br /&gt;
|-&lt;br /&gt;
| 7 || DeleteAllPersonalizedTicketEx&lt;br /&gt;
|-&lt;br /&gt;
| 8 || [2.0.0-5.1.0] GetTitleKey&lt;br /&gt;
|-&lt;br /&gt;
| 9 || CountCommonTicket&lt;br /&gt;
|-&lt;br /&gt;
| 10 || CountPersonalizedTicket&lt;br /&gt;
|-&lt;br /&gt;
| 11 || [6.0.0+] ListCommonTicketRightsIds ([2.0.0-5.1.0] ListCommonTicket)&lt;br /&gt;
|-&lt;br /&gt;
| 12 || [6.0.0+] ListPersonalizedTicketRightsIds ([2.0.0-5.1.0] ListPersonalizedTicket)&lt;br /&gt;
|-&lt;br /&gt;
| 13 || ListMissingPersonalizedTicket&lt;br /&gt;
|-&lt;br /&gt;
| 14 || GetCommonTicketSize&lt;br /&gt;
|-&lt;br /&gt;
| 15 || [2.0.0-4.1.0] GetPersonalizedTicketSize&lt;br /&gt;
|-&lt;br /&gt;
| 16 || GetCommonTicketData&lt;br /&gt;
|-&lt;br /&gt;
| 17 || [2.0.0-4.1.0] GetPersonalizedTicketData&lt;br /&gt;
|-&lt;br /&gt;
| 18 || OwnTicket&lt;br /&gt;
|-&lt;br /&gt;
| 19 || GetTicketInfo&lt;br /&gt;
|-&lt;br /&gt;
| 20 || ListLightTicketInfo&lt;br /&gt;
|-&lt;br /&gt;
| 21 || [2.0.0-6.2.0] SignData&lt;br /&gt;
|-&lt;br /&gt;
| 22 || [4.0.0+] GetCommonTicketAndCertificateSize&lt;br /&gt;
|-&lt;br /&gt;
| 23 || [4.0.0+] GetCommonTicketAndCertificateData&lt;br /&gt;
|-&lt;br /&gt;
| 24 || [4.0.0+] ImportPrepurchaseRecord&lt;br /&gt;
|-&lt;br /&gt;
| 25 || [4.0.0+] DeletePrepurchaseRecord&lt;br /&gt;
|-&lt;br /&gt;
| 26 || [4.0.0+] DeleteAllPrepurchaseRecord&lt;br /&gt;
|-&lt;br /&gt;
| 27 || [4.0.0+] CountPrepurchaseRecord&lt;br /&gt;
|-&lt;br /&gt;
| 28 || [6.0.0+] ListPrepurchaseRecordRightsIds ([4.0.0-5.1.0] ListPrepurchaseRecord)&lt;br /&gt;
|-&lt;br /&gt;
| 29 || [4.0.0+] [[#ListPrepurchaseRecordInfo]]&lt;br /&gt;
|-&lt;br /&gt;
| 30 || [5.0.0+] CountTicket&lt;br /&gt;
|-&lt;br /&gt;
| 31 || [5.0.0+] ListTicketRightsIds&lt;br /&gt;
|-&lt;br /&gt;
| 32 || [5.0.0+] CountPrepurchaseRecordEx&lt;br /&gt;
|-&lt;br /&gt;
| 33 || [5.0.0-6.2.0] ListPrepurchaseRecordRightsIdsEx&lt;br /&gt;
|-&lt;br /&gt;
| 34 || [5.0.0+] GetEncryptedTicketSize&lt;br /&gt;
|-&lt;br /&gt;
| 35 || [5.0.0+] [[#GetEncryptedTicketData]]&lt;br /&gt;
|-&lt;br /&gt;
| 36 || [6.0.0-12.1.0] DeleteAllInactiveELicenseRequiredPersonalizedTicket&lt;br /&gt;
|-&lt;br /&gt;
| 37 || [9.0.0+] OwnTicket2&lt;br /&gt;
|-&lt;br /&gt;
| 38 || [9.0.0+] OwnTicket3&lt;br /&gt;
|-&lt;br /&gt;
| 39 || [13.0.0+] DeleteAllInactivePersonalizedTicket&lt;br /&gt;
|-&lt;br /&gt;
| 40 || [13.0.0+] DeletePrepurchaseRecordByNintendoAccountId&lt;br /&gt;
|-&lt;br /&gt;
| 101 || [18.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 102 || [18.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 103 || [18.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 104 || [18.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 105 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 201 || [18.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 202 || [18.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 203 || [18.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 204 || [18.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 501 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 502 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 503 || [6.0.0+] [[#GetTitleKey]]&lt;br /&gt;
|-&lt;br /&gt;
| 504 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 508 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 509 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 510 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 511 || [9.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1001 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1002 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1003 || [6.0.0+] Returns an [[#IAsyncValue]].&lt;br /&gt;
|-&lt;br /&gt;
| 1004 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1005 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1006 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1007 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1009 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1010 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1011 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1012 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1013 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1014 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1015 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1016 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1017 || [9.0.0+] &lt;br /&gt;
|-&lt;br /&gt;
| 1018 || [9.0.0+] &lt;br /&gt;
|-&lt;br /&gt;
| 1019 || [9.0.0+] &lt;br /&gt;
|-&lt;br /&gt;
| 1020 || [9.0.0+] &lt;br /&gt;
|-&lt;br /&gt;
| 1021 || [9.0.0+] &lt;br /&gt;
|-&lt;br /&gt;
| 1022 || [15.0.0+] &lt;br /&gt;
|-&lt;br /&gt;
| 1023 || [17.0.0+] &lt;br /&gt;
|-&lt;br /&gt;
| 1024 || [17.0.0+] &lt;br /&gt;
|-&lt;br /&gt;
| 1025 || [17.0.0+] &lt;br /&gt;
|-&lt;br /&gt;
| 1026 || [17.0.0+] &lt;br /&gt;
|-&lt;br /&gt;
| 1027 || [17.0.0+] &lt;br /&gt;
|-&lt;br /&gt;
| 1028 || [18.0.0+] &lt;br /&gt;
|-&lt;br /&gt;
| 1029 || [19.0.0+] &lt;br /&gt;
|-&lt;br /&gt;
| 1030 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1031 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1032 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1033 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1034 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1035 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1036 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1037 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1501 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1502 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1503 || [6.0.0+] Creates an authentication challenge for LDN&lt;br /&gt;
|-&lt;br /&gt;
| 1504 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1505 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1506 || [13.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1601 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1602 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1603 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1604 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1605 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 1606 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 2000 || [6.0.0+] No input, returns an [[#IActiveRightsContext]].&lt;br /&gt;
|-&lt;br /&gt;
| 2001 || [9.0.0+] No input, returns an [[#IActiveRightsContext]].&lt;br /&gt;
|-&lt;br /&gt;
| 2002 || [13.0.0-16.1.0]&lt;br /&gt;
|-&lt;br /&gt;
| 2003 || [13.0.0-16.1.0]&lt;br /&gt;
|-&lt;br /&gt;
| 2100 || [7.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 2501 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 2502 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 2601 || [13.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 3001 || [7.0.0-15.0.1]&lt;br /&gt;
|-&lt;br /&gt;
| 3002 || [7.0.0-15.0.1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ListPrepurchaseRecordInfo ==&lt;br /&gt;
[7.0.0+] 0x8-bytes of input for ListPrepurchaseRecordInfo was removed.&lt;br /&gt;
&lt;br /&gt;
== GetEncryptedTicketData ==&lt;br /&gt;
[6.0.0+] Now returns an additional 8-bytes.&lt;br /&gt;
&lt;br /&gt;
== GetTitleKey ==&lt;br /&gt;
[3.0.0+] GetTitleKey now takes an additional 4-bytes of input.&lt;br /&gt;
&lt;br /&gt;
== Cmd1027 ==&lt;br /&gt;
Takes 0x10-bytes of input, a type-0x6 output buffer containing an array of 0x10-byte entries, and a type-0x5 input buffer. Returns 4-bytes of output.&lt;br /&gt;
&lt;br /&gt;
[19.0.0+] Takes 8-bytes of input, a type-0x6 output buffer containing an array of 0x10-byte entries, a type-0x5 input buffer containing an array of 8-byte entries, and a type-0x5 input buffer. Returns 4-bytes of output.&lt;br /&gt;
&lt;br /&gt;
== IAsyncValue ==&lt;br /&gt;
This is &amp;quot;nn::es::detail::IAsyncValue&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
This was added with [6.0.0+].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Cmd || Name&lt;br /&gt;
|-&lt;br /&gt;
| 0 || &lt;br /&gt;
|-&lt;br /&gt;
| 1 || &lt;br /&gt;
|-&lt;br /&gt;
| 2 || &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== IActiveRightsContext ==&lt;br /&gt;
This is &amp;quot;nn::es::IActiveRightsContext&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
This was added with [6.0.0+].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Cmd || Name&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &lt;br /&gt;
|-&lt;br /&gt;
| 2 || &lt;br /&gt;
|-&lt;br /&gt;
| 3 || &lt;br /&gt;
|-&lt;br /&gt;
| 4 || &lt;br /&gt;
|-&lt;br /&gt;
| 5 || [6.0.0-14.1.2]&lt;br /&gt;
|-&lt;br /&gt;
| 6 || &lt;br /&gt;
|-&lt;br /&gt;
| 7 || &lt;br /&gt;
|-&lt;br /&gt;
| 8 || &lt;br /&gt;
|-&lt;br /&gt;
| 9 || &lt;br /&gt;
|-&lt;br /&gt;
| 10 || &lt;br /&gt;
|-&lt;br /&gt;
| 11 || [8.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 12 || [10.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 13 || [11.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 14 || [13.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 15 || [13.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 16 || [13.1.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 17 || [14.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 18 || [16.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 100 || &lt;br /&gt;
|-&lt;br /&gt;
| 101 || &lt;br /&gt;
|-&lt;br /&gt;
| 102 || &lt;br /&gt;
|-&lt;br /&gt;
| 200 || &lt;br /&gt;
|-&lt;br /&gt;
| 201 || &lt;br /&gt;
|-&lt;br /&gt;
| 202 || &lt;br /&gt;
|-&lt;br /&gt;
| 203 || &lt;br /&gt;
|-&lt;br /&gt;
| 210 || &lt;br /&gt;
|-&lt;br /&gt;
| 211 || &lt;br /&gt;
|-&lt;br /&gt;
| 212 || [13.0.0-16.1.0]&lt;br /&gt;
|-&lt;br /&gt;
| 213 || [13.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 214 || [14.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 215 || [14.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 216 || [15.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 300 || [13.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 900 || &lt;br /&gt;
|-&lt;br /&gt;
| 901 || &lt;br /&gt;
|-&lt;br /&gt;
| 1000 || &lt;br /&gt;
|-&lt;br /&gt;
| 1001 || [7.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[8.0.0+] Cmd201 now returns an additional 0x8-bytes of output.&lt;br /&gt;
&lt;br /&gt;
= ndrm:lu =&lt;br /&gt;
This is &amp;quot;nn::ndrm::low::detail::INdrmLowUserInterface&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
This was added with [13.0.0+]. &lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Cmd || Name&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &lt;br /&gt;
|-&lt;br /&gt;
| 2 || &lt;br /&gt;
|-&lt;br /&gt;
| 3 || &lt;br /&gt;
|-&lt;br /&gt;
| 1000 || &lt;br /&gt;
|-&lt;br /&gt;
| 8000 || &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= ndrm:la =&lt;br /&gt;
This is &amp;quot;nn::ndrm::low::detail::INdrmLowAdminInterface&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
This was added with [13.0.0+].&lt;br /&gt;
 &lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Cmd || Name&lt;br /&gt;
|-&lt;br /&gt;
| 1 || &lt;br /&gt;
|-&lt;br /&gt;
| 2 || &lt;br /&gt;
|-&lt;br /&gt;
| 3 || &lt;br /&gt;
|-&lt;br /&gt;
| 4 ||&lt;br /&gt;
|-&lt;br /&gt;
| 5 || &lt;br /&gt;
|-&lt;br /&gt;
| 6 || &lt;br /&gt;
|-&lt;br /&gt;
| 7 || &lt;br /&gt;
|-&lt;br /&gt;
| 8 || &lt;br /&gt;
|-&lt;br /&gt;
| 9 || &lt;br /&gt;
|-&lt;br /&gt;
| 10 || &lt;br /&gt;
|-&lt;br /&gt;
| 11 || &lt;br /&gt;
|-&lt;br /&gt;
| 12 || &lt;br /&gt;
|-&lt;br /&gt;
| 13 || &lt;br /&gt;
|-&lt;br /&gt;
| 14 ||&lt;br /&gt;
|-&lt;br /&gt;
| 15 ||&lt;br /&gt;
|-&lt;br /&gt;
| 16 ||&lt;br /&gt;
|-&lt;br /&gt;
| 17 ||&lt;br /&gt;
|-&lt;br /&gt;
| 18 ||&lt;br /&gt;
|-&lt;br /&gt;
| 19 ||&lt;br /&gt;
|-&lt;br /&gt;
| 20 || &lt;br /&gt;
|-&lt;br /&gt;
| 21 || &lt;br /&gt;
|-&lt;br /&gt;
| 22 || &lt;br /&gt;
|-&lt;br /&gt;
| 23 || &lt;br /&gt;
|-&lt;br /&gt;
| 24 || &lt;br /&gt;
|-&lt;br /&gt;
| 25 || &lt;br /&gt;
|-&lt;br /&gt;
| 26 ||        &lt;br /&gt;
|-&lt;br /&gt;
| 27 || &lt;br /&gt;
|-&lt;br /&gt;
| 28 ||&lt;br /&gt;
|-&lt;br /&gt;
| 29 || &lt;br /&gt;
|-&lt;br /&gt;
| 30 || &lt;br /&gt;
|-&lt;br /&gt;
| 31 ||&lt;br /&gt;
|-&lt;br /&gt;
| 32 ||&lt;br /&gt;
|-&lt;br /&gt;
| 33 || [13.1.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 34 || [13.1.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 35 || [13.1.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 36 || [13.1.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 37 || [14.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 38 || [14.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 39 || [14.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 40 || [15.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 42 || [15.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 43 || [15.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 44 || [15.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 45 || [17.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 46 || [18.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 47 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 48 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 49 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 50 || [20.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 8000 ||&lt;br /&gt;
|-&lt;br /&gt;
| 8001 ||&lt;br /&gt;
|-&lt;br /&gt;
| 8002 ||&lt;br /&gt;
|-&lt;br /&gt;
| 8003 || [13.0.0-13.2.1]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Cmd3 ==&lt;br /&gt;
Takes a total of 8-bytes of input and a type-0x5 input buffer, no output.&lt;br /&gt;
&lt;br /&gt;
[15.0.0+] Now takes a total of 0x18-bytes of input and a type-0x5 input buffer, no output.&lt;/div&gt;</summary>
		<author><name>Yannik</name></author>
	</entry>
	<entry>
		<id>https://switchbrew.org/w/index.php?title=SSL_services&amp;diff=13653</id>
		<title>SSL services</title>
		<link rel="alternate" type="text/html" href="https://switchbrew.org/w/index.php?title=SSL_services&amp;diff=13653"/>
		<updated>2025-07-27T08:29:46Z</updated>

		<summary type="html">&lt;p&gt;Yannik: Document ApiVersion 5&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= ssl =&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::ISslService&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The sdknso uses SessionManager with this, where the additional session-count is user-specified (default is 0x2). An error is thrown when the input value is less than 1 or &amp;gt;4.&lt;br /&gt;
&lt;br /&gt;
This service implements client-mode TLS using [https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS NSS].&lt;br /&gt;
&lt;br /&gt;
Note that SystemPrograms generally use [[#RegisterInternalPki]] (see [[libcurl]]). However, [[Internet_Browser|web-applets]] use [[#GetCertificates]] with [[#CaCertificateId]] All - [[#RegisterInternalPki]] is not used.&lt;br /&gt;
&lt;br /&gt;
This has IPC max_sessions = 0x40.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Cmd || Name&lt;br /&gt;
|-&lt;br /&gt;
| 0 || [[#CreateContext]]&lt;br /&gt;
|-&lt;br /&gt;
| 1 || [[#GetContextCount]]&lt;br /&gt;
|-&lt;br /&gt;
| 2 || [[#GetCertificates]]&lt;br /&gt;
|-&lt;br /&gt;
| 3 || [[#GetCertificateBufSize]]&lt;br /&gt;
|-&lt;br /&gt;
| 4 || [3.0.0+] [[#DebugIoctl]]&lt;br /&gt;
|-&lt;br /&gt;
| 5 || [3.0.0+] [[#SetInterfaceVersion]]&lt;br /&gt;
|-&lt;br /&gt;
| 6 || [5.0.0+] [[#FlushSessionCache]]&lt;br /&gt;
|-&lt;br /&gt;
| 7 || [6.0.0+] [[#SetDebugOption]]&lt;br /&gt;
|-&lt;br /&gt;
| 8 || [6.0.0+] [[#GetDebugOption]]&lt;br /&gt;
|-&lt;br /&gt;
| 9 || [14.0.0+] [[#ClearTls12FallbackFlag]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== CreateContext ==&lt;br /&gt;
Takes a PID, an input u32 [[#SslVersion]], an input u64 pid_placeholder, and returns an output [[#ISslContext]].&lt;br /&gt;
&lt;br /&gt;
== GetContextCount ==&lt;br /&gt;
No input, returns an output u32.&lt;br /&gt;
&lt;br /&gt;
This is not exposed by sdknso.&lt;br /&gt;
&lt;br /&gt;
== GetCertificates ==&lt;br /&gt;
Takes a type-0x6 output buffer and a type-0x5 input buffer containing an array of [[#CaCertificateId]].&lt;br /&gt;
&lt;br /&gt;
[3.0.0+] This now returns an output u32 for actual total output entries.&lt;br /&gt;
&lt;br /&gt;
The output buffer starts with an array of [[#BuiltInCertificateInfo]], with the DER cert data following afterwards.&lt;br /&gt;
&lt;br /&gt;
== GetCertificateBufSize ==&lt;br /&gt;
Takes a type-0x5 input buffer containing an array of [[#CaCertificateId]], returns an output u32 for the size to use with [[#GetCertificates]].&lt;br /&gt;
&lt;br /&gt;
== DebugIoctl ==&lt;br /&gt;
Stubbed on retail, just returns an error.&lt;br /&gt;
&lt;br /&gt;
== SetInterfaceVersion ==&lt;br /&gt;
Takes an input u32 &#039;&#039;&#039;version&#039;&#039;&#039;, no output.&lt;br /&gt;
&lt;br /&gt;
Used by user-processes during service init.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value || SystemVersion&lt;br /&gt;
|-&lt;br /&gt;
| 0x1 || [3.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 0x2 || [5.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 0x3 || [6.0.0+]&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || [20.0.0+]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== FlushSessionCache ==&lt;br /&gt;
Takes a type-0x5 input buffer, an input u32 [[#FlushSessionCacheOptionType]], returns an output u32.&lt;br /&gt;
&lt;br /&gt;
The input buffer contains a NUL-terminated string, which is only used when the type is value 0. For type 1, an empty buffer is passed (addr=NULL/size=0).&lt;br /&gt;
&lt;br /&gt;
== SetDebugOption ==&lt;br /&gt;
Takes an input u32 [[#DebugOptionType]] and a type-0x5 input buffer, no output.&lt;br /&gt;
&lt;br /&gt;
The input u32 value must be 0, and the buffer addr/size must not be 0.&lt;br /&gt;
&lt;br /&gt;
The u8 at buf+0 is copied to state.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;nn::ssl::SetDebugOption&amp;lt;/code&amp;gt; func in sdknso just verifies the input and that the service is initialized, without actually using the cmd.&lt;br /&gt;
&lt;br /&gt;
== GetDebugOption ==&lt;br /&gt;
Takes an input u32 [[#DebugOptionType]] and a type-0x6 output buffer.&lt;br /&gt;
&lt;br /&gt;
Same as [[#SetDebugOption]] except this copies state to the buffer instead.&lt;br /&gt;
&lt;br /&gt;
== ClearTls12FallbackFlag ==&lt;br /&gt;
No input/output.&lt;br /&gt;
&lt;br /&gt;
== ISslContext ==&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::ISslContext&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Cmd || Name&lt;br /&gt;
|-&lt;br /&gt;
| 0 || [[#SetOption]]&lt;br /&gt;
|-&lt;br /&gt;
| 1 || [[#GetOption]]&lt;br /&gt;
|-&lt;br /&gt;
| 2 || [[#CreateConnection]]&lt;br /&gt;
|-&lt;br /&gt;
| 3 || [[#GetConnectionCount]]&lt;br /&gt;
|-&lt;br /&gt;
| 4 || [[#ImportServerPki]]&lt;br /&gt;
|-&lt;br /&gt;
| 5 || [[#ImportClientPki]]&lt;br /&gt;
|-&lt;br /&gt;
| 6 || [[#RemoveServerPki]]&lt;br /&gt;
|-&lt;br /&gt;
| 7 || [[#RemoveClientPki]]&lt;br /&gt;
|-&lt;br /&gt;
| 8 || [[#RegisterInternalPki]]&lt;br /&gt;
|-&lt;br /&gt;
| 9 || [[#AddPolicyOid]]&lt;br /&gt;
|-&lt;br /&gt;
| 10 || [3.0.0+] [[#ImportCrl]]&lt;br /&gt;
|-&lt;br /&gt;
| 11 || [3.0.0+] [[#RemoveCrl]]&lt;br /&gt;
|-&lt;br /&gt;
| 12 || [16.0.0+] [[#ImportClientCertKeyPki]]&lt;br /&gt;
|-&lt;br /&gt;
| 13 || [16.0.0+] [[#GeneratePrivateKeyAndCert]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== SetOption ===&lt;br /&gt;
Takes an input [[#ContextOption]] and an input s32, no output.&lt;br /&gt;
&lt;br /&gt;
With [[#ContextOption]] value 1, the s32 has to be 0 or 1 (state field is set to the s32 value).&lt;br /&gt;
&lt;br /&gt;
Prior to 4.x this is stubbed.&lt;br /&gt;
&lt;br /&gt;
=== GetOption ===&lt;br /&gt;
Takes an input [[#ContextOption]], returns an output s32.&lt;br /&gt;
&lt;br /&gt;
Prior to 4.x this is stubbed.&lt;br /&gt;
&lt;br /&gt;
=== CreateConnection ===&lt;br /&gt;
No input, returns an [[#ISslConnection]].&lt;br /&gt;
&lt;br /&gt;
=== GetConnectionCount ===&lt;br /&gt;
No input, returns an output u32.&lt;br /&gt;
&lt;br /&gt;
This is not exposed by sdknso. Immediately prior to closing the [[#ISslContext]] object, sdknso uses this cmd, returning the error from there on failure. An error is also thrown if the output count is non-zero.&lt;br /&gt;
&lt;br /&gt;
=== ImportServerPki ===&lt;br /&gt;
Takes a type-0x5 input buffer and a [[#CertificateFormat]], returns an output u64 Id.&lt;br /&gt;
&lt;br /&gt;
The input buffer can contain multiple certs. A maximum of 71 ServerPki objects (associated with the output Id) can be imported.&lt;br /&gt;
&lt;br /&gt;
The certs can be CAs or server certs (no pubkeys).&lt;br /&gt;
&lt;br /&gt;
The [[#CertificateFormat]] only validated, afterwards it&#039;s ignored.&lt;br /&gt;
&lt;br /&gt;
=== ImportClientPki ===&lt;br /&gt;
Takes two type-0x5 input buffers, returns an output u64 Id.&lt;br /&gt;
&lt;br /&gt;
The first buffer contains the PKCS#12 data, the second buffer contains the optional (addr=NULL/size=0) ASCII password. The password is copied to a heap buffer with size+1, for NUL-termination.&lt;br /&gt;
&lt;br /&gt;
An error is thrown if this cmd or [[#RegisterInternalPki]] was already used previously.&lt;br /&gt;
&lt;br /&gt;
=== RemoveServerPki ===&lt;br /&gt;
Takes an input u64 Id, no output.&lt;br /&gt;
&lt;br /&gt;
=== RemoveClientPki ===&lt;br /&gt;
Takes an input u64 Id, no output.&lt;br /&gt;
&lt;br /&gt;
=== RegisterInternalPki ===&lt;br /&gt;
Takes an input [[#InternalPki]], returns an output u64 Id.&lt;br /&gt;
&lt;br /&gt;
An error is thrown if this cmd or [[#ImportClientPki]] was already used previously.&lt;br /&gt;
&lt;br /&gt;
=== AddPolicyOid ===&lt;br /&gt;
Takes a type-0x5 input buffer, no output.&lt;br /&gt;
&lt;br /&gt;
The buffer contains a string. The string length must not match the buffer size, and the string length must be &amp;lt;=0xFE.&lt;br /&gt;
&lt;br /&gt;
=== ImportCrl ===&lt;br /&gt;
Takes a type-0x5 input buffer, returns an output u64 Id.&lt;br /&gt;
&lt;br /&gt;
The input buffer contains the DER CRL.&lt;br /&gt;
&lt;br /&gt;
=== RemoveCrl ===&lt;br /&gt;
Takes an input u64 Id, no output.&lt;br /&gt;
&lt;br /&gt;
=== ImportClientCertKeyPki ===&lt;br /&gt;
Takes two type-0x5 input buffers and a [[#CertificateFormat]], returns an output u64 Id.&lt;br /&gt;
&lt;br /&gt;
This imports the specified client cert (first inbuf) and key (second inbuf). The [[#CertificateFormat]] controls the format for the cert and key.&lt;br /&gt;
&lt;br /&gt;
=== GeneratePrivateKeyAndCert ===&lt;br /&gt;
Takes two type-0x6 output buffers, a type-0x5 input buffer containing a [[#KeyAndCertParams]], and an u32, returns two output u32s.&lt;br /&gt;
&lt;br /&gt;
Official sw passes hard-coded value 1 for the u32. Sysmodule will throw an error if the u32 is not value 1.&lt;br /&gt;
&lt;br /&gt;
The input buffer size must match the size of [[#KeyAndCertParams]] (0x58-bytes).&lt;br /&gt;
&lt;br /&gt;
This generates a self-signed DER cert/key with algo sha256WithRSAEncryption, with the cert expiring in exactly 30 days from the begin-timestamp.&lt;br /&gt;
&lt;br /&gt;
The first outbuf contains the cert, the second outbuf contains the key. The output u32s are the actual output size of the cert and key.&lt;br /&gt;
&lt;br /&gt;
Sample cert:&lt;br /&gt;
&lt;br /&gt;
  Certificate:&lt;br /&gt;
    Data:&lt;br /&gt;
        Version: 1 (0x0)&lt;br /&gt;
        Serial Number: {...}&lt;br /&gt;
        Signature Algorithm: sha256WithRSAEncryption&lt;br /&gt;
        Issuer: CN = {input}&lt;br /&gt;
        Validity&lt;br /&gt;
            Not Before: {...}&lt;br /&gt;
            Not After : {...}&lt;br /&gt;
        Subject: CN = {input}&lt;br /&gt;
        Subject Public Key Info:&lt;br /&gt;
            Public Key Algorithm: rsaEncryption&lt;br /&gt;
                Public-Key: {input bit-size}&lt;br /&gt;
                Modulus:&lt;br /&gt;
                    {...}&lt;br /&gt;
                Exponent: {input}&lt;br /&gt;
    Signature Algorithm: sha256WithRSAEncryption&lt;br /&gt;
    Signature Value:&lt;br /&gt;
        {...}&lt;br /&gt;
&lt;br /&gt;
=== ISslConnection ===&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::ISslConnection&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Cmd || Name&lt;br /&gt;
|-&lt;br /&gt;
| 0 || [[#SetSocketDescriptor]]&lt;br /&gt;
|-&lt;br /&gt;
| 1 || [[#SetHostName]]&lt;br /&gt;
|-&lt;br /&gt;
| 2 || [[#SetVerifyOption]]&lt;br /&gt;
|-&lt;br /&gt;
| 3 || [[#SetIoMode]]&lt;br /&gt;
|-&lt;br /&gt;
| 4 || [[#GetSocketDescriptor]]&lt;br /&gt;
|-&lt;br /&gt;
| 5 || [[#GetHostName]]&lt;br /&gt;
|-&lt;br /&gt;
| 6 || [[#GetVerifyOption]]&lt;br /&gt;
|-&lt;br /&gt;
| 7 || [[#GetIoMode]]&lt;br /&gt;
|-&lt;br /&gt;
| 8 || [[#DoHandshake]]&lt;br /&gt;
|-&lt;br /&gt;
| 9 || [[#DoHandshakeGetServerCert]]&lt;br /&gt;
|-&lt;br /&gt;
| 10 || [[#Read]]&lt;br /&gt;
|-&lt;br /&gt;
| 11 || [[#Write]]&lt;br /&gt;
|-&lt;br /&gt;
| 12 || [[#Pending]]&lt;br /&gt;
|-&lt;br /&gt;
| 13 || [[#Peek]]&lt;br /&gt;
|-&lt;br /&gt;
| 14 || [[#Poll]]&lt;br /&gt;
|-&lt;br /&gt;
| 15 || [[#GetVerifyCertError]]&lt;br /&gt;
|-&lt;br /&gt;
| 16 || [[#GetNeededServerCertBufferSize]]&lt;br /&gt;
|-&lt;br /&gt;
| 17 || [[#SetSessionCacheMode]]&lt;br /&gt;
|-&lt;br /&gt;
| 18 || [[#GetSessionCacheMode]]&lt;br /&gt;
|-&lt;br /&gt;
| 19 || [[#FlushSessionCache]]&lt;br /&gt;
|-&lt;br /&gt;
| 20 || [[#SetRenegotiationMode]]&lt;br /&gt;
|-&lt;br /&gt;
| 21 || [[#GetRenegotiationMode]]&lt;br /&gt;
|-&lt;br /&gt;
| 22 || [[#SetOption_2|SetOption]]&lt;br /&gt;
|-&lt;br /&gt;
| 23 || [[#GetOption_2|GetOption]]&lt;br /&gt;
|-&lt;br /&gt;
| 24 || [[#GetVerifyCertErrors]]&lt;br /&gt;
|-&lt;br /&gt;
| 25 || [4.0.0+] [[#GetCipherInfo]]&lt;br /&gt;
|-&lt;br /&gt;
| 26 || [9.0.0+] [[#SetNextAlpnProto]]&lt;br /&gt;
|-&lt;br /&gt;
| 27 || [9.0.0+] [[#GetNextAlpnProto]]&lt;br /&gt;
|-&lt;br /&gt;
| 28 || [16.0.0+] [[#SetDtlsSocketDescriptor]]&lt;br /&gt;
|-&lt;br /&gt;
| 29 || [16.0.0+] [[#GetDtlsHandshakeTimeout]]&lt;br /&gt;
|-&lt;br /&gt;
| 30 || [16.0.0+] [[#SetPrivateOption]]&lt;br /&gt;
|-&lt;br /&gt;
| 31 || [16.0.0+] [[#SetSrtpCiphers]]&lt;br /&gt;
|-&lt;br /&gt;
| 32 || [16.0.0+] [[#GetSrtpCipher]]&lt;br /&gt;
|-&lt;br /&gt;
| 33 || [16.0.0+] [[#ExportKeyingMaterial]]&lt;br /&gt;
|-&lt;br /&gt;
| 34 || [16.0.0+] [[#SetIoTimeout]]&lt;br /&gt;
|-&lt;br /&gt;
| 35 || [16.0.0+] [[#GetIoTimeout]]&lt;br /&gt;
|-&lt;br /&gt;
| 36 || [20.0.0+] GetSessionTicket&lt;br /&gt;
|-&lt;br /&gt;
| 37 || [20.0.0+] SetSessionTicket&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== SetSocketDescriptor ====&lt;br /&gt;
Takes an input s32 sockfd, returns an output s32 sockfd.&lt;br /&gt;
&lt;br /&gt;
An error is thrown if this was used previously.&lt;br /&gt;
&lt;br /&gt;
internal_sockfd = output from [[Sockets_services#DuplicateSocket|DuplicateSocket]], with the input sockfd. If the field which would be used for the input u64 (PID set by [[#CreateContext]]) is zero however, it instead uses internal_sockfd=input_sockfd directly without DuplicateSocket and later returns -1 for the sockfd. An error is thrown if DuplicateSocket fails. The input sockfd is later returned as the output sockfd, however if [[#OptionType|DoNotCloseSocket]] is set it will instead return -1 for the sockfd. Then [[Sockets_services#GetPeerName|GetPeerName]] is used with internal_sockfd, throwing an error if this fails. internal_sockfd is used for state initialization, and the input_sockfd is written into state.&lt;br /&gt;
&lt;br /&gt;
Immediately prior to closing the [[#ISslConnection]] object, sdknso will close the sockfd which was returned by this cmd if it is not negative.&lt;br /&gt;
&lt;br /&gt;
==== SetHostName ====&lt;br /&gt;
Takes a type-0x5 input buffer, no output.&lt;br /&gt;
&lt;br /&gt;
The input buffer contains a string, the buffer size must be &amp;lt;=0xFF.&lt;br /&gt;
&lt;br /&gt;
The buffer is copied to a tmpbuf, then [[Sockets_services|nsd]] ResolveEx is used with this tmpbuf to set the HostName in state.&lt;br /&gt;
&lt;br /&gt;
==== SetVerifyOption ====&lt;br /&gt;
Takes an input u32 [[#VerifyOption]], no output.&lt;br /&gt;
&lt;br /&gt;
==== SetIoMode ====&lt;br /&gt;
Takes an input [[#IoMode]], no output.&lt;br /&gt;
&lt;br /&gt;
[[#SetSocketDescriptor]] must have been used prior to this successfully.&lt;br /&gt;
&lt;br /&gt;
==== GetSocketDescriptor ====&lt;br /&gt;
No input, returns an output s32.&lt;br /&gt;
&lt;br /&gt;
[[#SetSocketDescriptor]] must have been used prior to this successfully.&lt;br /&gt;
&lt;br /&gt;
This gets the input_sockfd which was previously saved in state by [[#SetSocketDescriptor]].&lt;br /&gt;
&lt;br /&gt;
==== GetHostName ====&lt;br /&gt;
Takes a type-0x6 output buffer, returns an output u32.&lt;br /&gt;
&lt;br /&gt;
The output u32 is the string length (buffer must be large enough for the entire string).&lt;br /&gt;
&lt;br /&gt;
==== GetVerifyOption ====&lt;br /&gt;
No input, returns an output u32 [[#VerifyOption]].&lt;br /&gt;
&lt;br /&gt;
==== GetIoMode ====&lt;br /&gt;
No input, returns an output [[#IoMode]].&lt;br /&gt;
&lt;br /&gt;
==== DoHandshake ====&lt;br /&gt;
No input/output.&lt;br /&gt;
&lt;br /&gt;
[[#SetSocketDescriptor]] must have been used prior to this successfully.&lt;br /&gt;
&lt;br /&gt;
The [[#SetHostName|hostname]] must be set (non-empty string) when [[#VerifyOption]] HostName is set, otherwise an error is thrown.&lt;br /&gt;
&lt;br /&gt;
This will also set a callback eventually for saving an [[Error_Report_services|error-report]] when needed, which just contains the ErrorCode loaded from a Result in state. This sysmodule doesn&#039;t save any other reports elsewhere.&lt;br /&gt;
&lt;br /&gt;
==== DoHandshakeGetServerCert ====&lt;br /&gt;
Takes a type-0x6 output buffer, returns two output u32s.&lt;br /&gt;
&lt;br /&gt;
Same as [[#DoHandshake]] except the params for the func called internally are user-specified, instead of all 0.&lt;br /&gt;
&lt;br /&gt;
The buffer contains the output server cert DER. The first u32 is the output size, the second u32 is the total certs in the buffer.&lt;br /&gt;
&lt;br /&gt;
When [[#OptionType|GetServerCertChain]] is set, the output buffer contains the full chain. This buffer can then be parsed by a seperate sdknso func:&lt;br /&gt;
* The header is at +0. +0 must match a magicnum (0x4E4D684374726543 &amp;quot;CertChMN&amp;quot;), and the u32 at +0x4 must be larger than the input cert_index.&lt;br /&gt;
* The data at +0x10 is the 0x8-byte array-entries, for each cert. Entry +0x0 is the u32 size, and +0x4 is the u32 offset. These are copied to the output &amp;quot;nn::ssl::Connection::ServerCertDetail&amp;quot; struct, for the entry with the input cert_index: +0 = u32 size, +8 = address (input_buffer+offset).&lt;br /&gt;
&lt;br /&gt;
No certs are returned when [[#VerifyOption|PeerCa]] is not set.&lt;br /&gt;
&lt;br /&gt;
When [[#IoMode]] is NonBlocking the buffer will be only filled in once - when this cmd returns successfully the buffer will generally be empty.&lt;br /&gt;
&lt;br /&gt;
==== Read ====&lt;br /&gt;
Takes a type-0x6 output buffer, returns an output u32.&lt;br /&gt;
&lt;br /&gt;
[[#SetSocketDescriptor]] must have been used prior to this successfully.&lt;br /&gt;
&lt;br /&gt;
The output u32 is the actual transferred size.&lt;br /&gt;
&lt;br /&gt;
==== Write ====&lt;br /&gt;
Takes a type-0x5 input buffer, returns an output u32.&lt;br /&gt;
&lt;br /&gt;
[[#SetSocketDescriptor]] must have been used prior to this successfully.&lt;br /&gt;
&lt;br /&gt;
The output u32 is the actual transferred size.&lt;br /&gt;
&lt;br /&gt;
==== Pending ====&lt;br /&gt;
No input, returns an output s32.&lt;br /&gt;
&lt;br /&gt;
[[#SetSocketDescriptor]] must have been used prior to this successfully.&lt;br /&gt;
&lt;br /&gt;
==== Peek ====&lt;br /&gt;
Takes a type-0x6 output buffer, returns an output u32 size.&lt;br /&gt;
&lt;br /&gt;
[[#SetSocketDescriptor]] must have been used prior to this successfully.&lt;br /&gt;
&lt;br /&gt;
==== Poll ====&lt;br /&gt;
Takes an input [[#PollEvent]], an u32, returns an output [[#PollEvent]].&lt;br /&gt;
&lt;br /&gt;
[[#SetSocketDescriptor]] must have been used prior to this successfully.&lt;br /&gt;
&lt;br /&gt;
The u32 is the timeout in milliseconds.&lt;br /&gt;
&lt;br /&gt;
==== GetVerifyCertError ====&lt;br /&gt;
No input/output.&lt;br /&gt;
&lt;br /&gt;
This loads a field from state, clears the original value in state, and returns the Result from calling a conversion func with the loaded value.&lt;br /&gt;
&lt;br /&gt;
==== GetNeededServerCertBufferSize ====&lt;br /&gt;
No input, returns an output u32.&lt;br /&gt;
&lt;br /&gt;
This just copies an u32 from state to output and returns 0.&lt;br /&gt;
&lt;br /&gt;
==== SetSessionCacheMode ====&lt;br /&gt;
Takes an input [[#SessionCacheMode]], no output.&lt;br /&gt;
&lt;br /&gt;
[[#SetSocketDescriptor]] must have been used prior to this successfully.&lt;br /&gt;
&lt;br /&gt;
==== GetSessionCacheMode ====&lt;br /&gt;
No input, returns an output [[#SessionCacheMode]].&lt;br /&gt;
&lt;br /&gt;
[[#SetSocketDescriptor]] must have been used prior to this successfully.&lt;br /&gt;
&lt;br /&gt;
==== FlushSessionCache ====&lt;br /&gt;
No input/output.&lt;br /&gt;
&lt;br /&gt;
[[#SetSocketDescriptor]] must have been used prior to this successfully.&lt;br /&gt;
&lt;br /&gt;
==== SetRenegotiationMode ====&lt;br /&gt;
Takes an input [[#RenegotiationMode]], no output.&lt;br /&gt;
&lt;br /&gt;
[[#SetSocketDescriptor]] must have been used prior to this successfully.&lt;br /&gt;
&lt;br /&gt;
==== GetRenegotiationMode ====&lt;br /&gt;
No input, returns an output [[#RenegotiationMode]].&lt;br /&gt;
&lt;br /&gt;
[[#SetSocketDescriptor]] must have been used prior to this successfully.&lt;br /&gt;
&lt;br /&gt;
==== SetOption ====&lt;br /&gt;
Takes an input u8 bool and an [[#OptionType]], no output.&lt;br /&gt;
&lt;br /&gt;
==== GetOption ====&lt;br /&gt;
Takes an input [[#OptionType]], returns an output u8 bool.&lt;br /&gt;
&lt;br /&gt;
==== GetVerifyCertErrors ====&lt;br /&gt;
Takes a type-0x6 output buffer, returns two output u32s.&lt;br /&gt;
&lt;br /&gt;
On success, sdknso will throw an error if the two output u32s don&#039;t match.&lt;br /&gt;
&lt;br /&gt;
==== GetCipherInfo ====&lt;br /&gt;
Takes an input u32 and a type-0x6 output buffer.&lt;br /&gt;
&lt;br /&gt;
sdknso uses hard-coded value 0x1 for the u32. The output buffer contains [[#CipherInfo]].&lt;br /&gt;
&lt;br /&gt;
Errors are thrown if the input u32 doesn&#039;t match 0x1, or if the buffer size doesn&#039;t match the size for [[#CipherInfo]].&lt;br /&gt;
&lt;br /&gt;
[[#SetSocketDescriptor]] must have been used prior to this successfully.&lt;br /&gt;
&lt;br /&gt;
==== SetNextAlpnProto ====&lt;br /&gt;
Takes a type-0x5 input buffer, no output.&lt;br /&gt;
&lt;br /&gt;
[[#SetSocketDescriptor]] must have been used prior to this successfully.&lt;br /&gt;
&lt;br /&gt;
The buffer size must be at least 0x2.&lt;br /&gt;
&lt;br /&gt;
[[#OptionType|EnableAlpn]] should be set at the time of using DoHandshake*, otherwise using this cmd will have no affect.&lt;br /&gt;
&lt;br /&gt;
The buffer contains an array of {u8 size, {data with the specified size}}, which must be within the buffer-size bounds.&lt;br /&gt;
&lt;br /&gt;
==== GetNextAlpnProto ====&lt;br /&gt;
Takes a type-0x6 output buffer, returns an output [[#AlpnProtoState]] and an output u32.&lt;br /&gt;
&lt;br /&gt;
[[#SetSocketDescriptor]] must have been used prior to this successfully.&lt;br /&gt;
&lt;br /&gt;
The buffer contains the output string. The u32 is the output string length.&lt;br /&gt;
&lt;br /&gt;
The output will be all-zero/empty if not available - such as when this was used before DoHandshake*.&lt;br /&gt;
&lt;br /&gt;
==== SetDtlsSocketDescriptor ====&lt;br /&gt;
Takes an input s32 sockfd and a type-0x5 input buffer, returns an output s32 sockfd.&lt;br /&gt;
&lt;br /&gt;
The input buffer contains a &amp;quot;nn::socket::SockAddr&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The input buffer must be at least 0x10-bytes and the byte at buf+0 must match the buffer size, otherwise an error is thrown. Then the same func is called as [[#SetSocketDescriptor]] internally, except this passes the input buffer for the SockAddr, while SetSocketDescriptor passes NULL for SockAddr.&lt;br /&gt;
&lt;br /&gt;
==== GetDtlsHandshakeTimeout ====&lt;br /&gt;
Takes a type-0x6 output buffer containing a &amp;quot;nn::TimeSpan&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The buffer size must be 0x8.&lt;br /&gt;
&lt;br /&gt;
==== SetPrivateOption ====&lt;br /&gt;
Takes an input bool and an [[#OptionType]], no output.&lt;br /&gt;
&lt;br /&gt;
[17.0.0+] Takes an input u32 value and an [[#OptionType]], no output.&lt;br /&gt;
&lt;br /&gt;
==== SetSrtpCiphers ====&lt;br /&gt;
Takes a type-0x5 input buffer, no output.&lt;br /&gt;
&lt;br /&gt;
The buffer must be aligned to 2-bytes. The buffer contains an array of u16s, a maximum of 4 entries is allowed. Each entry must be value 1-2, otherwise the entry is ignored.&lt;br /&gt;
&lt;br /&gt;
==== GetSrtpCipher ====&lt;br /&gt;
No input, returns an output u16.&lt;br /&gt;
&lt;br /&gt;
==== ExportKeyingMaterial ====&lt;br /&gt;
Takes a type-0x6 output buffer and two type-0x5 input buffers.&lt;br /&gt;
&lt;br /&gt;
The first inbuf contains the label string. The buffer size must match the output from strnlen(inbuf0, bufsize0), therefore the buffer size must not include the NUL-terminator.&lt;br /&gt;
&lt;br /&gt;
The second inbuf is the optional context data, if specified the buffer size must be &amp;lt;0xFFFF.&lt;br /&gt;
&lt;br /&gt;
This is for standard TLS keying material export.&lt;br /&gt;
&lt;br /&gt;
==== SetIoTimeout ====&lt;br /&gt;
Takes an input u32, no output.&lt;br /&gt;
&lt;br /&gt;
This sets a field in the ISslConnection object, then returns 0.&lt;br /&gt;
&lt;br /&gt;
==== GetIoTimeout ====&lt;br /&gt;
No input, returns an output u32.&lt;br /&gt;
&lt;br /&gt;
This gets the field set by [[#SetIoTimeout]], then returns 0.&lt;br /&gt;
&lt;br /&gt;
= ssl:s =&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::ISslServiceForSystem&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
This was added with [15.0.0+].&lt;br /&gt;
&lt;br /&gt;
This has IPC max_sessions = 0x40.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Cmd || Name&lt;br /&gt;
|-&lt;br /&gt;
| 0 || [[#CreateContext]]&lt;br /&gt;
|-&lt;br /&gt;
| 1 || [[#GetContextCount]]&lt;br /&gt;
|-&lt;br /&gt;
| 2 || [[#GetCertificates]]&lt;br /&gt;
|-&lt;br /&gt;
| 3 || [[#GetCertificateBufSize]]&lt;br /&gt;
|-&lt;br /&gt;
| 4 || [[#DebugIoctl]]&lt;br /&gt;
|-&lt;br /&gt;
| 5 || [[#SetInterfaceVersion]]&lt;br /&gt;
|-&lt;br /&gt;
| 6 || [[#FlushSessionCache]]&lt;br /&gt;
|-&lt;br /&gt;
| 7 || [[#SetDebugOption]]&lt;br /&gt;
|-&lt;br /&gt;
| 8 || [[#GetDebugOption]]&lt;br /&gt;
|-&lt;br /&gt;
| 9 || [[#ClearTls12FallbackFlag]]&lt;br /&gt;
|-&lt;br /&gt;
| 100 || [[#CreateContextForSystem]]&lt;br /&gt;
|-&lt;br /&gt;
| 101 || [[#SetThreadCoreMask]]&lt;br /&gt;
|-&lt;br /&gt;
| 102 || [[#GetThreadCoreMask]]&lt;br /&gt;
|-&lt;br /&gt;
| 103 || [18.0.0+] [[#VerifySignature]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== CreateContextForSystem ==&lt;br /&gt;
Takes a PID, an input u32 [[#SslVersion]] and an input u64 pid_placeholder. Returns an output [[#ISslContextForSystem]].&lt;br /&gt;
&lt;br /&gt;
== SetThreadCoreMask ==&lt;br /&gt;
Takes an input u64 mask, no output.&lt;br /&gt;
&lt;br /&gt;
An error is thrown if the mask is 0, or if any bits are set which are not present in CoreMask from [[SVC|svcGetInfo]].&lt;br /&gt;
&lt;br /&gt;
This updates a global field and uses [[SVC|svcSetThreadCoreMask]].&lt;br /&gt;
&lt;br /&gt;
== GetThreadCoreMask ==&lt;br /&gt;
No input, returns an output u64.&lt;br /&gt;
&lt;br /&gt;
This gets the global field which is also used by [[#SetThreadCoreMask]].&lt;br /&gt;
&lt;br /&gt;
== VerifySignature ==&lt;br /&gt;
Takes three type-0x5 input buffers. No output.&lt;br /&gt;
&lt;br /&gt;
[19.0.0+] Now takes an additional 4-bytes of input.&lt;br /&gt;
&lt;br /&gt;
== ISslContextForSystem ==&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::ISslContextForSystem&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
This was added with [15.0.0+].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Cmd || Name&lt;br /&gt;
|-&lt;br /&gt;
| 0 || [[#SetOption]]&lt;br /&gt;
|-&lt;br /&gt;
| 1 || [[#GetOption]]&lt;br /&gt;
|-&lt;br /&gt;
| 2 || [[#CreateConnection]]&lt;br /&gt;
|-&lt;br /&gt;
| 3 || [[#GetConnectionCount]]&lt;br /&gt;
|-&lt;br /&gt;
| 4 || [[#ImportServerPki]]&lt;br /&gt;
|-&lt;br /&gt;
| 5 || [[#ImportClientPki]]&lt;br /&gt;
|-&lt;br /&gt;
| 6 || [[#RemoveServerPki]]&lt;br /&gt;
|-&lt;br /&gt;
| 7 || [[#RemoveClientPki]]&lt;br /&gt;
|-&lt;br /&gt;
| 8 || [[#RegisterInternalPki]]&lt;br /&gt;
|-&lt;br /&gt;
| 9 || [[#AddPolicyOid]]&lt;br /&gt;
|-&lt;br /&gt;
| 10 || [[#ImportCrl]]&lt;br /&gt;
|-&lt;br /&gt;
| 11 || [[#RemoveCrl]]&lt;br /&gt;
|-&lt;br /&gt;
| 12 || [16.0.0+] [[#ImportClientCertKeyPki]]&lt;br /&gt;
|-&lt;br /&gt;
| 13 || [16.0.0+] [[#GeneratePrivateKeyAndCert]]&lt;br /&gt;
|-&lt;br /&gt;
| 100 || [[#CreateConnectionEx]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== CreateConnectionEx ===&lt;br /&gt;
No input. Returns an [[#ISslConnection]].&lt;br /&gt;
&lt;br /&gt;
This is similar to [[#CreateConnection]], but allows a maximum of 10 connections instead of 8.&lt;br /&gt;
&lt;br /&gt;
= SslVersion =&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::SslVersion&amp;quot; or &amp;quot;nn::ssl::Context::SslVersion&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
This is a bitmask which controls the min/max TLS versions to use, depending on which lowest/highest bits are set (if Auto isn&#039;t set).&lt;br /&gt;
&lt;br /&gt;
Auto uses min=TlsV10 max=TlsV12.&lt;br /&gt;
&lt;br /&gt;
[11.0.0+] ApiVersion is 1.&lt;br /&gt;
&lt;br /&gt;
[12.0.0+] ApiVersion is now 2. Auto now uses min=TlsV10 max=((ApiVersion &amp;lt; 2) ? TlsV12 : TlsV13).&lt;br /&gt;
&lt;br /&gt;
[12.0.3+] TLS 1.3 is no longer used. The TlsV13 bit is now handled the same as TlsV12 (uses TLS 1.2), and Auto only uses TLS 1.2 for the maximum.&lt;br /&gt;
&lt;br /&gt;
[14.0.0+] ApiVersion is now 3 and TLS 1.3 is supported again. Auto now uses min=TlsV10 max=((ApiVersion &amp;lt; 3) ? TlsV12 : TlsV13). If too many connection errors arise, TLS now automatically falls back to version 1.2 by setting an internal flag which can be manually cleared with [[#ClearTls12FallbackFlag]].&lt;br /&gt;
&lt;br /&gt;
[17.0.0+] ApiVersion is now 4.&lt;br /&gt;
&lt;br /&gt;
[19.0.0+] ApiVersion is now 5. Different ciphers are enabled if the ApiVersion &amp;gt;= 5.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Bits&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Auto&lt;br /&gt;
|-&lt;br /&gt;
| 3 || TlsV10&lt;br /&gt;
|-&lt;br /&gt;
| 4 || TlsV11&lt;br /&gt;
|-&lt;br /&gt;
| 5 || TlsV12&lt;br /&gt;
|-&lt;br /&gt;
| 6 || [11.0.0+] TlsV13&lt;br /&gt;
|-&lt;br /&gt;
| 24-31 || [11.0.0+] ApiVersion&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= DebugOptionType =&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::DebugOptionType&amp;quot; or &amp;quot;nn::ssl::DebugOption&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || AllowDisableVerifyOption&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= FlushSessionCacheOptionType =&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::FlushSessionCacheOptionType&amp;quot; or &amp;quot;nn::ssl::FlushSessionCacheOptionType&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || SingleHost&lt;br /&gt;
|-&lt;br /&gt;
| 1 || AllHosts&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= BuiltInCertificateInfo =&lt;br /&gt;
This is &amp;quot;nn::ssl::BuiltInManager::BuiltInCertificateInfo&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset&lt;br /&gt;
! Size&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x4&lt;br /&gt;
| [[#CaCertificateId]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x4&lt;br /&gt;
| [[#TrustedCertStatus]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x8&lt;br /&gt;
| CertificateSize&lt;br /&gt;
|-&lt;br /&gt;
| 0x10&lt;br /&gt;
| 0x8&lt;br /&gt;
| CertificateDataOffset&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This is the struct returned by [[#GetCertificates]]. It is internally converted from &amp;quot;nn::ssl::detail::BuiltinDataInfo&amp;quot; by copying &amp;quot;nn::ssl::detail::BuiltinDataInfo::BuiltinDataStatus&amp;quot; into [[#TrustedCertStatus]] and official software then further converts this to &amp;quot;nn::ssl::BuiltInManager::BuiltInCertificateInfo&amp;quot; by transforming &amp;quot;CertificateDataOffset&amp;quot; into an actual pointer.&lt;br /&gt;
&lt;br /&gt;
= TrustedCertStatus =&lt;br /&gt;
This is &amp;quot;nn::ssl::TrustedCertStatus&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| -1&lt;br /&gt;
| Invalid&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Removed&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| EnabledTrusted&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| EnabledNotTrusted&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Revoked&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= CaCertificateId =&lt;br /&gt;
This is &amp;quot;nn::ssl::CaCertificateId&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| -1 || [3.0.0+] All&lt;br /&gt;
|-&lt;br /&gt;
| 1 || NintendoCAG3&lt;br /&gt;
|-&lt;br /&gt;
| 2 || NintendoClass2CAG3&lt;br /&gt;
|-&lt;br /&gt;
| 3 || [16.0.0+] &amp;quot;Nintendo Root CA G4&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1000 || AmazonRootCA1&lt;br /&gt;
|-&lt;br /&gt;
| 1001 || StarfieldServicesRootCertificateAuthorityG2&lt;br /&gt;
|-&lt;br /&gt;
| 1002 || AddTrustExternalCARoot&lt;br /&gt;
|-&lt;br /&gt;
| 1003 || COMODOCertificationAuthority&lt;br /&gt;
|-&lt;br /&gt;
| 1004 || UTNDATACorpSGC&lt;br /&gt;
|-&lt;br /&gt;
| 1005 || UTNUSERFirstHardware&lt;br /&gt;
|-&lt;br /&gt;
| 1006 || BaltimoreCyberTrustRoot&lt;br /&gt;
|-&lt;br /&gt;
| 1007 || CybertrustGlobalRoot&lt;br /&gt;
|-&lt;br /&gt;
| 1008 || VerizonGlobalRootCA&lt;br /&gt;
|-&lt;br /&gt;
| 1009 || DigiCertAssuredIDRootCA&lt;br /&gt;
|-&lt;br /&gt;
| 1010 || DigiCertAssuredIDRootG2&lt;br /&gt;
|-&lt;br /&gt;
| 1011 || DigiCertGlobalRootCA&lt;br /&gt;
|-&lt;br /&gt;
| 1012 || DigiCertGlobalRootG2&lt;br /&gt;
|-&lt;br /&gt;
| 1013 || DigiCertHighAssuranceEVRootCA&lt;br /&gt;
|-&lt;br /&gt;
| 1014 || EntrustnetCertificationAuthority2048&lt;br /&gt;
|-&lt;br /&gt;
| 1015 || EntrustRootCertificationAuthority&lt;br /&gt;
|-&lt;br /&gt;
| 1016 || EntrustRootCertificationAuthorityG2&lt;br /&gt;
|-&lt;br /&gt;
| 1017 || GeoTrustGlobalCA2 ([8.0.0+] [[#TrustedCertStatus]] is EnabledNotTrusted)&lt;br /&gt;
|-&lt;br /&gt;
| 1018 || GeoTrustGlobalCA ([8.0.0+] [[#TrustedCertStatus]] is EnabledNotTrusted)&lt;br /&gt;
|-&lt;br /&gt;
| 1019 || GeoTrustPrimaryCertificationAuthorityG3 ([8.0.0+] [[#TrustedCertStatus]] is EnabledNotTrusted)&lt;br /&gt;
|-&lt;br /&gt;
| 1020 || GeoTrustPrimaryCertificationAuthority ([8.0.0+] [[#TrustedCertStatus]] is EnabledNotTrusted)&lt;br /&gt;
|-&lt;br /&gt;
| 1021 || GlobalSignRootCA&lt;br /&gt;
|-&lt;br /&gt;
| 1022 || GlobalSignRootCAR2&lt;br /&gt;
|-&lt;br /&gt;
| 1023 || GlobalSignRootCAR3&lt;br /&gt;
|-&lt;br /&gt;
| 1024 || GoDaddyClass2CertificationAuthority&lt;br /&gt;
|-&lt;br /&gt;
| 1025 || GoDaddyRootCertificateAuthorityG2&lt;br /&gt;
|-&lt;br /&gt;
| 1026 || StarfieldClass2CertificationAuthority&lt;br /&gt;
|-&lt;br /&gt;
| 1027 || StarfieldRootCertificateAuthorityG2&lt;br /&gt;
|-&lt;br /&gt;
| 1028 || thawtePrimaryRootCAG3 ([8.0.0+] [[#TrustedCertStatus]] is EnabledNotTrusted)&lt;br /&gt;
|-&lt;br /&gt;
| 1029 || thawtePrimaryRootCA ([8.0.0+] [[#TrustedCertStatus]] is EnabledNotTrusted)&lt;br /&gt;
|-&lt;br /&gt;
| 1030 || VeriSignClass3PublicPrimaryCertificationAuthorityG3 ([8.0.0+] [[#TrustedCertStatus]] is EnabledNotTrusted)&lt;br /&gt;
|-&lt;br /&gt;
| 1031 || VeriSignClass3PublicPrimaryCertificationAuthorityG5 ([8.0.0+] [[#TrustedCertStatus]] is EnabledNotTrusted)&lt;br /&gt;
|-&lt;br /&gt;
| 1032 || VeriSignUniversalRootCertificationAuthority ([8.0.0+] [[#TrustedCertStatus]] is EnabledNotTrusted)&lt;br /&gt;
|-&lt;br /&gt;
| 1033 || [6.0.0+] DSTRootCAX3&lt;br /&gt;
|-&lt;br /&gt;
| 1034 || [10.0.3+] &amp;quot;USERTrust RSA Certification Authority&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1035 || [10.1.0+] &amp;quot;ISRG Root X10&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1036 || [10.1.0+] &amp;quot;USERTrust ECC Certification Authority&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1037 || [10.1.0+] &amp;quot;COMODO RSA Certification Authority&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1038 || [10.1.0+] &amp;quot;COMODO ECC Certification Authority&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1039 || [11.0.0+] &amp;quot;Amazon Root CA 2&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1040 || [11.0.0+] &amp;quot;Amazon Root CA 3&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1041 || [11.0.0+] &amp;quot;Amazon Root CA 4&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1042 || [11.0.0+] &amp;quot;DigiCert Assured ID Root G3&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1043 || [11.0.0+] &amp;quot;DigiCert Global Root G3&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1044 || [11.0.0+] &amp;quot;DigiCert Trusted Root G4&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1045 || [11.0.0+] &amp;quot;Entrust Root Certification Authority - EC1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1046 || [11.0.0+] &amp;quot;Entrust Root Certification Authority - G4&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1047 || [11.0.0+] &amp;quot;GlobalSign ECC Root CA - R4&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1048 || [11.0.0+] &amp;quot;GlobalSign ECC Root CA - R5&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1049 || [11.0.0+] &amp;quot;GlobalSign ECC Root CA - R6&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1050 || [11.0.0+] &amp;quot;GTS Root R1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1051 || [11.0.0+] &amp;quot;GTS Root R2&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1052 || [11.0.0+] &amp;quot;GTS Root R3&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1053 || [11.0.0+] &amp;quot;GTS Root R4&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1054 || [12.0.0+] &amp;quot;Security Communication RootCA&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1055 || [15.0.0+] &amp;quot;GlobalSign Root E4&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1056 || [15.0.0+] &amp;quot;GlobalSign Root R4&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1057 || [15.0.0+] &amp;quot;T-TeleSec GlobalRoot Class 2&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1058 || [16.0.0+] &amp;quot;DigiCert TLS ECC P384 Root G5&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1059 || [16.0.0+] &amp;quot;DigiCert TLS RSA4096 Root G5&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32801 || [20.0.0+] &amp;quot;ssl-rr-01.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 65536 (0x10000) || [16.0.0+] &amp;quot;Nintendo Temp Root CA G4&amp;quot; (Only in &amp;quot;ssl_TrustedCerts.Ounce.bdf&amp;quot;) ([19.0.0+] Removed)&lt;br /&gt;
|-&lt;br /&gt;
| 32802 || [20.0.0+] &amp;quot;ssl-rr-02.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32803 || [20.0.0+] &amp;quot;ssl-rr-03.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32804 || [20.0.0+] &amp;quot;ssl-rr-04.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32805 || [20.0.0+] &amp;quot;ssl-rr-05.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32806 || [20.0.0+] &amp;quot;ssl-rr-06.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32807 || [20.0.0+] &amp;quot;ssl-rr-07.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32808 || [20.0.0+] &amp;quot;ssl-rr-08.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32809 || [20.0.0+] &amp;quot;ssl-rr-09.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32810 || [20.0.0+] &amp;quot;ssl-rr-10.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32811 || [20.0.0+] &amp;quot;ssl-rr-11.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32812 || [20.0.0+] &amp;quot;ssl-rr-12.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32813 || [20.0.0+] &amp;quot;ssl-rr-13.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32814 || [20.0.0+] &amp;quot;ssl-rr-14.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32815 || [20.0.0+] &amp;quot;ssl-rr-15.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32816 || [20.0.0+] &amp;quot;ssl-rr-16.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32817 || [20.0.0+] &amp;quot;ssl-rr-17.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32818 || [20.0.0+] &amp;quot;ssl-rr-18.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32819 || [20.0.0+] &amp;quot;ssl-rr-19.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32820 || [20.0.0+] &amp;quot;ssl-rr-20.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32821 || [20.0.0+] &amp;quot;ssl-rr-21.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32822 || [20.0.0+] &amp;quot;ssl-rr-22.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32823 || [20.0.0+] &amp;quot;ssl-rr-23.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32824 || [20.0.0+] &amp;quot;ssl-rr-24.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32825 || [20.0.0+] &amp;quot;ssl-rr-25.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32826 || [20.0.0+] &amp;quot;ssl-rr-26.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32827 || [20.0.0+] &amp;quot;ssl-rr-27.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32828 || [20.0.0+] &amp;quot;ssl-rr-28.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32829 || [20.0.0+] &amp;quot;ssl-rr-29.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32830 || [20.0.0+] &amp;quot;ssl-rr-30.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32831 || [20.0.0+] &amp;quot;ssl-rr-31.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32832 || [20.0.0+] &amp;quot;ssl-rr-32.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32833 || [20.0.0+] &amp;quot;ssl-rr-33.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32834 || [20.0.0+] &amp;quot;ssl-rr-34.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32835 || [20.0.0+] &amp;quot;ssl-rr-35.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32836 || [20.0.0+] &amp;quot;ssl-rr-36.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32837 || [20.0.0+] &amp;quot;ssl-rr-37.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32838 || [20.0.0+] &amp;quot;ssl-rr-38.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32839 || [20.0.0+] &amp;quot;ssl-rr-39.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32840 || [20.0.0+] &amp;quot;ssl-rr-40.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32841 || [20.0.0+] &amp;quot;ssl-rr-41.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32842 || [20.0.0+] &amp;quot;ssl-rr-42.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32843 || [20.0.0+] &amp;quot;ssl-rr-43.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32844 || [20.0.0+] &amp;quot;ssl-rr-44.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32845 || [20.0.0+] &amp;quot;ssl-rr-45.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32846 || [20.0.0+] &amp;quot;ssl-rr-46.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32847 || [20.0.0+] &amp;quot;ssl-rr-47.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32848 || [20.0.0+] &amp;quot;ssl-rr-48.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32849 || [20.0.0+] &amp;quot;ssl-rr-49.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32850 || [20.0.0+] &amp;quot;ssl-rr-50.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32851 || [20.0.0+] &amp;quot;ssl-rr-51.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32852 || [20.0.0+] &amp;quot;ssl-rr-52.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32853 || [20.0.0+] &amp;quot;ssl-rr-53.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32854 || [20.0.0+] &amp;quot;ssl-rr-54.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32855 || [20.0.0+] &amp;quot;ssl-rr-55.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32856 || [20.0.0+] &amp;quot;ssl-rr-56.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32857 || [20.0.0+] &amp;quot;ssl-rr-57.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32858 || [20.0.0+] &amp;quot;ssl-rr-58.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32859 || [20.0.0+] &amp;quot;ssl-rr-59.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32860 || [20.0.0+] &amp;quot;ssl-rr-60.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32861 || [20.0.0+] &amp;quot;ssl-rr-61.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32862 || [20.0.0+] &amp;quot;ssl-rr-62.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32863 || [20.0.0+] &amp;quot;ssl-rr-63.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 32864 || [20.0.0+] &amp;quot;ssl-rr-64.netdev.ntd.nintendo.com&amp;quot;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= InternalPki =&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::InternalPki&amp;quot; or &amp;quot;nn::ssl::Context::InternalPki&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || None&lt;br /&gt;
|-&lt;br /&gt;
| 1 || DeviceClientCertDefault&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
An error is thrown by [[#RegisterInternalPki]] when the input value does not match &amp;quot;DeviceClientCertDefault&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&amp;quot;DeviceClientCertDefault&amp;quot; enables using the DeviceCert.&lt;br /&gt;
&lt;br /&gt;
= ContextOption =&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::ContextOption&amp;quot; or &amp;quot;nn::ssl::Context::ContextOption&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The default value for CrlImportDateCheckEnable at the time of [[#ISslContext]] object creation is value 1.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || None&lt;br /&gt;
|-&lt;br /&gt;
| 1 || CrlImportDateCheckEnable&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= CertificateFormat =&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::CertificateFormat&amp;quot; or &amp;quot;nn::ssl::CertificateFormat&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Pem&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Der&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= VerifyOption =&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::VerifyOption&amp;quot;. This is a bitmask. At the time of [[#ISslConnection]] object creation, the default value is 0x3.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Bit&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || PeerCa&lt;br /&gt;
|-&lt;br /&gt;
| 1 || HostName&lt;br /&gt;
|-&lt;br /&gt;
| 2 || DateCheck&lt;br /&gt;
|-&lt;br /&gt;
| 3 || EvCertPartial&lt;br /&gt;
|-&lt;br /&gt;
| 4 || [6.0.0+] EvPolicyOid&lt;br /&gt;
|-&lt;br /&gt;
| 5 || [6.0.0+] EvCertFingerprint&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Originally ssl-sysmodule ([[#SetVerifyOption]]) just wrote the input field to state. With [5.0.0+] there&#039;s now validation for the input, with the value written to state masked with {allowed bitmask}. When [[#SetInterfaceVersion|InterfaceVersion]] is &amp;gt;=0x2, the low 2-bits of VerifyOption must be set, unless {state flag for [[#OptionType|SkipDefaultVerify]]} is set or [9.0.0+] {bool [[#SetDebugOption|DebugOption]] state flag} is set, otherwise an error is thrown. [6.0.0+]: Following that, if VerifyOption bit4 is set, then VerifyOption &amp;amp; 0x15 must match 0x15 otherwise an error is thrown.&lt;br /&gt;
&lt;br /&gt;
= IoMode =&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::IoMode&amp;quot; or &amp;quot;nn::ssl::Connection::IoMode&amp;quot;. At the time of [[#ISslConnection]] object creation, the default value is 1.&lt;br /&gt;
&lt;br /&gt;
The socket non-blocking flag is always set regardless of this field, this is only used for calculating the timeout (converted from milliseconds) passed to the NSS funcs used by various cmds. NonBlocking = 0, Blocking = 300000 (5 minutes).&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Blocking&lt;br /&gt;
|-&lt;br /&gt;
| 2 || NonBlocking&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= PollEvent =&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::PollEvent&amp;quot; or &amp;quot;nn::ssl::Connection::PollEvent&amp;quot;. This is a bitmask.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Bit&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Read&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Write&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Except&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= SessionCacheMode =&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::SessionCacheMode&amp;quot; or &amp;quot;nn::ssl::Connection::SessionCacheMode&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || None&lt;br /&gt;
|-&lt;br /&gt;
| 1 || SessionId&lt;br /&gt;
|-&lt;br /&gt;
| 2 || SessionTicket&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= RenegotiationMode =&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::RenegotiationMode&amp;quot; or &amp;quot;nn::ssl::Connection::RenegotiationMode&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || None&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Secure&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= OptionType =&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::OptionType&amp;quot; or &amp;quot;nn::ssl::Connection::OptionType&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || DoNotCloseSocket&lt;br /&gt;
|-&lt;br /&gt;
| 1 || [3.0.0+] GetServerCertChain&lt;br /&gt;
|-&lt;br /&gt;
| 2 || [5.0.0+] SkipDefaultVerify&lt;br /&gt;
|-&lt;br /&gt;
| 3 || [9.0.0+] EnableAlpn&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Or in the case of [[#SetPrivateOption]] which is also &amp;quot;nn::ssl::ConnectionPrivate::PrivateOptionType&amp;quot;:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || [[#SetSessionCacheMode]] will throw an error if the input [[#SessionCacheMode]] is non-zero and this option flag is set.&lt;br /&gt;
|-&lt;br /&gt;
| 2 || [17.0.0+] This exclusively enables the cipher suite specified in the input u32 passed to [[#SetPrivateOption]] (all other ciphers disabled).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
This corresponds to bool flags. At the time of [[#ISslConnection]] object creation, all of these fields are cleared (excluding PrivateOptionType val1 above?).&lt;br /&gt;
&lt;br /&gt;
&amp;quot;SkipDefaultVerify&amp;quot; is checked by [[#VerifyOption|SetVerifyOption]] and &amp;quot;EnableAlpn&amp;quot; is only available with [[#SetOption_2|SetOption]].&lt;br /&gt;
&lt;br /&gt;
[[#SetOption_2|SetOption]] with &amp;quot;DoNotCloseSocket&amp;quot; is only available when [[#SetSocketDescriptor]] was not used previously. &amp;quot;EnableAlpn&amp;quot; can optionally use the state setup by [[#SetSocketDescriptor]], but it will return 0 regardless.&lt;br /&gt;
&lt;br /&gt;
See [[#SetNextAlpnProto]] for &amp;quot;EnableAlpn&amp;quot;, and [[#DoHandshakeGetServerCert]] for &amp;quot;GetServerCertChain&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= AlpnProtoState =&lt;br /&gt;
This is &amp;quot;nn::ssl::sf::AlpnProtoState&amp;quot; or &amp;quot;nn::ssl::Connection::AlpnProtoState&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Value&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || NoSupport&lt;br /&gt;
|-&lt;br /&gt;
| 1 || Negotiated&lt;br /&gt;
|-&lt;br /&gt;
| 2 || NoOverlap&lt;br /&gt;
|-&lt;br /&gt;
| 3 || Selected&lt;br /&gt;
|-&lt;br /&gt;
| 4 || EarlyValue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= CipherInfo =&lt;br /&gt;
This is &amp;quot;nn::ssl::Connection::CipherInfo&amp;quot;. This is a 0x48-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset&lt;br /&gt;
! Size&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x40 || Cipher string&lt;br /&gt;
|-&lt;br /&gt;
| 0x40 || 0x8 || Protocol version string&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= KeyAndCertParams =&lt;br /&gt;
This is &amp;quot;nn::ssl::ContextPrivate::KeyAndCertParams&amp;quot;. This is a 0x58-byte struct.&lt;br /&gt;
&lt;br /&gt;
This was added with [16.0.0+].&lt;br /&gt;
&lt;br /&gt;
The constructor for this in official sw just clears this struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset&lt;br /&gt;
! Size&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || Must be value 1.&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x4 || s32 Key size in bits.&lt;br /&gt;
|-&lt;br /&gt;
| 0x8 || 0x8 || Public exponent, must be non-zero. Only the low 4-bytes are used.&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || 0x40 || CN (Common Name) NUL-terminated string.&lt;br /&gt;
|-&lt;br /&gt;
| 0x50 || 0x4 || An error is thrown if this is value 0 or &amp;gt;0x3F. The official wrapper code for [[#GeneratePrivateKeyAndCert]] verifies that this matches the output from &amp;lt;code&amp;gt;strnlen(struct+0x10, 0x40)&amp;lt;/code&amp;gt;, however the sysmodule version just throws an error if the strnlen output matches 0x40 (as in no NUL-terminator found).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= CertStore =&lt;br /&gt;
This is the CertStore title, which contains the following files in RomFS:&lt;br /&gt;
* &amp;quot;/ssl_TrustedCerts.bdf&amp;quot; ([1.0.0-2.3.0] &amp;quot;ssl_TrustedCerts.tcf&amp;quot;) (file was renamed with [3.0.0], content is identical)&lt;br /&gt;
* [3.0.0+] &amp;quot;/ssl_Crl.bdf&amp;quot;&lt;br /&gt;
* [6.0.0+] &amp;quot;/ssl_CaFingerprints.bdf&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The content of this SystemData was updated with the following system-versions: [[3.0.0]], [[6.0.0]], [[8.0.0]], [[10.1.0]], [[11.0.0]], [[12.0.0]]. See [[#CaCertificateId]] for the ssl_TrustedCerts changes.&lt;br /&gt;
&lt;br /&gt;
[10.1.0+] added 3 more fingerprints to &amp;quot;/ssl_CaFingerprints.bdf&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[11.0.0+] updated &amp;quot;/ssl_CaFingerprints.bdf&amp;quot; and &amp;quot;/ssl_TrustedCerts.bdf&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[12.0.0+] updated &amp;quot;/ssl_TrustedCerts.bdf&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[16.0.0+] updated &amp;quot;/ssl_TrustedCerts.bdf&amp;quot; and added &amp;quot;/ssl_TrustedCerts.Ounce.bdf&amp;quot;. The latter is identical to the former except that it contains an additional cert. The latter also isn&#039;t used in ssl-sysmodule (in the retail build at least).&lt;br /&gt;
&lt;br /&gt;
[19.0.0+] &amp;quot;/ssl_TrustedCerts.Ounce.bdf&amp;quot; updated&lt;br /&gt;
&lt;br /&gt;
[20.0.0+] &amp;quot;/ssl_TrustedCerts.bdf&amp;quot; updated, &amp;quot;/ssl_TrustedCerts.Ounce.bdf&amp;quot; updated (&amp;quot;/ssl_TrustedCerts.Ounce.bdf&amp;quot; is identical to &amp;quot;/ssl_TrustedCerts.bdf&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
[[#ISslContext]] automatically uses this CertStore, regardless of the used cmds.&lt;br /&gt;
&lt;br /&gt;
These have the following structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset&lt;br /&gt;
! Size&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || Magic &amp;quot;sslT&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x4 || Total entries&lt;br /&gt;
|-&lt;br /&gt;
| 0x8 || 0x10*{total entries} || Array entries&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Array entry structure:&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset&lt;br /&gt;
! Size&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0&lt;br /&gt;
| 0x4&lt;br /&gt;
| Id&lt;br /&gt;
|-&lt;br /&gt;
| 0x4&lt;br /&gt;
| 0x4&lt;br /&gt;
| [[#TrustedCertStatus]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x8&lt;br /&gt;
| 0x4&lt;br /&gt;
| Data size&lt;br /&gt;
|-&lt;br /&gt;
| 0xC&lt;br /&gt;
| 0x4&lt;br /&gt;
| Data offset&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Data offset is relative to absolute offset 0x8.&lt;br /&gt;
&lt;br /&gt;
The Id is the same one used by service commands to access these entries. For ssl_TrustedCerts, Id is [[#CaCertificateId]].&lt;br /&gt;
&lt;br /&gt;
= Client cert+privk =&lt;br /&gt;
SSL-sysmodule uses [[Settings_services|set:cal]] [[Settings_services#GetSslKey|GetSslKey]] and [[Settings_services#GetSslCert|GetSslCert]]. The rest of this section documents handling for the former, which can be decrypted with [[SPL_services|SPL]].&lt;br /&gt;
&lt;br /&gt;
key* below refers to the 3 0x10-byte input blocks passed to this code.&lt;br /&gt;
&lt;br /&gt;
When actual_size is:&lt;br /&gt;
* 0x100+0x10: If the u32 actual_size is less than (u32)-0x11, and the last 0x10-bytes of the actual-data are all-zero, the data is copied to the output as raw plaintext. If a non-zero byte is found, it will continue with [[SPL_services|SPL]] usage, skipping over the SPL block for the devunit flag. In this case, key=key0 and the flag passed to SPL later is set to 0.&lt;br /&gt;
* 0x100+0x30: Size must match this if it&#039;s not the above, otherwise error 0xC81A is returned. The flag passed to SPL later is set to 1 in this case. Runs the devunit-flag-block: uses [[SPL_services#SPL#GetDevunitFlag]]. key = key1 when out_flag!=0, key2 otherwise.&lt;br /&gt;
&lt;br /&gt;
[[Category:Services]]&lt;/div&gt;</summary>
		<author><name>Yannik</name></author>
	</entry>
	<entry>
		<id>https://switchbrew.org/w/index.php?title=HIPC&amp;diff=13417</id>
		<title>HIPC</title>
		<link rel="alternate" type="text/html" href="https://switchbrew.org/w/index.php?title=HIPC&amp;diff=13417"/>
		<updated>2025-06-12T14:00:24Z</updated>

		<summary type="html">&lt;p&gt;Yannik: Document ReceiveListOffset field of HIPC header&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;HIPC (Horizon Inter-Process Communication) is a custom IPC implementation tailored for the Horizon OS.&lt;br /&gt;
&lt;br /&gt;
There are two protocols over [[#HIPC|HIPC]]:&lt;br /&gt;
&lt;br /&gt;
* [[#CMIF|CMIF]]: Command Interface (?). Supports session management with domains and dynamic multiplexing of services.&lt;br /&gt;
* [[#TIPC|TIPC]]: Tiny IPC. Simple and lightweight protocol.&lt;br /&gt;
&lt;br /&gt;
[[#CMIF|CMIF]] is universally used for virtually almost all Switch services. [[#TIPC|TIPC]] is only used by the [[Services_API|Service Manager]] and [[PGL_services|pgl]].&lt;br /&gt;
&lt;br /&gt;
= HIPC =&lt;br /&gt;
This is a buffer located in the [[Thread Local Region]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x8 || [[#HeaderData|HeaderData]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x8 || 0x4 || [[#SpecialTag|SpecialHeaderData]] (if [[#Header1Tag|SpecialCount]] is set)&lt;br /&gt;
|-&lt;br /&gt;
| 0xC || 0x8 || ProcessId (if [[#SpecialTag|Pid]] is set)&lt;br /&gt;
|-&lt;br /&gt;
| 0x14 || Variable || Array of CopyHandle (if [[#SpecialTag|CopyHandleCount]] &amp;gt; 0)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Array of MoveHandle (if [[#SpecialTag|MoveHandleCount]] &amp;gt; 0)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Array of [[#PointerData|PointerData]] (if [[#Header0Tag|PointerCount]] &amp;gt; 0)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Array of [[#MapData|SendData]] (if [[#Header0Tag|SendCount]] &amp;gt; 0)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Array of [[#MapData|ReceiveData]] (if [[#Header0Tag|ReceiveCount]] &amp;gt; 0)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Array of [[#MapData|ExchangeData]] (if [[#Header0Tag|ExchangeCount]] &amp;gt; 0)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || [[#RawData|RawData]] (if [[#Header1Tag|RawCount]] &amp;gt; 0)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Array of [[#ReceiveListData|ReceiveListData]] (if [[#Header1Tag|ReceiveListCount]] &amp;gt; 0)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[#HeaderData|HeaderData]] and [[#SpecialTag|SpecialHeaderData]] (if available) are copied as-is from one process to another.&lt;br /&gt;
&lt;br /&gt;
Sysmodules load the last u64 of rawdata when handling the ProcessId. This is not written by kernel. For sysmodule handling:&lt;br /&gt;
* In some cases: these commands require a placeholder u64 value passed in the input parameters, as mentioned above. In these cases the OverwriteClientProcessId method is called to replace the value before it is used.&lt;br /&gt;
* In other cases: The rawdata_u64 is compared with the ProcessId. On mismatch and when rawdata_u64!=0, error 0x60A is returned. The ProcessId value passed to the cmdhandler vtable funcptr is the rawdata_u64.&lt;br /&gt;
&lt;br /&gt;
Handle 0 is allowed, and just means no handle was sent.&lt;br /&gt;
&lt;br /&gt;
A reply must not use Send/Receive/Exchange data buffers, svcReplyAndReceive will return 0xE801. [[SVC|MemoryAttribute]] IsBorrowed and IsUncached are never allowed for the source address. &amp;quot;Send&amp;quot; means buffer is sent from source process into service process, &amp;quot;Receive&amp;quot; means that data is copied from service process into user process and &amp;quot;Exchange&amp;quot; means both &amp;quot;Send&amp;quot; and &amp;quot;Receive&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[#HeaderData|HeaderData]]&#039;s ReceiveListCount field controls the behavior of [[#ReceiveListData|ReceiveListData]].&lt;br /&gt;
&lt;br /&gt;
If ReceiveListCount is 0, [[#ReceiveListData|ReceiveListData]] is disabled. If ReceiveListCount is 1, there is an &amp;quot;inlined&amp;quot; [[#ReceiveListData|ReceiveListData]] buffer after the raw data (received data is copied to ROUND_UP(cmdbuf+raw_size+index, 16)). If ReceiveListCount is 2, there is a single [[#ReceiveListData|ReceiveListData]] buffer. Otherwise it has (ReceiveListCount-2) [[#ReceiveListData|ReceiveListData]] buffers. In this case, ReceiveIndex picks which [[#ReceiveListData|ReceiveListData]] buffer to copy received data to [instead of picking the offset into the buffer]. Data sent with this method must have MemoryState 0x4000000 mask set.&lt;br /&gt;
&lt;br /&gt;
After reply, [[#PointerData|PointerData]] buffers are written to the sender containing the AddressValue, Size and ReceiveIndex that were copied to.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== HeaderData ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::HeaderData&amp;quot;. This is a 0x8-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || [[#Header0Tag|Header0]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x4 || [[#Header1Tag|Header1]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== MapData ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::MapData&amp;quot;. This is a 0xC-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || [[#Map0Tag|Data0]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x4 || [[#Map1Tag|Data1]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x8 || 0x4 || [[#Map2Tag|Data2]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== PointerData ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::PointerData&amp;quot;. This is a 0x8-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || [[#Pointer0Tag|Data0]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x4 || [[#Pointer1Tag|Data1]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ReceiveListData ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::ReceiveListData&amp;quot;. This is a 0x8-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || [[#ReceiveList0Tag|Data0]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x4 || [[#ReceiveList1Tag|Data1]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Header0Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::Header0Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-15&lt;br /&gt;
| Tag&lt;br /&gt;
|-&lt;br /&gt;
| 16-19&lt;br /&gt;
| PointerCount&lt;br /&gt;
|-&lt;br /&gt;
| 20-23&lt;br /&gt;
| SendCount&lt;br /&gt;
|-&lt;br /&gt;
| 24-27&lt;br /&gt;
| ReceiveCount&lt;br /&gt;
|-&lt;br /&gt;
| 28-31&lt;br /&gt;
| ExchangeCount&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Header1Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::Header1Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-9&lt;br /&gt;
| RawCount (number of 32-bit words in RawData)&lt;br /&gt;
|-&lt;br /&gt;
| 10-13&lt;br /&gt;
| ReceiveListCount&lt;br /&gt;
|-&lt;br /&gt;
| 14-19&lt;br /&gt;
| Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 20-30&lt;br /&gt;
| ReceiveListOffset&lt;br /&gt;
|-&lt;br /&gt;
| 31&lt;br /&gt;
| SpecialCount&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== SpecialTag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::SpecialTag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Pid&lt;br /&gt;
|-&lt;br /&gt;
| 1-4&lt;br /&gt;
| CopyHandleCount&lt;br /&gt;
|-&lt;br /&gt;
| 5-8&lt;br /&gt;
| MoveHandleCount&lt;br /&gt;
|-&lt;br /&gt;
| 9-31&lt;br /&gt;
| Reserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Map0Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::Map0Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-31&lt;br /&gt;
| MapSizeLow (bits 0 to 31)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Map1Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::Map1Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-31&lt;br /&gt;
| MapAddress0 (bits 0 to 31)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Map2Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::Map2Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| [[#MapTransferAttribute|MapTransferAttribute]]&lt;br /&gt;
|-&lt;br /&gt;
| 2-4&lt;br /&gt;
| MapAddress36 (bits 36 to 38)&lt;br /&gt;
|-&lt;br /&gt;
| 5-23&lt;br /&gt;
| Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 24-27&lt;br /&gt;
| MapSizeHi (bits 32 to 35)&lt;br /&gt;
|-&lt;br /&gt;
| 28-31&lt;br /&gt;
| MapAddress32 (bits 32 to 35)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Pointer0Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::Pointer0Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-3&lt;br /&gt;
| PointerIndex&lt;br /&gt;
|-&lt;br /&gt;
| 4-5&lt;br /&gt;
| Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 6-8&lt;br /&gt;
| PointerAddress36 (bits 36 to 38)&lt;br /&gt;
|-&lt;br /&gt;
| 9-11&lt;br /&gt;
| Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 12-15&lt;br /&gt;
| PointerAddress32 (bits 32 to 35)&lt;br /&gt;
|-&lt;br /&gt;
| 16-31&lt;br /&gt;
| PointerSize&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Pointer1Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::Pointer1Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-31&lt;br /&gt;
| PointerAddress0 (bits 0 to 31)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ReceiveList0Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::ReceiveList0Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-31&lt;br /&gt;
| ReceiveListAddressLow (bits 0 to 31)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ReceiveList1Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::ReceiveList1Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-6&lt;br /&gt;
| ReceiveListAddressHi (bits 32 to 38)&lt;br /&gt;
|-&lt;br /&gt;
| 7-15&lt;br /&gt;
| Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 16-31&lt;br /&gt;
| ReceiveListSize&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== MapTransferAttribute ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Bit&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| AllowsNonSecure&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| AllowsNonDevice&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== MessageType ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::MessageType&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Invalid&lt;br /&gt;
|-&lt;br /&gt;
| 1 || [[#InvokeMethod|InvokeMethod]]&lt;br /&gt;
|-&lt;br /&gt;
| 2 || [[#Release|Release]]&lt;br /&gt;
|-&lt;br /&gt;
| 3 || [[#InvokeManagerMethod|InvokeManagerMethod]]&lt;br /&gt;
|-&lt;br /&gt;
| 4 || [[#Invoke2MethodOld|Invoke2MethodOld]]&lt;br /&gt;
|-&lt;br /&gt;
| 5 || [[#Invoke2ManagerMethodOld|Invoke2ManagerMethodOld]]&lt;br /&gt;
|-&lt;br /&gt;
| 6 || [5.0.0+] [[#Invoke2Method|Invoke2Method]]&lt;br /&gt;
|-&lt;br /&gt;
| 7 || [5.0.0+] [[#Invoke2ManagerMethod|Invoke2ManagerMethod]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== HipcMessageDataInfo ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcMessageDataInfo&amp;quot;. This is a 0x38-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x1 || Valid&lt;br /&gt;
|-&lt;br /&gt;
| 0x1 || 0x1 || HasSpecial&lt;br /&gt;
|-&lt;br /&gt;
| 0x2 || 0x2 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x8 || [[#HeaderData|HeaderData]]&lt;br /&gt;
|-&lt;br /&gt;
| 0xC || 0x4 || [[#SpecialTag|SpecialHeaderData]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || 0x4 || [[#HipcMessageDataOffsetInfo|DataOffsetInfo]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== HipcMessageDataOffsetInfo ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcMessageDataOffsetInfo&amp;quot;. This is a 0x28-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || PidOffset&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x4 || CopyHandleOffset&lt;br /&gt;
|-&lt;br /&gt;
| 0x8 || 0x4 || MoveHandleOffset&lt;br /&gt;
|-&lt;br /&gt;
| 0xC || 0x4 || PointerOffset&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || 0x4 || SendOffset&lt;br /&gt;
|-&lt;br /&gt;
| 0x14 || 0x4 || ReceiveOffset&lt;br /&gt;
|-&lt;br /&gt;
| 0x18 || 0x4 || ExchangeCount&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C || 0x4 || RawOffset&lt;br /&gt;
|-&lt;br /&gt;
| 0x20 || 0x4 || ReceiveListOffset&lt;br /&gt;
|-&lt;br /&gt;
| 0x24 || 0x4 || AllCount&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== RawData ==&lt;br /&gt;
Depending on the protocol, either [[#CMIF|CMIF]] or [[#TIPC|TIPC]] data is used here.&lt;br /&gt;
&lt;br /&gt;
= CMIF =&lt;br /&gt;
== Raw Data ==&lt;br /&gt;
[[File:Ipc msg buffer type a example.png|thumb|An example of an IPC message with a type 0xA (OutPointer) buffer in it. Red is headers/descriptors, yellow is padding, and blue is data/buffer lengths. Note that the size of the u16 array for type 0xA lengths is padded to fill up a whole word.]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Reserved (padding to align to 16 bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || [[#CmifMessage|CmifMessage]] or [[#CmifDomain|CmifDomain]] &lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Reserved (padding to align to 16 bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Buffer type 0xA (OutPointer) lengths (u16 array)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The total amount of padding within the raw data section is always 0x10 bytes. This means that if no padding is required before the message, there will be 0x10 bytes of padding after the message (before the buffer type 0xA (OutPointer) - lengths).&lt;br /&gt;
&lt;br /&gt;
=== CmifMessage ===&lt;br /&gt;
This is an array of u32s, but individual parameters are generally stored as u64s.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x10 || [[#CmifInHeader|CmifInHeader]] or [[#CmifOutHeader|CmifOutHeader]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || Variable || Input parameters or return values&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The rawdata struct for input parameters/return values is generated by stable-sorting function parameters by alignment, from low to high. It is likely this is a mistake, as it generates structs with suboptimal possible padding -- Nintendo probably meant to sort from high to low (which would give minimized padding), but couldn&#039;t/can&#039;t change this without breaking backwards compatibility.&lt;br /&gt;
&lt;br /&gt;
=== CmifDomain ===&lt;br /&gt;
Because the switch has relatively low limits on the total number of sessions available to the system (Kernel slabheap limits, sysmodule handle table size limits), HIPC supports a &amp;quot;Domains&amp;quot; feature that allows multiplexing multiple service sessions through a single handle. Domains store (effectively) a mapping from u32 object id to a SharedPointer&amp;lt;IServiceObject&amp;gt; -- When messages are sent to a domain, an extra header is sent in the raw data section (before anything else) with information about what object in the domain is being acted on; responses similarly contain an additional header. Official session code implements this by just using the dispatch table for the object in the map with the appropriate ID, instead of the dispatch table the session was initialized with.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x10 || [[#CmifDomainMessageInHeader|CmifDomainMessageInHeader]] or [[#CmifDomainMessageOutHeader|CmifDomainMessageOutHeader]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || Variable || [[#CmifMessage|CmifMessage]] (size must be InRawSize, only for input domain messages)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Array of [[#CmifDomainObjectId|CmifDomainObjectId]] (count must be InObjectCount, only for input domain messages)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== MessageType ==&lt;br /&gt;
IPC messages can have different types which influence how the IPC server processes requests. The message type value is passed in the lower 16 bits of the first [[#HeaderData|HeaderData]] word (see [[#Header0Tag|Header0Tag]]).&lt;br /&gt;
&lt;br /&gt;
=== InvokeMethod ===&lt;br /&gt;
This is a normal message to be processed using the regular marshalling system. Now deprecated, this type of message would be copied first to an intermediary set of internal structures by the server.&lt;br /&gt;
&lt;br /&gt;
=== Release ===&lt;br /&gt;
This is a message that tells to close and destroy the server session.&lt;br /&gt;
&lt;br /&gt;
=== InvokeManagerMethod ===&lt;br /&gt;
This is a message that requests a server manager operation. Now deprecated, this type of message would be copied first to an intermediary set of internal structures by the server.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Cmd || Name&lt;br /&gt;
|-&lt;br /&gt;
| 0 || [[#ConvertCurrentObjectToDomain|ConvertCurrentObjectToDomain]]&lt;br /&gt;
|-&lt;br /&gt;
| 1 || [[#CopyFromCurrentDomain|CopyFromCurrentDomain]]&lt;br /&gt;
|-&lt;br /&gt;
| 2 || [[#CloneCurrentObject|CloneCurrentObject]]&lt;br /&gt;
|-&lt;br /&gt;
| 3 || [[#QueryPointerBufferSize|QueryPointerBufferSize]]&lt;br /&gt;
|-&lt;br /&gt;
| 4 || [[#CloneCurrentObjectEx|CloneCurrentObjectEx]] &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== ConvertCurrentObjectToDomain ====&lt;br /&gt;
No input. Returns an output [[#CmifDomainObjectId|CmifDomainObjectId]].&lt;br /&gt;
&lt;br /&gt;
==== CopyFromCurrentDomain ====&lt;br /&gt;
Takes an input [[#CmifDomainObjectId|CmifDomainObjectId]]. Returns an output handle.&lt;br /&gt;
&lt;br /&gt;
==== CloneCurrentObject ====&lt;br /&gt;
No input. Returns an output handle.&lt;br /&gt;
&lt;br /&gt;
==== QueryPointerBufferSize ====&lt;br /&gt;
No input. Returns an output u16 &#039;&#039;&#039;PointerBufferSize&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== CloneCurrentObjectEx ====&lt;br /&gt;
Takes an input u32 &#039;&#039;&#039;Tag&#039;&#039;&#039;. Returns an output handle.&lt;br /&gt;
&lt;br /&gt;
=== Invoke2MethodOld ===&lt;br /&gt;
Same as [[#InvokeMethod|InvokeMethod]] but using a more streamlined logic that no longer requires additional internal copying and parsing.&lt;br /&gt;
&lt;br /&gt;
=== Invoke2ManagerMethodOld ===&lt;br /&gt;
Same as [[#InvokeManagerMethod|InvokeManagerMethod]] but using a more streamlined logic that no longer requires additional internal copying and parsing.&lt;br /&gt;
&lt;br /&gt;
=== Invoke2Method ===&lt;br /&gt;
Same as [[#Invoke2MethodOld|Invoke2MethodOld]] but with the additional requirement of suppling a token in the [[#CmifInHeader|CmifInHeader]].&lt;br /&gt;
&lt;br /&gt;
This token is used by &amp;quot;nn::sf::cmif::SetInlineContext&amp;quot; which has the sole purpose of saving it into the TLS in order for it to be distributed to any IPC commands that are made while processing the current command. It&#039;s unknown if this token serves any purpose or if it&#039;s just a debug-tool to figure out what IPC command caused a particular chain of commands.&lt;br /&gt;
&lt;br /&gt;
=== Invoke2ManagerMethod ===&lt;br /&gt;
Same as [[#Invoke2ManagerMethodOld|Invoke2ManagerMethodOld]] but with the additional requirement of suppling a token in the [[#CmifInHeader|CmifInHeader]].&lt;br /&gt;
&lt;br /&gt;
This token is used by &amp;quot;nn::sf::cmif::SetInlineContext&amp;quot; which has the sole purpose of saving it into the TLS in order for it to be distributed to any IPC commands that are made while processing the current command. It&#039;s unknown if this token serves any purpose or if it&#039;s just a debug-tool to figure out what IPC command caused a particular chain of commands.&lt;br /&gt;
&lt;br /&gt;
== CmifInHeader ==&lt;br /&gt;
This is &amp;quot;nn::sf::cmif::CmifInHeader&amp;quot;. This is a 0x10-byte struct.&lt;br /&gt;
&lt;br /&gt;
[5.0.0+] Version was incremented from 0 to 1.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || Signature (&amp;quot;SFCI&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x2 || Version&lt;br /&gt;
|-&lt;br /&gt;
| 0x6 || 0x2 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x8 || 0x4 || MethodId&lt;br /&gt;
|-&lt;br /&gt;
| 0xC || 0x4 || [5.0.0+] Token ([1.0.0-4.1.0] Reserved)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== CmifOutHeader ==&lt;br /&gt;
This is &amp;quot;nn::sf::cmif::CmifOutHeader&amp;quot;. This is a 0x10-byte struct.&lt;br /&gt;
&lt;br /&gt;
[14.0.0+] Reserved field at +0xC is now InterfaceId, generated as little endian first four bytes of sha256(&amp;lt;fully qualified interface name&amp;gt;). The version field was not incremented.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || Signature (&amp;quot;SFCO&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x2 || Version&lt;br /&gt;
|-&lt;br /&gt;
| 0x6 || 0x2 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x8 || 0x4 || Result&lt;br /&gt;
|-&lt;br /&gt;
| 0xC || 0x4 || [14.0.0+] InterfaceId ([1.0.0-13.2.1] Reserved)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== CmifDomainMessageInHeader ==&lt;br /&gt;
This is &amp;quot;nn::sf::cmif::detail::CmifDomainMessageInHeader&amp;quot;. This is a 0x10-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x1 || [[#CmifDomainRequestKind|RequestKind]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x1 || 0x1 || InObjectCount&lt;br /&gt;
|-&lt;br /&gt;
| 0x2 || 0x2 || InRawSize&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x4 || [[#CmifDomainObjectId|TargetObjectId]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x8 || 0x4 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0xC || 0x4 || [5.0.0+] Token ([1.0.0-4.1.0] Reserved)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== CmifDomainMessageOutHeader ==&lt;br /&gt;
This is &amp;quot;nn::sf::cmif::detail::CmifDomainMessageOutHeader&amp;quot;. This is a 0x10-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || OutObjectCount&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0xC || Reserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== CmifDomainObjectId ==&lt;br /&gt;
This is &amp;quot;nn::sf::cmif::CmifDomainObjectId&amp;quot;. This is a 4 byte value.&lt;br /&gt;
&lt;br /&gt;
== CmifDomainRequestKind ==&lt;br /&gt;
This is &amp;quot;nn::sf::cmif::detail::CmifDomainRequestKind&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Invalid&lt;br /&gt;
|-&lt;br /&gt;
| 1 || InvokeMethod&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Release&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== BufferAttribute ==&lt;br /&gt;
This is &amp;quot;nn::sf::cmif::BufferAttribute&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| In&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Out&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| HipcMapAlias&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| HipcPointer&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| FixedSize&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| HipcAutoSelect&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| HipcMapTransferAllowsNonSecure&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| HipcMapTransferAllowsNonDevice&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NativeHandleAttribute ==&lt;br /&gt;
This is &amp;quot;nn::sf::cmif::NativeHandleAttribute&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| HipcCopy&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| HipcMove&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= TIPC =&lt;br /&gt;
TIPC (Tiny IPC) is a simpler protocol than CMIF. It has no concept of domains, so it cannot support multiple objects per session. It is only used in the Service Manager.&lt;br /&gt;
&lt;br /&gt;
== MessageType ==&lt;br /&gt;
In TIPC, the request ID is stored in the lowest 16 bits of [[#Header0Tag|Header0Tag]].&lt;br /&gt;
&lt;br /&gt;
== RawData ==&lt;br /&gt;
In TIPC, [[#RawData|RawData]] field directly contains the payload.&lt;br /&gt;
&lt;br /&gt;
= Server =&lt;br /&gt;
Send/Receive/Exchange data buffers map memory into the sysmodule process. For the mapped memory in the sysmodule the permissions are: Send = R--, Receive = RW-. The buffer is automatically unmapped while the kernel handles the cmdreply, the sysmodule doesn&#039;t need to specify anything in the cmdreply to trigger this.&lt;br /&gt;
&lt;br /&gt;
This memory is mapped in the sysmodule to the same vaddr from the original user-process cmd-request, except with with bits &amp;gt;=(~28(?)) changed to a different ASLR&#039;d region.&lt;br /&gt;
&lt;br /&gt;
No user-process-&amp;gt;sysmodule memcpy is done for outbufs, only sysmodule-&amp;gt;user-process.&lt;br /&gt;
&lt;br /&gt;
ReceiveList/Pointer data buffers are somewhat different. Rather than mapping new memory into the server process, ReceiveList/Pointer data buffers copy data between existing buffers in different processes. Each Pointer data buffer in a message has its data copied into a ReceiveList data buffer on the other side. Each Pointer data buffer in a message is used to reserve space for the other side&#039;s ReceiveList data buffer to copy into.&lt;br /&gt;
&lt;br /&gt;
When the kernel processes Pointer data buffers, it must determine where to copy the data to. If the destination used ReceiveList data buffers with flags &amp;gt;= 3, each Pointer data buffer from the source is matched to a ReceiveList data buffer in the destination by the Pointer data buffer&#039;s ReceiveIndex field. If the destination used a single ReceiveList data buffer, the data from all the Pointer data buffers is copied into the same buffer specified by the destination&#039;s ReceiveList data buffer (causing error 0xce01 if there is not enough space) and the Pointer data buffer&#039;s ReceiveIndex is ignored. The kernel then modifies the addresses in the Pointer data buffers to indicate where the data was copied to in the destination.&lt;br /&gt;
&lt;br /&gt;
Before receiving a request, if the IPC server is expecting Pointer data buffers, it prepares a message with a single ReceiveList data buffer (flags=2) in its message buffer before calling svcReplyAndReceive so that Pointer data buffers from the client have a place to copy their data to. The usage of the flag-2 ReceiveList data buffer allows the server to receive an arbitrary number of Pointer data buffers, since they&#039;re all packed into the same buffer. If the server had used flag-3+ ReceiveList data buffers, it would be limited in how many Pointer data buffers it could receive since the Pointer data buffers would have to be matched to distinct ReceiveList data buffers. The buffer that the server&#039;s ReceiveList data buffer points to is called the &#039;&#039;&#039;pointer buffer&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When the client sends Pointer data buffers, data is copied into the server&#039;s pointer buffer. When the client sends ReceiveList data buffers, no data is copied automatically. The server needs to use Pointer data buffers to copy the data back to the client&#039;s ReceiveList data buffers (using the index field to match Pointer data buffers in the response back to the correct ReceiveList data buffers).&lt;br /&gt;
&lt;br /&gt;
= Client =&lt;br /&gt;
The official marshalling function for the client is called &amp;quot;nn::sf::hipc::client::Hipc2ClientCoreProcessorImpl::WriteBufferDataImpl&amp;quot; and takes:&lt;br /&gt;
* A pointer to a &amp;quot;nn::sf::hipc::detail::HipcMessageWriter&amp;quot; context;&lt;br /&gt;
* The number of (buf_ptr, size) pairs;&lt;br /&gt;
* An array of (buf_ptr, size) pairs (called &amp;quot;nn::sf::detail::PointerAndSize&amp;quot;);&lt;br /&gt;
* A pointer to a type bitfield for each such pair;&lt;br /&gt;
* The offset of the main IPC command structure;&lt;br /&gt;
* The size of the IPC command&#039;s raw data payload.&lt;br /&gt;
&lt;br /&gt;
Pointer and ReceiveList data buffers are backed by the &amp;quot;pointer buffer&amp;quot;, a buffer in the service process. Its size is a u16, which is retrieved using the &amp;quot;QueryPointerBufferSize&amp;quot; control message. If the client code determines all buffers with [[#BufferAttribute|BufferAttribute]] HipcPointer do not fit in the pointer buffer, it returns error 0x11A0B.&lt;br /&gt;
&lt;br /&gt;
For buffers with [[#BufferAttribute|BufferAttribute]] HipcAutoSelect, it creates two data buffers (Send+Pointer or Receive+ReceiveList), but one of them is NULL (zero size and pointer), while the other holds the expected values. Pointer/ReceiveList data buffers are used as the non-NULL buffer where possible, but if they don&#039;t fit in the pointer buffer, Send/Receive descriptors are used instead. The code defers processing of HipcAutoSelect buffers with sizes that fit in a u16 (and may therefore fit in the pointer buffer), which ensures all HipcPointer buffers get pointer-buffer space before any HipcAutoSelect. The order in which the deferred HipcAutoSelect buffers are processed is determined by a convoluted loop.&lt;/div&gt;</summary>
		<author><name>Yannik</name></author>
	</entry>
	<entry>
		<id>https://switchbrew.org/w/index.php?title=HIPC&amp;diff=13378</id>
		<title>HIPC</title>
		<link rel="alternate" type="text/html" href="https://switchbrew.org/w/index.php?title=HIPC&amp;diff=13378"/>
		<updated>2025-06-06T14:46:18Z</updated>

		<summary type="html">&lt;p&gt;Yannik: Clarify RawCount&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;HIPC (Horizon Inter-Process Communication) is a custom IPC implementation tailored for the Horizon OS.&lt;br /&gt;
&lt;br /&gt;
There are two protocols over [[#HIPC|HIPC]]:&lt;br /&gt;
&lt;br /&gt;
* [[#CMIF|CMIF]]: Command Interface (?). Supports session management with domains and dynamic multiplexing of services.&lt;br /&gt;
* [[#TIPC|TIPC]]: Tiny IPC. Simple and lightweight protocol.&lt;br /&gt;
&lt;br /&gt;
[[#CMIF|CMIF]] is universally used for virtually almost all Switch services. [[#TIPC|TIPC]] is only used by the [[Services_API|Service Manager]] and [[PGL_services|pgl]].&lt;br /&gt;
&lt;br /&gt;
= HIPC =&lt;br /&gt;
This is a buffer located in the [[Thread Local Region]].&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x8 || [[#HeaderData|HeaderData]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x8 || 0x4 || [[#SpecialTag|SpecialHeaderData]] (if [[#Header1Tag|SpecialCount]] is set)&lt;br /&gt;
|-&lt;br /&gt;
| 0xC || 0x8 || ProcessId (if [[#SpecialTag|Pid]] is set)&lt;br /&gt;
|-&lt;br /&gt;
| 0x14 || Variable || Array of CopyHandle (if [[#SpecialTag|CopyHandleCount]] &amp;gt; 0)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Array of MoveHandle (if [[#SpecialTag|MoveHandleCount]] &amp;gt; 0)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Array of [[#PointerData|PointerData]] (if [[#Header0Tag|PointerCount]] &amp;gt; 0)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Array of [[#MapData|SendData]] (if [[#Header0Tag|SendCount]] &amp;gt; 0)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Array of [[#MapData|ReceiveData]] (if [[#Header0Tag|ReceiveCount]] &amp;gt; 0)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Array of [[#MapData|ExchangeData]] (if [[#Header0Tag|ExchangeCount]] &amp;gt; 0)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || [[#RawData|RawData]] (if [[#Header1Tag|RawCount]] &amp;gt; 0)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Array of [[#ReceiveListData|ReceiveListData]] (if [[#Header1Tag|ReceiveListCount]] &amp;gt; 0)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
[[#HeaderData|HeaderData]] and [[#SpecialTag|SpecialHeaderData]] (if available) are copied as-is from one process to another.&lt;br /&gt;
&lt;br /&gt;
Sysmodules load the last u64 of rawdata when handling the ProcessId. This is not written by kernel. For sysmodule handling:&lt;br /&gt;
* In some cases: these commands require a placeholder u64 value passed in the input parameters, as mentioned above. In these cases the OverwriteClientProcessId method is called to replace the value before it is used.&lt;br /&gt;
* In other cases: The rawdata_u64 is compared with the ProcessId. On mismatch and when rawdata_u64!=0, error 0x60A is returned. The ProcessId value passed to the cmdhandler vtable funcptr is the rawdata_u64.&lt;br /&gt;
&lt;br /&gt;
Handle 0 is allowed, and just means no handle was sent.&lt;br /&gt;
&lt;br /&gt;
A reply must not use Send/Receive/Exchange data buffers, svcReplyAndReceive will return 0xE801. [[SVC|MemoryAttribute]] IsBorrowed and IsUncached are never allowed for the source address. &amp;quot;Send&amp;quot; means buffer is sent from source process into service process, &amp;quot;Receive&amp;quot; means that data is copied from service process into user process and &amp;quot;Exchange&amp;quot; means both &amp;quot;Send&amp;quot; and &amp;quot;Receive&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
[[#HeaderData|HeaderData]]&#039;s ReceiveListCount field controls the behavior of [[#ReceiveListData|ReceiveListData]].&lt;br /&gt;
&lt;br /&gt;
If ReceiveListCount is 0, [[#ReceiveListData|ReceiveListData]] is disabled. If ReceiveListCount is 1, there is an &amp;quot;inlined&amp;quot; [[#ReceiveListData|ReceiveListData]] buffer after the raw data (received data is copied to ROUND_UP(cmdbuf+raw_size+index, 16)). If ReceiveListCount is 2, there is a single [[#ReceiveListData|ReceiveListData]] buffer. Otherwise it has (ReceiveListCount-2) [[#ReceiveListData|ReceiveListData]] buffers. In this case, ReceiveIndex picks which [[#ReceiveListData|ReceiveListData]] buffer to copy received data to [instead of picking the offset into the buffer]. Data sent with this method must have MemoryState 0x4000000 mask set.&lt;br /&gt;
&lt;br /&gt;
After reply, [[#PointerData|PointerData]] buffers are written to the sender containing the AddressValue, Size and ReceiveIndex that were copied to.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== HeaderData ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::HeaderData&amp;quot;. This is a 0x8-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || [[#Header0Tag|Header0]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x4 || [[#Header1Tag|Header1]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== MapData ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::MapData&amp;quot;. This is a 0xC-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || [[#Map0Tag|Data0]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x4 || [[#Map1Tag|Data1]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x8 || 0x4 || [[#Map2Tag|Data2]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== PointerData ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::PointerData&amp;quot;. This is a 0x8-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || [[#Pointer0Tag|Data0]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x4 || [[#Pointer1Tag|Data1]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ReceiveListData ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::ReceiveListData&amp;quot;. This is a 0x8-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || [[#ReceiveList0Tag|Data0]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x4 || [[#ReceiveList1Tag|Data1]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Header0Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::Header0Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-15&lt;br /&gt;
| Tag&lt;br /&gt;
|-&lt;br /&gt;
| 16-19&lt;br /&gt;
| PointerCount&lt;br /&gt;
|-&lt;br /&gt;
| 20-23&lt;br /&gt;
| SendCount&lt;br /&gt;
|-&lt;br /&gt;
| 24-27&lt;br /&gt;
| ReceiveCount&lt;br /&gt;
|-&lt;br /&gt;
| 28-31&lt;br /&gt;
| ExchangeCount&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Header1Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::Header1Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-9&lt;br /&gt;
| RawCount (number of 32-bit words in RawData)&lt;br /&gt;
|-&lt;br /&gt;
| 10-13&lt;br /&gt;
| ReceiveListCount&lt;br /&gt;
|-&lt;br /&gt;
| 14-30&lt;br /&gt;
| Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 31&lt;br /&gt;
| SpecialCount&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== SpecialTag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::SpecialTag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| Pid&lt;br /&gt;
|-&lt;br /&gt;
| 1-4&lt;br /&gt;
| CopyHandleCount&lt;br /&gt;
|-&lt;br /&gt;
| 5-8&lt;br /&gt;
| MoveHandleCount&lt;br /&gt;
|-&lt;br /&gt;
| 9-31&lt;br /&gt;
| Reserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Map0Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::Map0Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-31&lt;br /&gt;
| MapSizeLow (bits 0 to 31)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Map1Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::Map1Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-31&lt;br /&gt;
| MapAddress0 (bits 0 to 31)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Map2Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::Map2Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-1&lt;br /&gt;
| [[#MapTransferAttribute|MapTransferAttribute]]&lt;br /&gt;
|-&lt;br /&gt;
| 2-4&lt;br /&gt;
| MapAddress36 (bits 36 to 38)&lt;br /&gt;
|-&lt;br /&gt;
| 5-23&lt;br /&gt;
| Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 24-27&lt;br /&gt;
| MapSizeHi (bits 32 to 35)&lt;br /&gt;
|-&lt;br /&gt;
| 28-31&lt;br /&gt;
| MapAddress32 (bits 32 to 35)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Pointer0Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::Pointer0Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-3&lt;br /&gt;
| PointerIndex&lt;br /&gt;
|-&lt;br /&gt;
| 4-5&lt;br /&gt;
| Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 6-8&lt;br /&gt;
| PointerAddress36 (bits 36 to 38)&lt;br /&gt;
|-&lt;br /&gt;
| 9-11&lt;br /&gt;
| Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 12-15&lt;br /&gt;
| PointerAddress32 (bits 32 to 35)&lt;br /&gt;
|-&lt;br /&gt;
| 16-31&lt;br /&gt;
| PointerSize&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Pointer1Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::Pointer1Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-31&lt;br /&gt;
| PointerAddress0 (bits 0 to 31)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ReceiveList0Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::ReceiveList0Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-31&lt;br /&gt;
| ReceiveListAddressLow (bits 0 to 31)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== ReceiveList1Tag ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcFormat::ReceiveList1Tag&amp;quot;. This is a 32-bit flag.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0-6&lt;br /&gt;
| ReceiveListAddressHi (bits 32 to 38)&lt;br /&gt;
|-&lt;br /&gt;
| 7-15&lt;br /&gt;
| Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 16-31&lt;br /&gt;
| ReceiveListSize&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== MapTransferAttribute ==&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Bit&lt;br /&gt;
! Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| AllowsNonSecure&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| AllowsNonDevice&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== MessageType ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::MessageType&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Invalid&lt;br /&gt;
|-&lt;br /&gt;
| 1 || [[#InvokeMethod|InvokeMethod]]&lt;br /&gt;
|-&lt;br /&gt;
| 2 || [[#Release|Release]]&lt;br /&gt;
|-&lt;br /&gt;
| 3 || [[#InvokeManagerMethod|InvokeManagerMethod]]&lt;br /&gt;
|-&lt;br /&gt;
| 4 || [[#Invoke2MethodOld|Invoke2MethodOld]]&lt;br /&gt;
|-&lt;br /&gt;
| 5 || [[#Invoke2ManagerMethodOld|Invoke2ManagerMethodOld]]&lt;br /&gt;
|-&lt;br /&gt;
| 6 || [5.0.0+] [[#Invoke2Method|Invoke2Method]]&lt;br /&gt;
|-&lt;br /&gt;
| 7 || [5.0.0+] [[#Invoke2ManagerMethod|Invoke2ManagerMethod]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== HipcMessageDataInfo ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcMessageDataInfo&amp;quot;. This is a 0x38-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x1 || Valid&lt;br /&gt;
|-&lt;br /&gt;
| 0x1 || 0x1 || HasSpecial&lt;br /&gt;
|-&lt;br /&gt;
| 0x2 || 0x2 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x8 || [[#HeaderData|HeaderData]]&lt;br /&gt;
|-&lt;br /&gt;
| 0xC || 0x4 || [[#SpecialTag|SpecialHeaderData]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || 0x4 || [[#HipcMessageDataOffsetInfo|DataOffsetInfo]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== HipcMessageDataOffsetInfo ==&lt;br /&gt;
This is &amp;quot;nn::sf::hipc::detail::HipcMessageDataOffsetInfo&amp;quot;. This is a 0x28-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || PidOffset&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x4 || CopyHandleOffset&lt;br /&gt;
|-&lt;br /&gt;
| 0x8 || 0x4 || MoveHandleOffset&lt;br /&gt;
|-&lt;br /&gt;
| 0xC || 0x4 || PointerOffset&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || 0x4 || SendOffset&lt;br /&gt;
|-&lt;br /&gt;
| 0x14 || 0x4 || ReceiveOffset&lt;br /&gt;
|-&lt;br /&gt;
| 0x18 || 0x4 || ExchangeCount&lt;br /&gt;
|-&lt;br /&gt;
| 0x1C || 0x4 || RawOffset&lt;br /&gt;
|-&lt;br /&gt;
| 0x20 || 0x4 || ReceiveListOffset&lt;br /&gt;
|-&lt;br /&gt;
| 0x24 || 0x4 || AllCount&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== RawData ==&lt;br /&gt;
Depending on the protocol, either [[#CMIF|CMIF]] or [[#TIPC|TIPC]] data is used here.&lt;br /&gt;
&lt;br /&gt;
= CMIF =&lt;br /&gt;
== Raw Data ==&lt;br /&gt;
[[File:Ipc msg buffer type a example.png|thumb|An example of an IPC message with a type 0xA (OutPointer) buffer in it. Red is headers/descriptors, yellow is padding, and blue is data/buffer lengths. Note that the size of the u16 array for type 0xA lengths is padded to fill up a whole word.]]&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Reserved (padding to align to 16 bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || [[#CmifMessage|CmifMessage]] or [[#CmifDomain|CmifDomain]] &lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Reserved (padding to align to 16 bytes)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Buffer type 0xA (OutPointer) lengths (u16 array)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The total amount of padding within the raw data section is always 0x10 bytes. This means that if no padding is required before the message, there will be 0x10 bytes of padding after the message (before the buffer type 0xA (OutPointer) - lengths).&lt;br /&gt;
&lt;br /&gt;
=== CmifMessage ===&lt;br /&gt;
This is an array of u32s, but individual parameters are generally stored as u64s.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x10 || [[#CmifInHeader|CmifInHeader]] or [[#CmifOutHeader|CmifOutHeader]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || Variable || Input parameters or return values&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The rawdata struct for input parameters/return values is generated by stable-sorting function parameters by alignment, from low to high. It is likely this is a mistake, as it generates structs with suboptimal possible padding -- Nintendo probably meant to sort from high to low (which would give minimized padding), but couldn&#039;t/can&#039;t change this without breaking backwards compatibility.&lt;br /&gt;
&lt;br /&gt;
=== CmifDomain ===&lt;br /&gt;
Because the switch has relatively low limits on the total number of sessions available to the system (Kernel slabheap limits, sysmodule handle table size limits), HIPC supports a &amp;quot;Domains&amp;quot; feature that allows multiplexing multiple service sessions through a single handle. Domains store (effectively) a mapping from u32 object id to a SharedPointer&amp;lt;IServiceObject&amp;gt; -- When messages are sent to a domain, an extra header is sent in the raw data section (before anything else) with information about what object in the domain is being acted on; responses similarly contain an additional header. Official session code implements this by just using the dispatch table for the object in the map with the appropriate ID, instead of the dispatch table the session was initialized with.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x10 || [[#CmifDomainMessageInHeader|CmifDomainMessageInHeader]] or [[#CmifDomainMessageOutHeader|CmifDomainMessageOutHeader]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x10 || Variable || [[#CmifMessage|CmifMessage]] (size must be InRawSize, only for input domain messages)&lt;br /&gt;
|-&lt;br /&gt;
| Variable || Variable || Array of [[#CmifDomainObjectId|CmifDomainObjectId]] (count must be InObjectCount, only for input domain messages)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== MessageType ==&lt;br /&gt;
IPC messages can have different types which influence how the IPC server processes requests. The message type value is passed in the lower 16 bits of the first [[#HeaderData|HeaderData]] word (see [[#Header0Tag|Header0Tag]]).&lt;br /&gt;
&lt;br /&gt;
=== InvokeMethod ===&lt;br /&gt;
This is a normal message to be processed using the regular marshalling system. Now deprecated, this type of message would be copied first to an intermediary set of internal structures by the server.&lt;br /&gt;
&lt;br /&gt;
=== Release ===&lt;br /&gt;
This is a message that tells to close and destroy the server session.&lt;br /&gt;
&lt;br /&gt;
=== InvokeManagerMethod ===&lt;br /&gt;
This is a message that requests a server manager operation. Now deprecated, this type of message would be copied first to an intermediary set of internal structures by the server.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Cmd || Name&lt;br /&gt;
|-&lt;br /&gt;
| 0 || [[#ConvertCurrentObjectToDomain|ConvertCurrentObjectToDomain]]&lt;br /&gt;
|-&lt;br /&gt;
| 1 || [[#CopyFromCurrentDomain|CopyFromCurrentDomain]]&lt;br /&gt;
|-&lt;br /&gt;
| 2 || [[#CloneCurrentObject|CloneCurrentObject]]&lt;br /&gt;
|-&lt;br /&gt;
| 3 || [[#QueryPointerBufferSize|QueryPointerBufferSize]]&lt;br /&gt;
|-&lt;br /&gt;
| 4 || [[#CloneCurrentObjectEx|CloneCurrentObjectEx]] &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== ConvertCurrentObjectToDomain ====&lt;br /&gt;
No input. Returns an output [[#CmifDomainObjectId|CmifDomainObjectId]].&lt;br /&gt;
&lt;br /&gt;
==== CopyFromCurrentDomain ====&lt;br /&gt;
Takes an input [[#CmifDomainObjectId|CmifDomainObjectId]]. Returns an output handle.&lt;br /&gt;
&lt;br /&gt;
==== CloneCurrentObject ====&lt;br /&gt;
No input. Returns an output handle.&lt;br /&gt;
&lt;br /&gt;
==== QueryPointerBufferSize ====&lt;br /&gt;
No input. Returns an output u16 &#039;&#039;&#039;PointerBufferSize&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==== CloneCurrentObjectEx ====&lt;br /&gt;
Takes an input u32 &#039;&#039;&#039;Tag&#039;&#039;&#039;. Returns an output handle.&lt;br /&gt;
&lt;br /&gt;
=== Invoke2MethodOld ===&lt;br /&gt;
Same as [[#InvokeMethod|InvokeMethod]] but using a more streamlined logic that no longer requires additional internal copying and parsing.&lt;br /&gt;
&lt;br /&gt;
=== Invoke2ManagerMethodOld ===&lt;br /&gt;
Same as [[#InvokeManagerMethod|InvokeManagerMethod]] but using a more streamlined logic that no longer requires additional internal copying and parsing.&lt;br /&gt;
&lt;br /&gt;
=== Invoke2Method ===&lt;br /&gt;
Same as [[#Invoke2MethodOld|Invoke2MethodOld]] but with the additional requirement of suppling a token in the [[#CmifInHeader|CmifInHeader]].&lt;br /&gt;
&lt;br /&gt;
This token is used by &amp;quot;nn::sf::cmif::SetInlineContext&amp;quot; which has the sole purpose of saving it into the TLS in order for it to be distributed to any IPC commands that are made while processing the current command. It&#039;s unknown if this token serves any purpose or if it&#039;s just a debug-tool to figure out what IPC command caused a particular chain of commands.&lt;br /&gt;
&lt;br /&gt;
=== Invoke2ManagerMethod ===&lt;br /&gt;
Same as [[#Invoke2ManagerMethodOld|Invoke2ManagerMethodOld]] but with the additional requirement of suppling a token in the [[#CmifInHeader|CmifInHeader]].&lt;br /&gt;
&lt;br /&gt;
This token is used by &amp;quot;nn::sf::cmif::SetInlineContext&amp;quot; which has the sole purpose of saving it into the TLS in order for it to be distributed to any IPC commands that are made while processing the current command. It&#039;s unknown if this token serves any purpose or if it&#039;s just a debug-tool to figure out what IPC command caused a particular chain of commands.&lt;br /&gt;
&lt;br /&gt;
== CmifInHeader ==&lt;br /&gt;
This is &amp;quot;nn::sf::cmif::CmifInHeader&amp;quot;. This is a 0x10-byte struct.&lt;br /&gt;
&lt;br /&gt;
[5.0.0+] Version was incremented from 0 to 1.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || Signature (&amp;quot;SFCI&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x2 || Version&lt;br /&gt;
|-&lt;br /&gt;
| 0x6 || 0x2 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x8 || 0x4 || MethodId&lt;br /&gt;
|-&lt;br /&gt;
| 0xC || 0x4 || [5.0.0+] Token ([1.0.0-4.1.0] Reserved)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== CmifOutHeader ==&lt;br /&gt;
This is &amp;quot;nn::sf::cmif::CmifOutHeader&amp;quot;. This is a 0x10-byte struct.&lt;br /&gt;
&lt;br /&gt;
[14.0.0+] Reserved field at +0xC is now InterfaceId, generated as little endian first four bytes of sha256(&amp;lt;fully qualified interface name&amp;gt;). The version field was not incremented.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || Signature (&amp;quot;SFCO&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x2 || Version&lt;br /&gt;
|-&lt;br /&gt;
| 0x6 || 0x2 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0x8 || 0x4 || Result&lt;br /&gt;
|-&lt;br /&gt;
| 0xC || 0x4 || [14.0.0+] InterfaceId ([1.0.0-13.2.1] Reserved)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== CmifDomainMessageInHeader ==&lt;br /&gt;
This is &amp;quot;nn::sf::cmif::detail::CmifDomainMessageInHeader&amp;quot;. This is a 0x10-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x1 || [[#CmifDomainRequestKind|RequestKind]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x1 || 0x1 || InObjectCount&lt;br /&gt;
|-&lt;br /&gt;
| 0x2 || 0x2 || InRawSize&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0x4 || [[#CmifDomainObjectId|TargetObjectId]]&lt;br /&gt;
|-&lt;br /&gt;
| 0x8 || 0x4 || Reserved&lt;br /&gt;
|-&lt;br /&gt;
| 0xC || 0x4 || [5.0.0+] Token ([1.0.0-4.1.0] Reserved)&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== CmifDomainMessageOutHeader ==&lt;br /&gt;
This is &amp;quot;nn::sf::cmif::detail::CmifDomainMessageOutHeader&amp;quot;. This is a 0x10-byte struct.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Offset || Size || Description&lt;br /&gt;
|-&lt;br /&gt;
| 0x0 || 0x4 || OutObjectCount&lt;br /&gt;
|-&lt;br /&gt;
| 0x4 || 0xC || Reserved&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== CmifDomainObjectId ==&lt;br /&gt;
This is &amp;quot;nn::sf::cmif::CmifDomainObjectId&amp;quot;. This is a 4 byte value.&lt;br /&gt;
&lt;br /&gt;
== CmifDomainRequestKind ==&lt;br /&gt;
This is &amp;quot;nn::sf::cmif::detail::CmifDomainRequestKind&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Value&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0 || Invalid&lt;br /&gt;
|-&lt;br /&gt;
| 1 || InvokeMethod&lt;br /&gt;
|-&lt;br /&gt;
| 2 || Release&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== BufferAttribute ==&lt;br /&gt;
This is &amp;quot;nn::sf::cmif::BufferAttribute&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| In&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Out&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| HipcMapAlias&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| HipcPointer&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| FixedSize&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| HipcAutoSelect&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| HipcMapTransferAllowsNonSecure&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| HipcMapTransferAllowsNonDevice&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== NativeHandleAttribute ==&lt;br /&gt;
This is &amp;quot;nn::sf::cmif::NativeHandleAttribute&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot; border=&amp;quot;1&amp;quot;&lt;br /&gt;
!  Bits&lt;br /&gt;
!  Description&lt;br /&gt;
|-&lt;br /&gt;
| 0&lt;br /&gt;
| HipcCopy&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| HipcMove&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= TIPC =&lt;br /&gt;
TIPC (Tiny IPC) is a simpler protocol than CMIF. It has no concept of domains, so it cannot support multiple objects per session. It is only used in the Service Manager.&lt;br /&gt;
&lt;br /&gt;
== MessageType ==&lt;br /&gt;
In TIPC, the request ID is stored in the lowest 16 bits of [[#Header0Tag|Header0Tag]].&lt;br /&gt;
&lt;br /&gt;
== RawData ==&lt;br /&gt;
In TIPC, [[#RawData|RawData]] field directly contains the payload.&lt;br /&gt;
&lt;br /&gt;
= Server =&lt;br /&gt;
Send/Receive/Exchange data buffers map memory into the sysmodule process. For the mapped memory in the sysmodule the permissions are: Send = R--, Receive = RW-. The buffer is automatically unmapped while the kernel handles the cmdreply, the sysmodule doesn&#039;t need to specify anything in the cmdreply to trigger this.&lt;br /&gt;
&lt;br /&gt;
This memory is mapped in the sysmodule to the same vaddr from the original user-process cmd-request, except with with bits &amp;gt;=(~28(?)) changed to a different ASLR&#039;d region.&lt;br /&gt;
&lt;br /&gt;
No user-process-&amp;gt;sysmodule memcpy is done for outbufs, only sysmodule-&amp;gt;user-process.&lt;br /&gt;
&lt;br /&gt;
ReceiveList/Pointer data buffers are somewhat different. Rather than mapping new memory into the server process, ReceiveList/Pointer data buffers copy data between existing buffers in different processes. Each Pointer data buffer in a message has its data copied into a ReceiveList data buffer on the other side. Each Pointer data buffer in a message is used to reserve space for the other side&#039;s ReceiveList data buffer to copy into.&lt;br /&gt;
&lt;br /&gt;
When the kernel processes Pointer data buffers, it must determine where to copy the data to. If the destination used ReceiveList data buffers with flags &amp;gt;= 3, each Pointer data buffer from the source is matched to a ReceiveList data buffer in the destination by the Pointer data buffer&#039;s ReceiveIndex field. If the destination used a single ReceiveList data buffer, the data from all the Pointer data buffers is copied into the same buffer specified by the destination&#039;s ReceiveList data buffer (causing error 0xce01 if there is not enough space) and the Pointer data buffer&#039;s ReceiveIndex is ignored. The kernel then modifies the addresses in the Pointer data buffers to indicate where the data was copied to in the destination.&lt;br /&gt;
&lt;br /&gt;
Before receiving a request, if the IPC server is expecting Pointer data buffers, it prepares a message with a single ReceiveList data buffer (flags=2) in its message buffer before calling svcReplyAndReceive so that Pointer data buffers from the client have a place to copy their data to. The usage of the flag-2 ReceiveList data buffer allows the server to receive an arbitrary number of Pointer data buffers, since they&#039;re all packed into the same buffer. If the server had used flag-3+ ReceiveList data buffers, it would be limited in how many Pointer data buffers it could receive since the Pointer data buffers would have to be matched to distinct ReceiveList data buffers. The buffer that the server&#039;s ReceiveList data buffer points to is called the &#039;&#039;&#039;pointer buffer&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When the client sends Pointer data buffers, data is copied into the server&#039;s pointer buffer. When the client sends ReceiveList data buffers, no data is copied automatically. The server needs to use Pointer data buffers to copy the data back to the client&#039;s ReceiveList data buffers (using the index field to match Pointer data buffers in the response back to the correct ReceiveList data buffers).&lt;br /&gt;
&lt;br /&gt;
= Client =&lt;br /&gt;
The official marshalling function for the client is called &amp;quot;nn::sf::hipc::client::Hipc2ClientCoreProcessorImpl::WriteBufferDataImpl&amp;quot; and takes:&lt;br /&gt;
* A pointer to a &amp;quot;nn::sf::hipc::detail::HipcMessageWriter&amp;quot; context;&lt;br /&gt;
* The number of (buf_ptr, size) pairs;&lt;br /&gt;
* An array of (buf_ptr, size) pairs (called &amp;quot;nn::sf::detail::PointerAndSize&amp;quot;);&lt;br /&gt;
* A pointer to a type bitfield for each such pair;&lt;br /&gt;
* The offset of the main IPC command structure;&lt;br /&gt;
* The size of the IPC command&#039;s raw data payload.&lt;br /&gt;
&lt;br /&gt;
Pointer and ReceiveList data buffers are backed by the &amp;quot;pointer buffer&amp;quot;, a buffer in the service process. Its size is a u16, which is retrieved using the &amp;quot;QueryPointerBufferSize&amp;quot; control message. If the client code determines all buffers with [[#BufferAttribute|BufferAttribute]] HipcPointer do not fit in the pointer buffer, it returns error 0x11A0B.&lt;br /&gt;
&lt;br /&gt;
For buffers with [[#BufferAttribute|BufferAttribute]] HipcAutoSelect, it creates two data buffers (Send+Pointer or Receive+ReceiveList), but one of them is NULL (zero size and pointer), while the other holds the expected values. Pointer/ReceiveList data buffers are used as the non-NULL buffer where possible, but if they don&#039;t fit in the pointer buffer, Send/Receive descriptors are used instead. The code defers processing of HipcAutoSelect buffers with sizes that fit in a u16 (and may therefore fit in the pointer buffer), which ensures all HipcPointer buffers get pointer-buffer space before any HipcAutoSelect. The order in which the deferred HipcAutoSelect buffers are processed is determined by a convoluted loop.&lt;/div&gt;</summary>
		<author><name>Yannik</name></author>
	</entry>
</feed>