Summary
Two more function-local/module statics hold derived key material that no teardown path clears. Same class as the signing.c privkey and fsm.c derived-node leaks fixed in #393, found by the same sweep but left unfixed.
1. lib/firmware/transaction.c:272
if (in->address_n_count > 0) {
static CONFIDENTIAL HDNode node;
Populated via memcpy(&node, root, sizeof(HDNode)) inside compile_output(). No memzero anywhere in the file. Being a function-local static, it is unreachable from fsm_abortAllSigningFlows() without either hoisting it to file scope or adding an abort hook — the same treatment fsm_getDerivedNode()'s node received.
2. lib/firmware/eip712.c:423-424
dsname / dsversion / dschainId / dsverifyingContract are cleared only inside dsConfirm() (eip712.c:509-512, which correctly clears on both approve and cancel). Every early return from parseVals() — including user cancellation and every parse error — leaves them populated.
These are not secrets, so the impact is display integrity rather than key disclosure: stale domain name / version / chainId / verifying-contract values can persist into a subsequent EIP-712 confirmation. Given this is the clear-signing path, showing the user a domain from a previous, abandoned transaction is a real integrity concern.
Acceptance criteria
Summary
Two more function-local/module statics hold derived key material that no teardown path clears. Same class as the
signing.cprivkeyandfsm.cderived-node leaks fixed in #393, found by the same sweep but left unfixed.1.
lib/firmware/transaction.c:272Populated via
memcpy(&node, root, sizeof(HDNode))insidecompile_output(). Nomemzeroanywhere in the file. Being a function-local static, it is unreachable fromfsm_abortAllSigningFlows()without either hoisting it to file scope or adding an abort hook — the same treatmentfsm_getDerivedNode()'s node received.2.
lib/firmware/eip712.c:423-424dsname/dsversion/dschainId/dsverifyingContractare cleared only insidedsConfirm()(eip712.c:509-512, which correctly clears on both approve and cancel). Every early return fromparseVals()— including user cancellation and every parse error — leaves them populated.These are not secrets, so the impact is display integrity rather than key disclosure: stale domain name / version / chainId / verifying-contract values can persist into a subsequent EIP-712 confirmation. Given this is the clear-signing path, showing the user a domain from a previous, abandoned transaction is a real integrity concern.
Acceptance criteria
transaction.cnode zeroed on every exit and reachable from the central teardowneip712.cdomain statics cleared on every exit fromparseVals(), not only viadsConfirm()