Skip to content

Solana SPL transfer amounts are formatted with host-supplied decimals and symbol #406

Description

@BitHighlander

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    securitySecurity-relevant defect or hardening

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions