Hi,

On Tue, 3 Mar 2026 at 01:09, Ahmad Fatoum <[email protected]> wrote:
>
> Hello Tom,
>
> On 3/2/26 23:09, Tom Rini wrote:
> > There is a flaw in how U-Boot verifies and generates signatures for FIT
> > images. To prevent mix and match style attacks, it is recommended to
> > use signed configurations. How this is supposed to work is documented in
> > doc/usage/fit/signature.rst.
> >
> > Crucially, the `hashed-nodes` property of the `signature` node contains
> > which nodes of the FIT device tree were hashed as part of the signature
> > and should be verified. However, this property itself is not part of the
> > hash and can therefore be modified by an attacker. Furthermore, the
> > signature only contains the name of each node and not the path in the
> > device tree to the node.
> >
> > This patch reworks the code to address this specific oversight.
>
> Do I understand correctly that this is a breaking change
> for FIT with signed configurations?
>
> - New U-Boot hashes more than intended for old FIT
> - Old U-Boot hashes less than intended for new FIT

Yes, that's right.

Reviewed-by: Simon Glass <[email protected]>

I can see how this works. Please see nit below.

>
> Thanks,
> Ahmad
>
> >
> > Thanks to Apple Security Engineering and Architecture (SEAR) for
> > reporting this issue and then coming up with a fix.
> >
> > Reported-by: Apple Security Engineering and Architecture (SEAR)
> > Signed-off-by: Tom Rini <[email protected]>
> > ---
> > I assume one question will be about adding a test case. I have been
> > asked to not share the testcase for a few releases yet, so plan to add
> > that later in the year.
> > ---
> >  boot/image-fit-sig.c | 31 +++++++++++++++++++++++--------
> >  include/image.h      | 12 ++++++++++++
> >  tools/image-host.c   |  9 +++++++--
> >  3 files changed, 42 insertions(+), 10 deletions(-)
> >
> > diff --git a/boot/image-fit-sig.c b/boot/image-fit-sig.c
> > index f23e9d5d0b0f..dce8bbe30e93 100644
> > --- a/boot/image-fit-sig.c
> > +++ b/boot/image-fit-sig.c
> > @@ -44,12 +44,6 @@ struct image_region *fit_region_make_list(const void 
> > *fit,
> >       debug("Hash regions:\n");
> >       debug("%10s %10s\n", "Offset", "Size");
> >
> > -     /*
> > -      * Use malloc() except in SPL (to save code size). In SPL the caller
> > -      * must allocate the array.
> > -      */
> > -     if (!IS_ENABLED(CONFIG_XPL_BUILD) && !region)
> > -             region = calloc(sizeof(*region), count);
> >       if (!region)
> >               return NULL;
> >       for (i = 0; i < count; i++) {
> > @@ -62,6 +56,23 @@ struct image_region *fit_region_make_list(const void 
> > *fit,
> >       return region;
> >  }
> >
> > +int fit_region_add_hashed_nodes(struct image_region *region, int count,
> > +                                     const char* hashed_nodes, int 
> > hashed_nodes_len)

That should line up with the previous line.

> > +{
> > +     /* Add the spacer to ensure the hashed strings and hashed-nodes 
> > cannot "overlap". */
> > +     const char* HASHED_NODES_SPACER = "hashed-nodes";
> > +     region[count].data = HASHED_NODES_SPACER;
> > +     region[count].size = strlen(HASHED_NODES_SPACER);
> > +     count++;
> > +
> > +     region[count].data = hashed_nodes;
> > +     region[count].size = hashed_nodes_len;
> > +     count++;
> > +
> > +     /* Now add the actual hashed-nodes value. */
> > +     return count;
> > +}
> > +
> >  static int fit_image_setup_verify(struct image_sign_info *info,
> >                                 const void *fit, int noffset,
> >                                 const void *key_blob, int required_keynode,
> > @@ -376,10 +387,14 @@ static int fit_config_check_sig(const void *fit, int 
> > noffset, int conf_noffset,
> >               count++;
> >       }
> >
> > -     /* Allocate the region list on the stack */
> > -     struct image_region region[count];
> > +     /* Allocate the region list on the stack, +2 for the hashed-nodes */
> > +     struct image_region region[count+2];

spaces around +

> >
> >       fit_region_make_list(fit, fdt_regions, count, region);
> > +
> > +     /* Add the hashed-nodes */
> > +     count = fit_region_add_hashed_nodes(region, count, prop, prop_len);
> > +
> >       if (info.crypto->verify(&info, region, count, fit_value,
> >                               fit_value_len)) {
> >               *err_msgp = "Verification failed";
> > diff --git a/include/image.h b/include/image.h
> > index 34efac6056dd..5b847527b586 100644
> > --- a/include/image.h
> > +++ b/include/image.h
> > @@ -1771,6 +1771,18 @@ struct image_region *fit_region_make_list(const void 
> > *fit,
> >               struct fdt_region *fdt_regions, int count,
> >               struct image_region *region);
> >
> > +/**
> > + * fit_region_add_hashed_nodes() - Add a list of hashed nodes to the 
> > regions
> > + *
> > + * @region:          List of regions to add to
> > + * @count:           Number of existing regions (Note: region needs to 
> > have space for an additional two regions)

long line

> > + * @hashed_nodes:            List of hashed nodes
> > + * @hashed_nodes_len:        Length of list
> > + * Return: The updated count value (i.e. count+2).
> > + */
> > +int fit_region_add_hashed_nodes(struct image_region *region, int count,
> > +             const char* hashed_nodes, int hashed_nodes_len);
> > +
> >  static inline int fit_image_check_target_arch(const void *fdt, int node)
> >  {
> >  #ifndef USE_HOSTCC
> > diff --git a/tools/image-host.c b/tools/image-host.c
> > index 48d69191c921..1e7c38a9a211 100644
> > --- a/tools/image-host.c
> > +++ b/tools/image-host.c
> > @@ -1018,13 +1018,15 @@ static int fit_config_get_regions(const void *fit, 
> > int conf_noffset,
> >               return -EINVAL;
> >       }
> >
> > -     /* Build our list of data blocks */
> > -     region = fit_region_make_list(fit, fdt_regions, count, NULL);
> > +     /* Allocate the region array, +2 for the hashed-nodes region. */
> > +     region = calloc(count+2, sizeof(*region));
> >       if (!region) {
> >               fprintf(stderr, "Out of memory hashing configuration 
> > '%s/%s'\n",
> >                       conf_name, sig_name);
> >               return -ENOMEM;
> >       }
> > +     /* Build our list of data blocks */
> > +     fit_region_make_list(fit, fdt_regions, count, region);
> >
> >       /* Create a list of all hashed properties */
> >       debug("Hash nodes:\n");
> > @@ -1043,6 +1045,9 @@ static int fit_config_get_regions(const void *fit, 
> > int conf_noffset,
> >               strcpy(region_prop + len, node_inc.strings[i]);
> >       strlist_free(&node_inc);
> >
> > +     /* Add the hashed-nodes. */
> > +     count = fit_region_add_hashed_nodes(region, count, region_prop, len);
> > +
> >       *region_countp = count;
> >       *regionp = region;
> >       *region_propp = region_prop;

Regards,
Simon

Reply via email to