Skip to content

Report failed flash commits from the PKCS#11 keyvault, check STM32U5 flash errors - #957

Open
LinuxJedi wants to merge 3 commits into
wolfSSL:masterfrom
LinuxJedi:pkcs11-store-flash-errors
Open

LinuxJedi wants to merge 3 commits into
wolfSSL:masterfrom
LinuxJedi:pkcs11-store-flash-errors

Conversation

@LinuxJedi

Copy link
Copy Markdown
Member

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 of hal_flash_erase() and hal_flash_write(), wolfPKCS11's wolfPKCS11_Store_Close() returns void, 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() and check_vault() are counted. 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 (backup sector, then destination) is unchanged. user_settings.h defines WOLFPKCS11_CUSTOM_STORE_CLOSE_COMMIT, which wolfSSL/wolfPKCS11#265 adds: a wolfPKCS11 with it closes written records through wolfPKCS11_Store_CloseCommit() and writes the token again after a failure. The pinned wolfPKCS11 ignores the define and keeps calling wolfPKCS11_Store_Close(), so this can merge before or after it.

stm32u5: hal_flash_write() and hal_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, where hal_flash_write() used to return 0 (TrustZone) or not check at all, and hal_flash_erase() returned 0 for pages past the top. docs/HAL.md notes that hal_flash_erase() returns non-zero on failure and that the keyvault depends on both functions checking the error flags.

Testing:

  • unit-stm32u5-write also extracts hal_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-shot hal_flash_erase_fail.
  • The whole unit suite, in 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).
  • In wolfboot-ci-m33mu:v1.25, the stm32h5-tz PKCS11_TESTAPP=1 run passes and stm32u5-wolfcrypt-tz.config builds, with the pinned wolfPKCS11 and with Let custom stores report records that did not commit wolfPKCS11#265 (which then calls wolfPKCS11_Store_CloseCommit()).
  • Not tested on hardware. On U5 with TrustZone, bank 1 is always programmed through the secure registers; any error flags the hardware raised there were ignored before and now fail the write. Worth a boot and update check on a board before merging.

ZD 21908

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant