nrf52/nrf5340/stm32l0: fix the 32-bit fast path in hal_flash_write

F-6757 fixed the byte-wise path but left the fast path above it indexing
dst[i >> 2]/src[i >> 2] off the call-time base. The guard only proves that
"address + i" and "data + i" are word aligned, so when the destination and
source share a non-zero misalignment the byte path advances i to the next
word boundary and the fast path then copies the wrong word, through an
unaligned 32-bit access that faults on the Cortex-M0+ of stm32l0.

Index both pointers by i directly, and cover the case the existing tests
deliberately avoided.
pull/851/head
Daniele Lacamera 2026-08-12 13:20:56 +02:00
parent cfaf145649
commit a69b3384d7
4 changed files with 50 additions and 9 deletions

View File

@ -73,11 +73,15 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
while (i < len) {
if ((len - i > 3) && ((((address + i) & 0x03) == 0) && ((((uint32_t)data) + i) & 0x03) == 0)) {
src = (uint32_t *)data;
dst = (uint32_t *)address;
/* Index by "i" directly: the condition above only guarantees
* that "address + i" and "data + i" are word aligned, so
* dst[i >> 2] off the unaligned base would address the wrong
* word (and fault on a strict-alignment core). */
src = (uint32_t *)(data + i);
dst = (uint32_t *)(address + i);
NVMC_CONFIG = NVMC_CONFIG_WEN;
flash_wait_complete();
dst[i >> 2] = src[i >> 2];
*dst = *src;
flash_wait_complete();
i+=4;
} else {

View File

@ -317,14 +317,18 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
while (i < len) {
if ((len - i > 3) && ((((address + i) & 0x03) == 0) &&
((((uint32_t)data) + i) & 0x03) == 0)) {
src = (uint32_t *)data;
dst = (uint32_t *)address;
/* Index by "i" directly: the condition above only guarantees
* that "address + i" and "data + i" are word aligned, so
* dst[i >> 2] off the unaligned base would address the wrong
* word (and fault on a strict-alignment core). */
src = (uint32_t *)(data + i);
dst = (uint32_t *)(address + i);
#if TZ_SECURE() || defined(TARGET_nrf5340_net)
NVMC_CONFIG = NVMC_CONFIG_WEN;
#endif
NVMC_CONFIGNS = NVMC_CONFIG_WEN;
while (NVMC_READY == 0);
dst[i >> 2] = src[i >> 2];
*dst = *src;
while (NVMC_READY == 0);
i+=4;
} else {

View File

@ -111,10 +111,14 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
while (i < len) {
if ((len - i > 3) && ((((address + i) & 0x03) == 0) && ((((uint32_t)data) + i) & 0x03) == 0)) {
src = (uint32_t *)data;
dst = (uint32_t *)(address + FLASHMEM_ADDRESS_SPACE);
/* Index by "i" directly: the condition above only guarantees
* that "address + i" and "data + i" are word aligned, so
* dst[i >> 2] off the unaligned base would address the wrong
* word, and the Cortex-M0+ faults on the unaligned access. */
src = (uint32_t *)(data + i);
dst = (uint32_t *)(address + i + FLASHMEM_ADDRESS_SPACE);
flash_wait_complete();
dst[i >> 2] = src[i >> 2];
*dst = *src;
flash_wait_complete();
i+=4;
} else {

View File

@ -140,6 +140,34 @@ START_TEST(test_unaligned_write_mismatched_alignment)
}
END_TEST
/* Destination and source share the same non-zero misalignment, so once the
* byte-wise path has advanced i to the next word boundary both fast-path
* conditions hold and the 32-bit branch is entered with i != 0. Before the
* fix that branch indexed dst[i >> 2]/src[i >> 2] off the unaligned bases,
* writing data[0..3] to "address..address+3" instead of data[3..6] to
* "address+3..address+6" -- through a misaligned 32-bit flash access. */
START_TEST(test_unaligned_write_matching_alignment_fast_path)
{
uint8_t rawbuf[64];
uint8_t *data = rawbuf;
uint32_t base = (uint32_t)(uintptr_t)mock_flash;
int i;
while (((uintptr_t)data % 4) != 1)
data++;
for (i = 0; i < 12; i++)
data[i] = (uint8_t)(0xD0 + i);
ck_assert_int_eq(hal_flash_write(base + 1, data, 12), 0);
ck_assert_uint_eq(mock_flash[0], 0xFF);
for (i = 0; i < 12; i++)
ck_assert_uint_eq(mock_flash[1 + i], data[i]);
for (i = 13; i < MOCK_FLASH_SIZE; i++)
ck_assert_uint_eq(mock_flash[i], 0xFF);
}
END_TEST
/* A write that fits entirely inside a single flash word must still work:
* buggy and fixed forms agree here (i is always 0 in the byte-wise path),
* guarding against a fix that breaks the common case. */
@ -170,6 +198,7 @@ Suite *flash_write_suite(void)
tcase_add_checked_fixture(tc, setup, teardown);
tcase_add_test(tc, test_aligned_write_unaligned_tail);
tcase_add_test(tc, test_unaligned_write_mismatched_alignment);
tcase_add_test(tc, test_unaligned_write_matching_alignment_fast_path);
tcase_add_test(tc, test_unaligned_write_single_word);
suite_add_tcase(s, tc);