What is wrong
The SPL amount screen is built from token metadata the host supplies in the same message:
// lib/firmware/fsm_msg_solana.h @ 1af2ffe7de
108: /* Try to find token info from host-provided metadata */
110: if (pi->has_mint) { ti = solana_findTokenInfo(msg, pi->mint); }
114: if (ti && ti->has_symbol && ti->has_decimals) {
116: solana_formatTokenAmount(amount_str, sizeof(amount_str), pi->amount,
117: ti->symbol, (uint8_t)ti->decimals);
solana_findTokenInfo iterates msg->token_info[i] — host data only (lib/firmware/solana.c:645-655).
SolanaSignTx.token_info is max_count:4 in messages-solana.options. The authoritative decimals
carried in the signed TransferChecked instruction is parsed into pi->extra_u8
(solana.c:250) but is only ever used for a vote-commission screen (fsm_msg_solana.h:229); it is never
compared against ti->decimals.
Why it matters
The signed instruction fixes the amount in base units. The screen divides that by a host-chosen exponent
and labels it with a host-chosen symbol. A host declaring decimals: 9 for a 6-decimal token renders a
transfer of 1000 USDC as "1 USDC". The user approves the number they were shown; the chain executes the
number that was signed.
Both fields are unauthenticated, not just the exponent
decimals and symbol both come from msg->token_info[], which is host data. The signed
TransferChecked instruction carries its own authoritative decimals — parsed into pi->extra_u8
(lib/firmware/solana.c:250) and never compared against ti->decimals — but it carries no symbol at
all. There is nothing on-device to check the ticker against.
So the screen can misstate the magnitude (wrong exponent) and the asset (wrong ticker) independently. A
host declaring decimals: 9 for a 6-decimal token renders a 1000-unit transfer as "1"; a host supplying
an arbitrary symbol labels any mint as any asset. Correctness of the amount screen rests entirely on
metadata the attacker supplies in the same message.
Where it stands
Live on the release line. Fixed only on the orphaned commit 4895b8155 (9f625f2a9), which treats
ti->decimals as untrusted and enforces known->decimals != pi->extra_u8. Back-port for 7.14.2.
How to verify
git show 1af2ffe7de:lib/firmware/fsm_msg_solana.h | sed -n '103,127p'
git show 1af2ffe7de:lib/firmware/solana.c | sed -n '241,250p;645,655p'
git show 1af2ffe7de:include/keepkey/transport/messages-solana.options | grep token_info
Verified against 1af2ffe7de — the current head of develop on this fork and on keepkey/keepkey-firmware, CMakeLists.txt VERSION 7.14.1. Note the local clone's develop/origin/develop may point at an orphaned 7.15.0 commit; read with an explicit SHA.
What is wrong
The SPL amount screen is built from token metadata the host supplies in the same message:
solana_findTokenInfoiteratesmsg->token_info[i]— host data only (lib/firmware/solana.c:645-655).SolanaSignTx.token_infoismax_count:4inmessages-solana.options. The authoritativedecimalscarried in the signed
TransferCheckedinstruction is parsed intopi->extra_u8(
solana.c:250) but is only ever used for a vote-commission screen (fsm_msg_solana.h:229); it is nevercompared against
ti->decimals.Why it matters
The signed instruction fixes the amount in base units. The screen divides that by a host-chosen exponent
and labels it with a host-chosen symbol. A host declaring
decimals: 9for a 6-decimal token renders atransfer of 1000 USDC as "1 USDC". The user approves the number they were shown; the chain executes the
number that was signed.
Both fields are unauthenticated, not just the exponent
decimalsandsymbolboth come frommsg->token_info[], which is host data. The signedTransferCheckedinstruction carries its own authoritativedecimals— parsed intopi->extra_u8(
lib/firmware/solana.c:250) and never compared againstti->decimals— but it carries no symbol atall. There is nothing on-device to check the ticker against.
So the screen can misstate the magnitude (wrong exponent) and the asset (wrong ticker) independently. A
host declaring
decimals: 9for a 6-decimal token renders a 1000-unit transfer as "1"; a host supplyingan arbitrary
symbollabels any mint as any asset. Correctness of the amount screen rests entirely onmetadata the attacker supplies in the same message.
Where it stands
Live on the release line. Fixed only on the orphaned commit
4895b8155(9f625f2a9), which treatsti->decimalsas untrusted and enforcesknown->decimals != pi->extra_u8. Back-port for 7.14.2.How to verify
Verified against
1af2ffe7de— the current head ofdevelopon this fork and onkeepkey/keepkey-firmware,CMakeLists.txtVERSION 7.14.1. Note the local clone'sdevelop/origin/developmay point at an orphaned 7.15.0 commit; read with an explicit SHA.