From 9a3b78b472b2ed2c0d1095836364ae474071747a Mon Sep 17 00:00:00 2001 From: Jack Date: Fri, 7 Aug 2026 18:18:13 +0200 Subject: [PATCH] Validate legacy settings before flash writes --- .../stm32/boards/Passport/modules/settings.py | 16 ++++-------- .../Passport/modules/tests/test_unit.py | 4 +++ .../Passport/modules/tests/unit/settings.py | 25 +++++++++++++++++++ 3 files changed, 34 insertions(+), 11 deletions(-) create mode 100644 ports/stm32/boards/Passport/modules/tests/unit/settings.py diff --git a/ports/stm32/boards/Passport/modules/settings.py b/ports/stm32/boards/Passport/modules/settings.py index 28c7f6789..c1fc15c57 100644 --- a/ports/stm32/boards/Passport/modules/settings.py +++ b/ports/stm32/boards/Passport/modules/settings.py @@ -316,6 +316,11 @@ def save(self): # Render as JSON, encrypt and write it self.curr_dict['_revision'] = self.curr_dict.get('_revision', 0) + 1 + # Validate the encoded size before selecting or erasing a flash slot. + json_buf = ujson.dumps(self.curr_dict).encode('utf8') + if len(json_buf) > DATA_SIZE: + raise ValueError('JSON data is larger than {} bytes.'.format(DATA_SIZE)) + addr = self.next_addr() # print('===============================================================') @@ -328,17 +333,6 @@ def save(self): chk = trezorcrypto.sha256() - # Create the JSON string as bytes - json_buf = ujson.dumps(self.curr_dict).encode('utf8') - - # Ensure data is not too big - if len(json_buf) > DATA_SIZE: - # print('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') - # print(' JSON TOO BIG!') - # print('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') - assert false, 'JSON data is larger than {}.'.format(DATA_SIZE) - return - # Create a zero-filled byte buf padded_buf = bytearray(DATA_SIZE) diff --git a/ports/stm32/boards/Passport/modules/tests/test_unit.py b/ports/stm32/boards/Passport/modules/tests/test_unit.py index a088f75fb..670a94a72 100644 --- a/ports/stm32/boards/Passport/modules/tests/test_unit.py +++ b/ports/stm32/boards/Passport/modules/tests/test_unit.py @@ -20,6 +20,10 @@ def test_ext_settings(test): assert test('ext_settings.py') == b'OK' +def test_settings(test): + assert test('settings.py') == b'OK' + + def test_ui(test): assert test('ui.py') == b'OK' diff --git a/ports/stm32/boards/Passport/modules/tests/unit/settings.py b/ports/stm32/boards/Passport/modules/tests/unit/settings.py new file mode 100644 index 000000000..0c53f538c --- /dev/null +++ b/ports/stm32/boards/Passport/modules/tests/unit/settings.py @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2026 Foundation Devices, Inc. +# +# SPDX-License-Identifier: GPL-3.0-or-later + +from settings import DATA_SIZE, Settings + + +class OversizedSettings: + def __init__(self): + self.curr_dict = {'value': 'x' * DATA_SIZE} + + def next_addr(self): + raise RuntimeError('Oversized settings reached flash slot selection') + + +settings = OversizedSettings() + +try: + Settings.save(settings) +except ValueError as exc: + assert str(DATA_SIZE) in str(exc) +else: + raise RuntimeError('Oversized settings should fail before selecting a flash slot') + +return_value.write(b'OK')