Repository navigation
Conversation
hal_flash_write() never read the FLASH_SR error flags and returned 0 whatever happened. With TrustZone it also returned 0 for an address out of range, and without TrustZone it did not check the address at all. hal_flash_erase() failed only for a zero length or an address below the flash, and returned 0 for pages past the top of the flash. Check OPERR, PROGERR, WRPERR, PGAERR, SIZERR and PGSERR after each quad-word program and each page erase. On an error, clear PG or PER and the flags, and return -1. The status is read once before EOP is cleared, because clearing EOP writes the error flags back as well, which clears them. Both functions now return -1 for a negative length or a span that is not all inside the flash, before touching it. The STM32U5 write test now extracts hal_flash_erase() as well. Its flash is two 8 KB pages, and the mock hal_flash_wait_complete() erases the page selected in FLASH_CR on STRT. It can also fail one program or erase with chosen FLASH_SR flags, leaving the flash unchanged. The host register file cannot clear write-1-to-clear flags, so the mock sets FLASH_NS_SR to the result of each operation. New tests check that each error flag stops a write on the unit that set it and an erase on the page that set it, with PG or PER cleared, that other flags do not fail a write, and that ranges outside the flash are refused before anything changes. The error and range tests fail on the HAL without the checks.
The keyvault ignored the return values of hal_flash_erase() and hal_flash_write(), and wolfPKCS11_Store_Close() returns void, so a record that did not reach flash looked committed. wolfPKCS11 then had no reason to write it again. Count erase and program failures in cache_commit_entry(), restore_backup() and check_vault(). A store window records the count at wolfPKCS11_Store_Open(), and the new wolfPKCS11_Store_CloseCommit() returns BUFFER_E when it changed during the window. wolfPKCS11_Store_Remove() does the same. The sector cache is shared, so every window open during a failure reports it; a window that lost nothing only makes wolfPKCS11 write all records again. The commit sequence itself is unchanged. user_settings.h defines WOLFPKCS11_CUSTOM_STORE_CLOSE_COMMIT, so a wolfPKCS11 that supports it closes written records with wolfPKCS11_Store_CloseCommit() and marks the token for a full store when it fails. The pinned wolfPKCS11 ignores the define and keeps calling wolfPKCS11_Store_Close(). docs/HAL.md now says that hal_flash_erase() returns non-zero on failure, and why the keyvault needs both functions to check the flash error flags.
The mock flash gains hal_flash_erase_fail, a one-shot erase failure like hal_flash_write_fail. New tests check that a failed write or erase while a window commits fails its close and not the next window's, that a failure is reported by every window open at the time but not by one opened later, that wolfPKCS11_Store_Remove() reports a failure and returns NOT_AVAILABLE_E only for a missing object, and that failures while Store_Open formats a blank vault or restores the header from the backup sector belong to that window. They fail on the store without failure tracking.
|
@claude review |
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #957
Scan targets checked: wolfboot-src, wolfboot-bugs
Not reviewed (no scan target covers them): docs/HAL.md, hal/stm32u5.h, tools/unit-tests/Makefile
Coverage: 2 of 6 in-scope changed file(s) opened by the reviewer; not opened: include/user_settings.h, tools/unit-tests/unit-mock-flash.c, tools/unit-tests/unit-pkcs11_store.c, tools/unit-tests/unit-stm32u5-write.c
Fenrir result: Approved ✅
No new issues found in the changed files.
Advisory only — this automated result does not count as a GitHub approval.
Review tier: Lite
There was a problem hiding this comment.
I reviewed this PR and didn't find any bugs. It changes how the STM32U5 flash HAL reports errors, and that HAL is used during boot and updates. You also note it hasn't been tested on hardware, so a human look and a check on a board would still be worthwhile.
What was reviewed:
hal_flash_write/hal_flash_erasein hal/stm32u5.c: the new range checks, and checkingFLASH_SR_ERRORSafter each quad-word or page. Write reads SR once before clearing EOP, so the error flags aren't lost. Erase picks the secure or non-securesrto matchcrfor each bank.- src/pkcs11_store.c: the failure counter in
cache_commit_entry,restore_backupandcheck_vault. The window baseline is taken before Open's owncheck_vault. InStore_Remove, STORE_FLASH_E is now checked before NOT_AVAILABLE_E, and the payload is still erased before the object is deleted. Store_Closenow wrapsCloseCommit, so the pinned wolfPKCS11 behaves the same whether or not it knows the new define.
Extended reasoning...
The change touches the STM32U5 flash program/erase HAL in hal/stm32u5.c, which runs from RAM and has TrustZone secure and non-secure register paths. It also touches the secure PKCS#11 keyvault commit path in src/pkcs11_store.c, plus a new wolfPKCS11 integration define in user_settings.h. Both are security-sensitive: the HAL is used for firmware updates, and the keyvault holds key material. Write and erase now return -1 for errors and out-of-range spans where they used to return 0. Error flags set on TrustZone bank-1 writes will now fail operations that used to succeed. The author says the change hasn't been checked on hardware, so it should not be approved without a human review.
wolfPKCS11 2.2.0 and earlier close a data record even when opening it failed, and pass NULL. wolfPKCS11_Store_CloseCommit(), which wolfPKCS11_Store_Close() calls, wrote through the handle and faulted. It now returns 0 for NULL. unit-pkcs11_store covers both closes.
A flash erase or program that fails while the PKCS#11 keyvault (
src/pkcs11_store.c) commits a record went unnoticed. The store ignored the return values ofhal_flash_erase()andhal_flash_write(), wolfPKCS11'swolfPKCS11_Store_Close()returnsvoid, and on STM32U5 the HAL returned 0 anyway. wolfPKCS11 therefore treated the record as stored and did not write it again (since wolfSSL/wolfPKCS11#257 it only rewrites everything after a store that failed).pkcs11_store: erase and program failures in
cache_commit_entry(),restore_backup()andcheck_vault()are counted. A store window records the count atwolfPKCS11_Store_Open(), and the newwolfPKCS11_Store_CloseCommit()returnsBUFFER_Ewhen it changed during the window.wolfPKCS11_Store_Remove()does the same. The sector cache is shared, so every window open during a failure reports it; a window that lost nothing only makes wolfPKCS11 write all records again. The commit sequence itself (backup sector, then destination) is unchanged.user_settings.hdefinesWOLFPKCS11_CUSTOM_STORE_CLOSE_COMMIT, which wolfSSL/wolfPKCS11#265 adds: a wolfPKCS11 with it closes written records throughwolfPKCS11_Store_CloseCommit()and writes the token again after a failure. The pinned wolfPKCS11 ignores the define and keeps callingwolfPKCS11_Store_Close(), so this can merge before or after it.wolfPKCS11_Store_CloseCommit(), whichwolfPKCS11_Store_Close()calls, returns 0 for aNULLstore: wolfPKCS11 2.2.0 and earlier close a data record even when opening it failed, and the close wrote through the handle (wolfSSL/wolfPKCS11#265 stops wolfPKCS11 doing that).stm32u5:
hal_flash_write()andhal_flash_erase()now check OPERR, PROGERR, WRPERR, PGAERR, SIZERR and PGSERR after each quad-word and each page, clear PG or PER and the flags, and return -1. The status is read before EOP is cleared, since clearing EOP writes the error flags back and clears them too. Both return -1 for a negative length or a span not inside the flash, wherehal_flash_write()used to return 0 (TrustZone) or not check at all, andhal_flash_erase()returned 0 for pages past the top.docs/HAL.mdnotes thathal_flash_erase()returns non-zero on failure and that the keyvault depends on both functions checking the error flags.Testing:
unit-stm32u5-writealso extractshal_flash_erase(). Its mock wait erases pages and can fail one operation with chosen flags. New tests cover each error flag for write and erase, flags that are not errors, and out-of-range spans. The error and range tests fail on the HAL without the checks.unit-pkcs11_store(and-stalecache): new tests cover a failed write or erase in a window's close, a failure seen by every open window but not by a later one,wolfPKCS11_Store_Remove(), and failures while formatting the vault or restoring the header from the backup sector. They fail on the store without the tracking. The mock flash gains a one-shothal_flash_erase_fail. Another test closes aNULLstore, which faults without the check.wolfboot-ci-sim:v1.0, gives the same results on master and on this branch (223 pass; the same 7 tests fail on both for lack of tools or SDKs in that image).wolfboot-ci-m33mu:v1.25, thestm32h5-tzPKCS11_TESTAPP=1run passes andstm32u5-wolfcrypt-tz.configbuilds, with the pinned wolfPKCS11 and with Let custom stores report records that did not commit wolfPKCS11#265 (which then callswolfPKCS11_Store_CloseCommit()).stm32u5-wolfcrypt-tz.config), these commits on top of v2.10.0 with stm32u5: accept secure alias addresses in hal_flash_write/erase #943 and stm32u5: skip programming quad-words that are erased and stay erased #944, and a 2.1.0 backport of Let custom stores report records that did not commit wolfPKCS11#265: the PKCS#11 store and HAL code is the same as here. Creating 8 token objects, reading them back and destroying them across reboots raised no flash errors, with the same commit counts and times as before. With the keyvault pages write-protected (WRP1A), the HAL failed the erases and writes, and eachC_DestroyObjectreturnedCKR_FUNCTION_FAILEDinstead ofCKR_OK; once the protection was removed, the objects were intact and could be destroyed. A firmware update (swap) was not run on the board, so that use of the U5 HAL checks is covered by the unit tests only.ZD 21908