diff --git a/CHANGELOG.md b/CHANGELOG.md index 1747775c..aad359a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.3] - 2023-10-26 + +### Fixed +- Launcher: Fix missing RAM relocation emulation on NanoX, NanoSP and Stax + +## [0.3.2] - 2023-09-28 + +### Fixed +- API: the API thread is asked to stop when Speculos exits + +## [0.3.1] - 2023-09-28 + +### Fixed +- OCR: Prevent null dereference when expected font is not in ELF file ## [0.3.0] - 2023-09-11 diff --git a/README.md b/README.md index b4a3e812..e2fe2bd5 100644 --- a/README.md +++ b/README.md @@ -33,17 +33,49 @@ being merged to `master`: ## Limitations -The emulator handles only a few syscalls made by common apps; for instance, +There is absolutely no guarantee that apps will have the same behavior on +hardware devices and Speculos, though the differences are limited. + +### Syscalls + +The emulator handles only a few syscalls made by common apps. For instance, syscalls related to app install, firmware update or OS info can't be implemented. -There is absolutely no guarantee that apps will have the same behavior on -hardware devices and Speculos: +Invalid syscall parameters might throw an exception on a real device while +being ignored on Speculos. +Notably, this is the case for application allowed derivation path and curve and +application settings flags which are enforced by the device OS, but ignored by +Speculos. + +### Memory alignment + +Attempts to perform unaligned accesses when not allowed (eg. dereferencing a +misaligned pointer) will cause an alignment fault on a Ledger Nano S device but +not on Speculos. Note that such unaligned accesses are supported by other +Ledger devices. + +Following code crashes on LNS device, but not on Speculos nor on other devices. +``` +uint8_t buffer[20]; +for (int i = 0; i < 20; i++) { + buffer[i] = i; +} +uint32_t display_value = *((uint32_t*) (buffer + 1)); +PRINTF("display_value: %d\n", display_value); +``` + +### Watchdog + +NanoX and Stax devices use an internal watchdog enforcing usage of regular +calls to `io_seproxyhal_io_heartbeat();`. This watchdog is not emulated on +Speculos. + +### Pending review screen -- Invalid syscall parameters might throw an exception on a real device while - being ignored on Speculos. -- Attempts to perform unaligned accesses when not allowed (eg. dereferencing a - misaligned pointer) will cause an alignment fault on a hardware device. +The C SDK offers a feature to display a warning screen upon app launch +indicating that the app has not been reviewed. +As the `_install_parameters` are not handled by Speculos, the screen won't be displayed. ## Security diff --git a/speculos/api/api.py b/speculos/api/api.py index 5e455ae0..7192d148 100644 --- a/speculos/api/api.py +++ b/speculos/api/api.py @@ -30,6 +30,7 @@ def __init__(self, api_port: int) -> None: self._notify_exit: socket.socket self.sock, self._notify_exit = socket.socketpair() self.api_port: int = api_port + self._api_thread: threading.Thread @property def file(self): @@ -55,8 +56,11 @@ def start_server_thread(self, automation_server: BroadcastInterface) -> None: wrapper = ApiWrapper(screen_, seph_, automation_server) self._app = wrapper.app - api_thread = threading.Thread(target=self._run, name="API-server", daemon=True) - api_thread.start() + self._api_thread = threading.Thread(target=self._run, name="API-server", daemon=True) + self._api_thread.start() + + def stop(self): + self._api_thread.join(10) class ApiWrapper: diff --git a/speculos/main.py b/speculos/main.py index 43fc8831..0b80b42c 100644 --- a/speculos/main.py +++ b/speculos/main.py @@ -115,6 +115,8 @@ def get_elf_infos(app_path): fonts_addr = bagl_fonts_symbol[0]['st_value'] fonts_size = bagl_fonts_symbol[0]['st_size'] logger.info(f"Found C_bagl_fonts at 0x{fonts_addr:X} ({fonts_size} bytes)\n") + else: + logger.info("Disabling OCR.") supp_ram = elf.get_section_by_name('.rfbss') ram_addr, ram_size = (supp_ram['sh_addr'], supp_ram['sh_size']) if supp_ram is not None else (0, 0) @@ -518,6 +520,9 @@ def main(prog=None) -> int: screen_notifier.run() + if apirun is not None: + apirun.stop() + s2.close() _, status = os.waitpid(qemu_pid, 0) qemu_exit_status = os.WEXITSTATUS(status) diff --git a/src/bolos/cx_utils.h b/src/bolos/cx_utils.h index 05e43e05..c8e3b629 100644 --- a/src/bolos/cx_utils.h +++ b/src/bolos/cx_utils.h @@ -10,7 +10,7 @@ #ifndef NATIVE_64BITS // NO 64BITS /** 64bits types, native or by-hands, depending on target and/or compiler * support. - * This type is defined here only because sha-3 struct used it INTENALLY. + * This type is defined here only because sha-3 struct used it INTERNALLY. * It should never be directly used by other modules. */ struct uint64_s { diff --git a/src/bolos/fonts_info.c b/src/bolos/fonts_info.c index b8900863..84f94757 100644 --- a/src/bolos/fonts_info.c +++ b/src/bolos/fonts_info.c @@ -197,6 +197,11 @@ void parse_fonts(void *code, unsigned long text_load_addr, nb_fonts = fonts_size / 4; } + // There is no font or we don't know its format + if (!nb_fonts) { + return; + } + // Checks that fonts & nb_fonts are coherent if (fonts[nb_fonts] != nb_fonts) { fprintf(stdout, "ERROR: Expecting nb_fonts=%u and found %u instead!\n", diff --git a/src/launcher.c b/src/launcher.c index 5a328ea4..8e114d7b 100644 --- a/src/launcher.c +++ b/src/launcher.c @@ -17,6 +17,8 @@ #include "svc.h" #define LOAD_ADDR ((void *)0x40000000) +#define LINK_RAM_ADDR (0xda7a0000) +#define LOAD_RAM_ADDR (0x50000000) #define MAX_APP 16 #define MAIN_APP_NAME "main" @@ -299,6 +301,11 @@ static void *load_app(char *name) data_addr = get_lower_page_aligned_addr(app->elf.stack_addr); data_size = get_upper_page_aligned_size( app->elf.stack_size + app->elf.stack_addr - (unsigned long)data_addr); + if (app->elf.stack_addr == LINK_RAM_ADDR) { + // Emulate RAM relocation + data_addr = (void *)LOAD_RAM_ADDR; + data_size = get_upper_page_aligned_size(app->elf.stack_size); + } /* load code * map an extra page in case the _install_params are mapped in the beginning @@ -547,7 +554,11 @@ static int run_app(char *name, unsigned long *parameters) /* thumb mode */ f = (void *)((unsigned long)p | 1); stack_end = app->elf.stack_addr; - stack_start = app->elf.stack_addr + app->elf.stack_size; + if (app->elf.stack_addr == LINK_RAM_ADDR) { + // Emulate RAM relocation + stack_end = LOAD_RAM_ADDR; + } + stack_start = stack_end + app->elf.stack_size; asm volatile("mov r0, %2\n" "mov r9, %1\n"