Skip to content

oneshot hash build option#736

Open
bigbrett wants to merge 2 commits intowolfSSL:masterfrom
bigbrett:oneshot-hash
Open

oneshot hash build option#736
bigbrett wants to merge 2 commits intowolfSSL:masterfrom
bigbrett:oneshot-hash

Conversation

@bigbrett
Copy link
Contributor

Adds build option to enable hashing an entire image in one update call, which can dramatically speed up performance on platforms that can support it (memory mapped flash or wolfHSM client offload with DMA).

Deliberately did not make this incompatible at compile time with EXT_FLASH to prevent interfering with "off-label usage" of EXT_FLASH by some HALs (for example, used for flash read indirection only in certain cases on TC3xx), however the logical incompatibility of the two is clearly stated in the documentation.

Copilot AI review requested due to automatic review settings March 25, 2026 18:48
@bigbrett bigbrett self-assigned this Mar 25, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a build option to enable hashing a full image (and header region) in a single hash update call to improve verification performance on memory-mapped/offload-capable platforms.

Changes:

  • Introduces WOLFBOOT_IMG_HASH_ONESHOT build flag (config + options) to toggle one-shot hashing.
  • Updates image hashing paths in src/image.c to use a single wc_*Update() call when enabled.
  • Documents the option and expands CI workflows to build/run simulator tests with the flag enabled.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tools/config.mk Adds default value and exposes WOLFBOOT_IMG_HASH_ONESHOT in CONFIG_VARS.
options.mk Maps WOLFBOOT_IMG_HASH_ONESHOT=1 to -DWOLFBOOT_IMG_HASH_ONESHOT.
src/image.c Adds one-shot hashing branches for header/image hashing and flash hashing helpers.
docs/compile.md Documents behavior, constraints, and incompatibility notes for one-shot hashing.
.github/workflows/test-sunnyday-simulator.yml Adds simulator build+run coverage for oneshot + SHA256/SHA384/SHA3.
.github/workflows/test-elf-scattered.yml Adds an ELF_SCATTERED simulator build+run with oneshot enabled.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenrir Automated Review — PR #736

Scan targets checked: wolfboot-bugs, wolfboot-src
Findings: 3

Medium (2)

Missing bounds guard in ONESHOT header hash allows integer wrap on malformed image

File: src/image.c:990,1093,1202
Function: header_sha256, header_sha384, header_sha3_384
Category: Integer overflows

In the ONESHOT paths of header_sha256, header_sha384, and header_sha3_384, the expression (word32)(end_sha - p) is passed directly to the hash update function without first verifying that end_sha >= p. The original block-by-block code was naturally guarded by the while (p < end_sha) loop condition, which would simply not execute if end_sha <= p. In the ONESHOT path, if a malformed image header causes end_sha to be less than p (e.g., a crafted image where the stored SHA TLV appears at an unexpected offset), the pointer subtraction yields a negative ptrdiff_t which wraps to a very large value when cast to word32. This would cause wc_Sha256Update (and the sha384/sha3 variants) to read far beyond the intended buffer, potentially crashing the bootloader or causing a denial-of-service during firmware verification.

#ifdef WOLFBOOT_IMG_HASH_ONESHOT
    wc_Sha256Update(sha256_ctx, p, (word32)(end_sha - p));
#else
    {
        int blksz;
        while (p < end_sha) {

Recommendation: Add a bounds check before the ONESHOT hash call in all three header hash functions: if (end_sha <= p) return -1; (or return 0; to match the no-data behavior of the original loop). For example:

#ifdef WOLFBOOT_IMG_HASH_ONESHOT
    if (end_sha <= p)
        return -1;
    wc_Sha256Update(sha256_ctx, p, (word32)(end_sha - p));
#else

ONESHOT update_hash_flash_addr silently ignores src_ext flag with no compile-time guard

File: src/image.c:1803-1806
Function: update_hash_flash_addr
Category: Flash manipulation

When WOLFBOOT_IMG_HASH_ONESHOT is defined, update_hash_flash_addr casts addr to (uint8_t*) and dereferences it for size bytes, while discarding src_ext with (void)src_ext. The src_ext parameter indicates whether the address resides in external (non-memory-mapped) flash. If a user misconfigures the build by enabling both WOLFBOOT_IMG_HASH_ONESHOT=1 and EXT_FLASH=1, this function will attempt to directly dereference an external flash address as a memory pointer, leading to a hard fault, reading from an unintended memory region, or computing an incorrect hash — any of which could compromise the boot verification process. While the documentation warns against this combination, there is no #if defined(WOLFBOOT_IMG_HASH_ONESHOT) && defined(EXT_FLASH) #error ... #endif compile-time guard to enforce it.

#ifdef WOLFBOOT_IMG_HASH_ONESHOT
    (void)src_ext;
    update_hash(ctx, (uint8_t*)addr, size);
    return 0;
#else

Recommendation: Add a compile-time guard in src/image.c (or in options.mk) to prevent the incompatible configuration:

#if defined(WOLFBOOT_IMG_HASH_ONESHOT) && defined(EXT_FLASH)
    #error "WOLFBOOT_IMG_HASH_ONESHOT is incompatible with EXT_FLASH"
#endif

Alternatively, in options.mk:

ifeq ($(WOLFBOOT_IMG_HASH_ONESHOT),1)
  ifeq ($(EXT_FLASH),1)
    $(error WOLFBOOT_IMG_HASH_ONESHOT is incompatible with EXT_FLASH)
  endif
  CFLAGS+=-DWOLFBOOT_IMG_HASH_ONESHOT
endif

Low (1)

ONESHOT image hash paths skip NULL check on fw_base

File: src/image.c:1016,1122,1230
Function: image_sha256, image_sha384, image_sha3_384
Category: NULL pointer dereference

In the ONESHOT paths of image_sha256, image_sha384, and image_sha3_384, img->fw_base is passed directly to the hash update function (e.g., wc_Sha256Update(&sha256_ctx, img->fw_base, img->fw_size)). The original block-by-block code retrieved the data pointer via get_sha_block(img, position) and checked if (p == NULL) break;, which provided a safety net if the firmware base was not accessible. The ONESHOT path has no equivalent NULL check on img->fw_base. While img itself is checked in the preceding header_sha* call, img->fw_base could be NULL if the image metadata was initialized but the firmware was not memory-mapped, potentially causing a NULL pointer dereference.

#ifdef WOLFBOOT_IMG_HASH_ONESHOT
    wc_Sha256Update(&sha256_ctx, img->fw_base, img->fw_size);
#else
    {
        uint32_t position = 0;
        uint8_t* p;
        int      blksz;
        do {
            p = get_sha_block(img, position);
            if (p == NULL)
                break;

Recommendation: Add a NULL check on img->fw_base before the ONESHOT hash call in all three image hash functions:

#ifdef WOLFBOOT_IMG_HASH_ONESHOT
    if (img->fw_base == NULL)
        return -1;
    wc_Sha256Update(&sha256_ctx, img->fw_base, img->fw_size);
#else

This review was generated automatically by Fenrir. Findings are non-blocking.

@bigbrett
Copy link
Contributor Author

Addressed Fenrir pointer validation concerns. Wontfix compile-time check against EXT_FLASH for reasons mentioned in PR description

@bigbrett bigbrett requested review from danielinux and dgarske March 26, 2026 17:07
@bigbrett bigbrett assigned wolfSSL-Bot and unassigned bigbrett Mar 26, 2026
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.

4 participants