D647 - The name a faulting register points at
Status: measured Date: 2026-09-09
What the report said, and what it meant
PPSA21564 faults reading 0x38 - a null plus a field
offset - and its report printed:
r12 -> data f7uOxY9mM1U#r#n+0x0 = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
r13 -> data f7uOxY9mM1U#r#n+0x0 = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
D639 got the region named, which was the hard half. The label is the module's own spelling of the symbol - a vendor module encodes every import as base64 of the hash plus a library and module id - and a reader cannot tell from it whether the guest is holding a C++ vtable, a stdio object or the stack canary. Those three want completely different answers, and the run report's whole job is to be the thing that answers rather than the thing that has to be decoded by hand.
It is libkernel::__stack_chk_guard. Nothing about the
printed label says so.
Two faults, and the second only appeared once the first was fixed
The database was the wrong one. The service resolves
a hash through self.symbols, and a worker process
builds its Service with none - the symbol database
is loaded per run, because names belong to the run, which is exactly
what import_labels_with already documents. Reaching for the
service's copy compiled, ran, and renamed nothing. A lookup that cannot
fail always fails that way, and this is the second time in two days (the
first was Knowledge::library_of answering about documented
functions when the question was about declared ones).
The by-name map is lossy, and resolving the names is what
made it lossy. DataBlocks kept one
BTreeMap<String, u64> serving two questions:
where does optarg live (an implementation writing
a guest global) and what lives at this address (a fault
reporter). Encoded spellings carry a per-module suffix, so three modules
importing __stack_chk_guard produced three distinct keys
and the map happened to hold all three. Resolving them to one name
collapsed the three to one entry - and the run then printed a
bare number, worse than the encoded label it
replaced.
That regression was visible in one run, which is the only reason it was caught rather than shipped as an improvement.
One map per question
DataBlocks now keeps
labels: BTreeMap<u64, String> beside
named: BTreeMap<String, u64>, and
data_symbol_at asks the address-keyed one. A repeated name
labels every page it was given; a name lookup still answers the one
address it can. Neither map is asked a question it cannot answer.
r12 -> data __stack_chk_guard+0x0 = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
What this does not claim
The canary being zero is not the cause of this
fault. A zero canary compares equal to itself and protects nothing,
which is a real gap and is worth its own entry; the fault is a read
through a null base at +0x38, and the two registers merely
hold the guard's address, which is ordinary stack-protector
code. Naming the region says what the guest was holding. It does not say
what killed it, and the report does not claim it does.
The guard was made to fail
The test asserts on the second page of a repeated
name, because the first was already working - and it asserts that
named() really does hold one entry for two pages, so the
reason the second map exists is stated in the test rather than in a
comment nobody reads next to it.