Decreasing stack consumption: Nano X test adaptation
What changed, and why it matters
This commit only changes test helper code so that automated tests for the Ledger Bitcoin app use a smaller number of outputs on the Nano X device. It does not change the actual app firmware. The change is a test-suite adaptation to match a previously reduced Nano X limit, not a fix for a new security bug.
No immediate action required for end users. Developers should ensure the corresponding firmware-side limit reduction for Nano X is already in place and that tests now exercise the actual production limit. If the firmware limit was reduced to prevent stack overflow, verify the reduced limit is sufficient under worst-case input sizes.
Security signals we found
Memory/stack-consumption concern mentioned in commit message (Nano X 8 kB stack limit)
Reduced output-handling limit for Nano X
Only test/instruction Python files changed; no firmware code patched
Evidence from the diff
The diff updates Python test/instruction helpers in app-bitcoin-new to introduce a device-specific constant MAX_EXT_OUTPUT_SIMPLIFIED_NUMBER_NANOX = 8 (versus 16 for other models). It replaces direct uses of MAX_EXT_OUTPUT_SIMPLIFIED_NUMBER with get_max_ext_output_simplified_number(model) and renames tests from 4to17/4to18 to 4toMax/4toMaxPlus1. The commit message says this is to reduce stack consumption on Nano X, whose stack is limited to 8 kB. No C/firmware code is modified.
Changed components
ragger_bitcoin/ragger_instructions.pytests/instructions.pytests/test_sign_psbt.pyInspect captured patch +21 / −10
diff --git a/ragger_bitcoin/ragger_instructions.py b/ragger_bitcoin/ragger_instructions.py
index a07571d..d1e15a3 100644
--- a/ragger_bitcoin/ragger_instructions.py
+++ b/ragger_bitcoin/ragger_instructions.py
@@ -1,6 +1,17 @@
from ragger.navigator import NavInsID
MAX_EXT_OUTPUT_SIMPLIFIED_NUMBER = 16
+# On Nano X we have reduced the max. number of handled outputs
+# to reduce the stack consumption limited to 8k
+MAX_EXT_OUTPUT_SIMPLIFIED_NUMBER_NANOX = 8
+
+def get_max_ext_output_simplified_number(model):
+ """Returns the device-specific MAX_EXT_OUTPUT_SIMPLIFIED_NUMBER.
+ Nano X has a lower limit (MAX_EXT_OUTPUT_SIMPLIFIED_NUMBER_NANOX)
+ due to memory constraints."""
+ if hasattr(model, 'name') and model.name == "nanox":
+ return MAX_EXT_OUTPUT_SIMPLIFIED_NUMBER_NANOX
+ return MAX_EXT_OUTPUT_SIMPLIFIED_NUMBER
class Instructions:
def __init__(self, model):
@@ -49,7 +60,7 @@ class Instructions:
for output_index in range(0, output_count):
# the initial N_CACHED_EXTERNAL_OUTPUTS outputs are cached, so it is the same request
- if output_index < MAX_EXT_OUTPUT_SIMPLIFIED_NUMBER:
+ if output_index < get_max_ext_output_simplified_number(self.model):
self.same_request("Amount", NavInsID.USE_CASE_REVIEW_TAP, NavInsID.USE_CASE_REVIEW_TAP,
save_screenshot=save_screenshot)
else:
diff --git a/tests/instructions.py b/tests/instructions.py
index 6ddfc8c..49237df 100644
--- a/tests/instructions.py
+++ b/tests/instructions.py
@@ -4,7 +4,7 @@ from ragger.navigator import NavInsID, NavIns
from ragger.firmware import Firmware
from ragger.firmware.touch.positions import STAX_X_CENTER, FLEX_X_CENTER, APEX_P_X_CENTER
-from ragger_bitcoin.ragger_instructions import Instructions, MAX_EXT_OUTPUT_SIMPLIFIED_NUMBER
+from ragger_bitcoin.ragger_instructions import Instructions, get_max_ext_output_simplified_number
def message_instruction_approve(model: Firmware, save_screenshot=True) -> Instructions:
@@ -286,7 +286,7 @@ def sign_psbt_instruction_approve_selftransfer(model: Firmware) -> Instructions:
def sign_psbt_instruction_approve_generic(model: Firmware, output_count: int, save_screenshot: bool = True, go_back: bool = False) -> Instructions:
instructions = Instructions(model)
- if (output_count <= MAX_EXT_OUTPUT_SIMPLIFIED_NUMBER):
+ if (output_count <= get_max_ext_output_simplified_number(model)):
# Classical case
return sign_psbt_instruction_approve(model, save_screenshot, has_feewarning = True, go_back = go_back);
diff --git a/tests/test_sign_psbt.py b/tests/test_sign_psbt.py
index 838b50e..d887b5a 100644
--- a/tests/test_sign_psbt.py
+++ b/tests/test_sign_psbt.py
@@ -18,7 +18,7 @@ from ragger.firmware import Firmware
from test_utils import bip0340, txmaker, SpeculosGlobals
from ragger_bitcoin import RaggerClient
-from ragger_bitcoin.ragger_instructions import MAX_EXT_OUTPUT_SIMPLIFIED_NUMBER
+from ragger_bitcoin.ragger_instructions import get_max_ext_output_simplified_number
from .instructions import *
tests_root: Path = Path(__file__).parent
@@ -600,17 +600,17 @@ def singlesig_wpkh_4toN(navigator: Navigator, firmware: Firmware, client: Ragger
assert len(result) == n_ins
-def test_sign_psbt_singlesig_wpkh_4to17(navigator: Navigator, firmware: Firmware, client:
+def test_sign_psbt_singlesig_wpkh_4toMax(navigator: Navigator, firmware: Firmware, client:
RaggerClient, test_name: str):
- singlesig_wpkh_4toN(navigator, firmware, client, test_name, MAX_EXT_OUTPUT_SIMPLIFIED_NUMBER + 1)
+ singlesig_wpkh_4toN(navigator, firmware, client, test_name, get_max_ext_output_simplified_number(firmware) + 1)
-def test_sign_psbt_singlesig_wpkh_4to17_go_back(navigator: Navigator, firmware: Firmware, client:
+def test_sign_psbt_singlesig_wpkh_4toMax_go_back(navigator: Navigator, firmware: Firmware, client:
RaggerClient, test_name: str):
- singlesig_wpkh_4toN(navigator, firmware, client, test_name, MAX_EXT_OUTPUT_SIMPLIFIED_NUMBER + 1, True)
+ singlesig_wpkh_4toN(navigator, firmware, client, test_name, get_max_ext_output_simplified_number(firmware) + 1, True)
-def test_sign_psbt_singlesig_wpkh_4to18(navigator: Navigator, firmware: Firmware, client:
+def test_sign_psbt_singlesig_wpkh_4toMaxPlus1(navigator: Navigator, firmware: Firmware, client:
RaggerClient, test_name: str):
- singlesig_wpkh_4toN(navigator, firmware, client, test_name, MAX_EXT_OUTPUT_SIMPLIFIED_NUMBER + 2)
+ singlesig_wpkh_4toN(navigator, firmware, client, test_name, get_max_ext_output_simplified_number(firmware) + 2)
def test_sign_psbt_singlesig_large_amount(navigator: Navigator, firmware: Firmware, client:
Why this scored 23/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.