From e6655e696c4e9fb27342b272dbc464e657f81b9e Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 11 Aug 2026 08:09:10 +0200 Subject: [PATCH] F-7989: clamp unaligned head size in ext_flash_decrypt_read() When a read starts in the middle of an encryption block, the head copy size was computed as ENCRYPT_BLOCK_SIZE - row_offset without regard for the requested length. A read shorter than the remainder of the block (e.g. 1 byte at offset 1) copied up to 15 decrypted bytes into a buffer sized for fewer, and left read_remaining negative, so the subsequent flash_read_size = read_remaining & ~(ENCRYPT_BLOCK_SIZE - 1) was passed to ext_flash_read() as a negative length. Clamp the head size to the bytes actually requested. Add a unit test covering short unaligned reads (offsets 1/4/8/15) that checks the return value, the decrypted contents and that no byte past the requested length is written; the ext_flash_read() mock now also rejects negative lengths. --- src/libwolfboot.c | 3 +++ tools/unit-tests/unit-extflash.c | 39 ++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/libwolfboot.c b/src/libwolfboot.c index 62fa63e5..63714a64 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -2702,6 +2702,9 @@ int RAMFUNCTION ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len */ if (row_offset != 0) { unaligned_head_size = ENCRYPT_BLOCK_SIZE - row_offset; + /* Never copy more than the caller asked for */ + if (unaligned_head_size > read_remaining) + unaligned_head_size = read_remaining; if (ext_flash_read(row_address, block, ENCRYPT_BLOCK_SIZE) != ENCRYPT_BLOCK_SIZE) { return -1; diff --git a/tools/unit-tests/unit-extflash.c b/tools/unit-tests/unit-extflash.c index c25d7b8f..c4bfe513 100644 --- a/tools/unit-tests/unit-extflash.c +++ b/tools/unit-tests/unit-extflash.c @@ -96,6 +96,9 @@ uint8_t flash[FLASH_SIZE]; int ext_flash_read(uintptr_t address, uint8_t *data, int len) { printf("Called ext_flash_read %p %p %d\n", (void *)address, (void *)data, len); + /* A negative length is never a valid request */ + ck_assert_int_ge(len, 0); + /* Check that the read address and size are within the bounds of the flash memory */ ck_assert_int_le(address + len, FLASH_SIZE); @@ -260,6 +263,37 @@ START_TEST(test_ext_enc_flash_operations) { } END_TEST +START_TEST(test_ext_enc_flash_short_unaligned_read) { + uint32_t address = 0x1000; + uint32_t size = 64; + uint8_t data[64]; + uint8_t dataw[64]; + /* Reads shorter than the remainder of the encryption block they start in: + * { offset within the block, number of bytes requested } */ + static const int cases[][2] = { {1, 1}, {4, 4}, {8, 3}, {15, 1} }; + unsigned int c; + int i, rres, wres; + + memcpy(dataw, test_buffer, size); + wres = ext_flash_check_write(address, dataw, size); + ck_assert_int_eq(wres, 0); + + for (c = 0; c < sizeof(cases) / sizeof(cases[0]); c++) { + int off = cases[c][0]; + int len = cases[c][1]; + + memset(data, 0xA5, sizeof(data)); + rres = ext_flash_check_read(address + off, data, len); + ck_assert_int_eq(rres, len); + ck_assert_mem_eq(data, test_buffer + off, len); + + /* No byte past the requested length may be written */ + for (i = len; i < (int)sizeof(data); i++) + ck_assert_uint_eq(data[i], 0xA5); + } +} +END_TEST + Suite *wolfboot_suite(void) @@ -271,15 +305,20 @@ Suite *wolfboot_suite(void) /* Test cases */ TCase *ext_flash_operations = tcase_create("External flash operations: API"); TCase *ext_enc_flash_operations = tcase_create("External encrypted flash operations"); + TCase *ext_enc_flash_short_read = tcase_create("External encrypted flash short unaligned read"); /* Set parameters + add to suite */ tcase_add_test(ext_flash_operations, test_ext_flash_operations); tcase_add_test(ext_enc_flash_operations, test_ext_enc_flash_operations); + tcase_add_test(ext_enc_flash_short_read, + test_ext_enc_flash_short_unaligned_read); tcase_set_timeout(ext_flash_operations, 20); tcase_set_timeout(ext_enc_flash_operations, 20); + tcase_set_timeout(ext_enc_flash_short_read, 20); suite_add_tcase(s, ext_flash_operations); suite_add_tcase(s, ext_enc_flash_operations); + suite_add_tcase(s, ext_enc_flash_short_read); return s; }