scrub the RMW scratch buffers in ext_flash_encrypt_write

Since F-9750 the head and tail read-modify-write paths decrypt the
stored neighbour block into block/enc_block before splicing the
caller's bytes, so the two stack buffers transiently hold plaintext the
caller never supplied, and several exits returned without scrubbing
them (stale head plaintext also outlived into the tail path). Funnel
every exit after the partition switch through a single cleanup that
ForceZero()s both buffers, matching the zeroization posture of the rest
of the campaign (F-7396 header cache, F-7966/F-7971 keys, aes_set_iv
IV). Defense-in-depth: the buffers are stack-local, but this is the most
long-lived plaintext in the write path.

Skoll review finding 4, 2026-08-21 wolfboot review.
pull/868/head
Daniele Lacamera 2026-08-21 09:11:44 +02:00
parent 9b1a48554a
commit ca6b60a1e6
1 changed files with 13 additions and 5 deletions

View File

@ -2723,7 +2723,8 @@ int RAMFUNCTION ext_flash_encrypt_write(uintptr_t address, const uint8_t *data,
step = len;
if (ext_flash_read(row_address, block, ENCRYPT_BLOCK_SIZE)
!= ENCRYPT_BLOCK_SIZE) {
return -1;
ret = -1;
goto exit;
}
/* The stored block is ciphertext: decrypt it so the untouched bytes
* can be patched as plaintext and re-encrypted. Re-encrypting the
@ -2740,10 +2741,10 @@ int RAMFUNCTION ext_flash_encrypt_write(uintptr_t address, const uint8_t *data,
crypto_encrypt(enc_block, block, ENCRYPT_BLOCK_SIZE);
ret = ext_flash_write(row_address, enc_block, ENCRYPT_BLOCK_SIZE);
if (ret < 0)
return ret;
goto exit;
/* The request fits entirely within this block: nothing left to do */
if (step == len)
return ret;
goto exit;
address += step;
data += step;
sz = len - step;
@ -2763,7 +2764,7 @@ int RAMFUNCTION ext_flash_encrypt_write(uintptr_t address, const uint8_t *data,
}
ret = ext_flash_write(address, ENCRYPT_CACHE, chunk);
if (ret < 0)
return ret;
goto exit;
address += chunk;
data += chunk;
step -= chunk;
@ -2779,7 +2780,8 @@ int RAMFUNCTION ext_flash_encrypt_write(uintptr_t address, const uint8_t *data,
ENCRYPT_BLOCK_SIZE;
if (ext_flash_read(address, block, ENCRYPT_BLOCK_SIZE)
!= ENCRYPT_BLOCK_SIZE) {
return -1;
ret = -1;
goto exit;
}
/* Sync the decrypt context to this block (on backends with separate
* encrypt/decrypt contexts it did not advance with the full-block
@ -2796,6 +2798,12 @@ int RAMFUNCTION ext_flash_encrypt_write(uintptr_t address, const uint8_t *data,
ret = ext_flash_write(address, enc_block, ENCRYPT_BLOCK_SIZE);
}
exit:
/* The head/tail RMW paths above decrypted the stored neighbour blocks
* into block/enc_block; scrub the plaintext (and any stale copies)
* on every exit so it does not outlive the write on the stack. */
ForceZero(block, sizeof(block));
ForceZero(enc_block, sizeof(enc_block));
return ret;
}