fix(solana): fix signer account parsing.
What changed, and why it matters
This update fixes a bug in how Trezor hardware wallets classify accounts inside Solana transactions. The bug could mislabel read-only signer accounts as writable, or writable accounts as read-only. Because Trezor uses these labels to decide what the user must approve on screen, a crafted transaction could trick the wallet into showing a harmless-looking account while actually requiring it to sign or authorizing unexpected changes. The changelog files explicitly mark this as a security fix.
Treat this as a security-relevant firmware bug. Review whether the previous parsing logic could be exploited to misrepresent signer/writable accounts in crafted Solana transactions, and consider whether a security advisory or CVE is warranted. Users should update firmware once a release containing this patch is available.
Security signals we found
Changelog entries explicitly reclassified as security fixes
Address-type parsing logic changed to correctly model read-only signers as subset of signers
Potential for UI/account-type mismatch in transaction confirmation flow
Solana transaction parsing bug affecting account permission classification
Evidence from the diff
The patch changes Solana address-type parsing in core/src/apps/solana/transaction/init.py. Previously the parser treated required_signers_count and num_signature_read_only_addresses as separate, sequential ranges, which is incorrect: read-only signers are a subset of the total signer count, not an additional range. The new logic subtracts num_signature_read_only_addresses from required_signers_count to identify writable signers, then uses required_signers_count for read-only signers, and derives writable non-signers from num_of_addresses - num_read_only_addresses. Two new changelog entries (248.security and 249.security) label the fix as security-relevant, and the original non-security changelog entry is removed.
Changed components
core/src/apps/solana/transaction/__init__.pyTrezor Solana transaction parserTrezor firmware account-type / signer classification logicInspect captured patch +8 / −14
diff --git a/core/.changelog.d/248.fixed b/core/.changelog.d/248.fixed
deleted file mode 100644
index 0505012d..00000000
--- a/core/.changelog.d/248.fixed
+++ /dev/null
@@ -1 +0,0 @@
-Fixed solana ALT recipient and account type parsing.
diff --git a/core/.changelog.d/248.security b/core/.changelog.d/248.security
new file mode 100644
index 00000000..81324163
--- /dev/null
+++ b/core/.changelog.d/248.security
@@ -0,0 +1 @@
+Fixed Solana ALT recipient account parsing.
diff --git a/core/.changelog.d/249.security b/core/.changelog.d/249.security
new file mode 100644
index 00000000..ac5a2292
--- /dev/null
+++ b/core/.changelog.d/249.security
@@ -0,0 +1 @@
+Fixed bug in Solana account type identification.
diff --git a/core/src/apps/solana/transaction/__init__.py b/core/src/apps/solana/transaction/__init__.py
index b666bb79..419d522e 100644
--- a/core/src/apps/solana/transaction/__init__.py
+++ b/core/src/apps/solana/transaction/__init__.py
@@ -89,27 +89,20 @@ class Transaction:
def _parse_addresses(self, serialized_tx_reader: BufferReader) -> None:
num_of_addresses = parse_var_int(serialized_tx_reader)
+ # Read-only signers are always just a subset of all signers.
+ assert self.num_signature_read_only_addresses <= self.required_signers_count
assert (
num_of_addresses
- >= self.required_signers_count
- + self.num_signature_read_only_addresses
- + self.num_read_only_addresses
+ >= self.required_signers_count + self.num_read_only_addresses
)
addresses: list[Address] = []
for i in range(num_of_addresses):
- if i < self.required_signers_count:
+ if i < self.required_signers_count - self.num_signature_read_only_addresses:
type = AddressType.AddressSig
- elif (
- i < self.required_signers_count + self.num_signature_read_only_addresses
- ):
+ elif i < self.required_signers_count:
type = AddressType.AddressSigReadOnly
- elif (
- i
- < self.required_signers_count
- + self.num_signature_read_only_addresses
- + self.num_read_only_addresses
- ):
+ elif i < num_of_addresses - self.num_read_only_addresses:
type = AddressType.AddressRw
else:
type = AddressType.AddressReadOnly
Why this scored 70/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.