Hello U-Boot maintainers,

I found a heap out-of-bounds read in the FWU v2 metadata initialization path.

Tested version
==============

U-Boot v2026.10-rc5
Commit: a06e89ab05eaa2b8344d521319399333cd760ae5

Affected configuration
======================

CONFIG_FWU_MULTI_BANK_UPDATE=y
CONFIG_FWU_MDATA_V2=y
CONFIG_FWU_NUM_BANKS=2
CONFIG_FWU_NUM_IMAGES_PER_BANK=2

Summary
=======

FWU v2 uses the metadata_size value read from persistent FWU metadata to
allocate heap storage for two metadata copies.

A metadata_size of 40 bytes contains the 32-byte top-level header and the
8-byte firmware-store descriptor header. It can therefore pass the version,
descriptor offset, CRC, bank count, and image count checks.

The metadata does not contain the image-entry array required by the configured
number of images. However, fwu_data_init() copies the entire image array whose
size is determined by the compile-time configuration. This results in a heap
out-of-bounds read.

Technical details
=================

lib/fwu_updates/fwu.c allocates storage for two copies using the metadata size:

    int fwu_mdata_copies_allocate(u32 mdata_size)
    {
        ...
        g_fwu_data.fwu_mdata = calloc(2, mdata_size);
        ...
    }

With metadata_size=40, the total allocation is 80 bytes.

lib/fwu_updates/fwu_v2.c compares the descriptor's bank and image counts with
the configured values:

    num_banks = fwu_get_fw_desc(mdata)->num_banks;
    num_images = fwu_get_fw_desc(mdata)->num_images;

    if (num_banks != CONFIG_FWU_NUM_BANKS)
        return -EINVAL;

    if (num_images != CONFIG_FWU_NUM_IMAGES_PER_BANK)
        return -EINVAL;

There is no check that metadata_size includes the corresponding
struct fwu_image_entry array.

fwu_data_init() subsequently performs this copy:

    image_info_size = sizeof(data->fwu_images);
    src_img_info = &fwu_get_fw_desc(mdata)->img_entry[0];
    dst_img_info = &data->fwu_images[0];

    memcpy(dst_img_info, src_img_info, image_info_size);

With two banks and two images, the relevant sizes are:

    sizeof(struct fwu_mdata)             = 32
    descriptor header                    = 8
    declared metadata_size               = 40
    allocation for two metadata copies   = 80
    sizeof(data->fwu_images)              = 160
    copy source offset                    = 40
    bytes read beyond the allocation      = 120

memcpy() reads 160 bytes beginning at allocation offset 40. The allocation
ends at offset 80, so the final 120 bytes are outside the heap object.

Reproduction
============

1. Build U-Boot with the configuration shown above.
2. Prepare both the primary and secondary FWU metadata copies with:

       version       = 2
       metadata_size = 40
       desc_offset   = 32
       num_banks     = 2
       num_images    = 2

3. Set a valid CRC32 for the declared 40-byte metadata.
4. Store both copies in the configured FWU metadata locations.
5. Invoke fwu_init() through the normal FWU initialization path.

The mandatory-header, CRC, bank-count, and image-count checks pass.
fwu_data_init() then reads the missing image entries beyond the allocated
metadata storage.

ASan result
===========

    top-level metadata size: 32
    descriptor header size: 8
    declared metadata size: 40
    image copy size: 160
    total allocation for two copies: 80

    ERROR: AddressSanitizer: heap-buffer-overflow
    READ of size 160
        #0 memcpy
        #1 fwu_data_init
           lib/fwu_updates/fwu_v2.c:55
        #2 fwu_init
           lib/fwu_updates/fwu_v2.c:259

    The read begins at allocation offset 40 and extends to offset 199.
    The allocation ends at offset 80, resulting in a 120-byte over-read.

Impact
======

An attacker who can modify or supply FWU v2 metadata with a valid CRC can
trigger a heap out-of-bounds read while U-Boot initializes the metadata.

The confirmed impact is a bootloader crash or denial of service. Data located
after the allocation may also be copied into U-Boot's internal FWU image
information, although I have not demonstrated disclosure or control-flow
impact.

The issue is present in current main at the tested revision.

Regards,
Jaeyeong Lee

Reply via email to