You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When any decoder claims a transaction, lib/firmware/ethereum.c:720-728 clears bothneeds_confirm and data_needs_confirm, and ethereum.c:693 admits data_length up to 16,000,000 streamed into the hash. So the tail is signed and never shown.
Why the exemption does not hold
The in-code justification is that the outcome is "bounded by the input amount and minimum output amount shown on screen". That is an off-device invariant — it is the 0x ExchangeProxy's own min-output check, not anything this firmware verifies.
It also rests entirely on msg->to matching ZXSWAP_ADDRESS on an allowlisted chain (ethereum_contracts.c:38-45), and #441 records that allowlist as unverified against 0x's published deployments. So the firmware is trusting a contract's behaviour, at an address it has not confirmed is that contract, on chains it has not confirmed the contract exists on.
Reachable today
A host that sets to = 0xdef1c0ded9bec7f1a1670819833240f027b25eff, chain_id in {1, 56, 137, 8453, 42161, 43114}, selector 0x415565b0, and data_length well beyond data_initial_chunk.size gets #413's exact stated harm: a recognised, benign-looking call carrying arbitrary appended calldata that is hashed and signed after the user has approved.
Why this is filed separately
#413's commit (d674adf30) carries a Closes #413 trailer and states the residual in its own body. Without this issue, merging would auto-close #413 and this defect would leave the tracker entirely.
#400 does not cover it. That issue is about the generic layoutEthereumData path in ethereum.c — the screen a user reaches when no decoder claims the transaction. This is a decoder that does claim it and shows no raw data at all.
Options
Remove the exemption. transformERC20 falls to the generic disclosure when it does not fit one chunk — consistent with every other decoder, at the cost of a raw-hex screen for large 0x swaps.
Keep clear-signing but bind the tail: hash the streamed remainder and display a commitment the user can compare, or assert the decoded transformations[] covers exactly data_total bytes.
Option 1 is the smallest and fails closed. Option 3 is not viable on its own while #441 is open.
How to verify
git show release/7.14.2:lib/firmware/ethereum_contracts.c | sed -n '52,70p'
git show release/7.14.2:lib/firmware/ethereum_contracts/zxtransERC20.c | sed -n '55,60p'
git show release/7.14.2:lib/firmware/ethereum.c | sed -n '690,730p'
What is wrong
#413 added a central binding so a contract decoder cannot claim a transaction whose calldata exceeds the initial chunk:
transformERC20is hoisted above it and exempt:and the decoder still discards the total:
When any decoder claims a transaction,
lib/firmware/ethereum.c:720-728clears bothneeds_confirmanddata_needs_confirm, andethereum.c:693admitsdata_lengthup to 16,000,000 streamed into the hash. So the tail is signed and never shown.Why the exemption does not hold
The in-code justification is that the outcome is "bounded by the input amount and minimum output amount shown on screen". That is an off-device invariant — it is the 0x ExchangeProxy's own min-output check, not anything this firmware verifies.
It also rests entirely on
msg->tomatchingZXSWAP_ADDRESSon an allowlisted chain (ethereum_contracts.c:38-45), and #441 records that allowlist as unverified against 0x's published deployments. So the firmware is trusting a contract's behaviour, at an address it has not confirmed is that contract, on chains it has not confirmed the contract exists on.Reachable today
A host that sets
to = 0xdef1c0ded9bec7f1a1670819833240f027b25eff, chain_id in {1, 56, 137, 8453, 42161, 43114}, selector0x415565b0, anddata_lengthwell beyonddata_initial_chunk.sizegets #413's exact stated harm: a recognised, benign-looking call carrying arbitrary appended calldata that is hashed and signed after the user has approved.Why this is filed separately
#413's commit (
d674adf30) carries aCloses #413trailer and states the residual in its own body. Without this issue, merging would auto-close #413 and this defect would leave the tracker entirely.#400 does not cover it. That issue is about the generic
layoutEthereumDatapath inethereum.c— the screen a user reaches when no decoder claims the transaction. This is a decoder that does claim it and shows no raw data at all.Options
transformERC20falls to the generic disclosure when it does not fit one chunk — consistent with every other decoder, at the cost of a raw-hex screen for large 0x swaps.transformations[]covers exactlydata_totalbytes.Option 1 is the smallest and fails closed. Option 3 is not viable on its own while #441 is open.
How to verify