D346 - A console has users, and one of their names reaches a guest unencoded

decided · 2026-08-27 · the settings argument, finally with a caller attached

libSceUserService is sixteen imports across the corpus, and reading them is what decided the shape. A title asks for a name by identifier, enumerates who is signed in, and keys save data on which user - none of which a single user_name field can answer. So Settings holds a list.

sceUserServiceGetUserName is the one call in this crate that answers a console setting unencoded. Everything else here hands back a number whose meaning is a measurement: an age band, an accessibility flag, a parameter identifier. A name is a string. The owner types it in the shell, the guest reads it, and nothing in between has to be guessed. That is the argument this project made for holding settings at all, with a real caller at the end of it.

Everything else in the library is declared and not implemented, for the reason the empty parameter table exists: a person's answer to "what age level" is not the integer a title reads, and inventing the encoding would be principle 3's forbidden case with a consumer.

The size is checked rather than trusted

That size is the third argument is an assumption, and a wrong argument position writes past a caller's buffer - the failure sceUserServiceGetInitialUser already carries a warning about (D210, D272). So it is believed only within sixty-four bytes; anything outside that is treated as a value that is not a size, and the call is refused. A refusal is recoverable and a smashed stack is not, and the test asserts the refusal rather than the success.

Truncation happens on a character boundary, never a byte one: half a multi-byte character is a string that is not text.

Three things that were modelled and inert

console::configure was never called. Written that morning, so every setting a person chose stopped at the window and the guest read defaults. The worker now reads shell.toml itself - it is a separate process that already reads its own configuration, and a setting cannot arrive half-applied because a message was dropped.

sceUserServiceGetInitialUser answered a constant. It now answers whoever is signed in, and answers the placeholder when nobody is - which is the honest answer to "who is signed in" on a machine where the signed-in account was deleted.

A test caught the identifier reuse before the code did. next_id derived the next number from the highest live identifier, which reuses one the moment its holder is deleted - and save data is keyed on it, so a new person would silently inherit somebody else's saves. It is a stored high-water mark now, the same shape as HandleAllocator. The test was written first and failed, which is the only reason it was found.