Package: release.debian.org
Severity: normal
Tags: trixie
Control: affects -1 + src:u-boot
User: [email protected]
Usertags: pu

On behalf of the LTS Team and in coordination with u-boot maintainer
I'd like to propose this stable update of u-boot.

[ Reason ]
I've prepared an updated u-boot package for stable fixing 2 outstanding
CVEs, which have both been tagged no-DSA. The same upstream version
is currently in use in unstable, testing and stable. The
unstable/testing upload can thus serve us as a useful test for this
stable-proposed-update.

[ Impact ]
The two issues are a rouge dhcp server can extract memory from u-boot
bootp (dhcp) implementation + FIT verification can be bypassed.
I expect the latter to not be very commonly used with debian packages

[ Tests ]
No regressions has been reported so far in unstable/testing, however
note that the binary payload of the debian package is *not*
automatically installed on target systems (unlike for example
upgrade-grub2) so I'm not sure exactly how much actual testing has
happened yet.

The package has also been in debusine which runs as much tests as
are available. The included test that came with the FIT verification
bugfix is however unused.

The debusine work-request for the stable update is at:
https://debusine.debian.net/debian/developers/work-request/856876/

It has not yet been signed, and is thus not uploaded to the archive yet.
Please tell me when to proceed to sign/upload (or feel free to sign it
yourself).

[ Risks ]
I don't think FIT verification is much used by those using the debian
packaged u-boot and dhcp is likely not the standard boot path either.
Any regression is thus unlikely to effect most u-boot users regular
bootup.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Changes ]
Backported patches cherry-picked from upstream for CVE fixes.

[ Other info ]
Apart from unstable/testing, the issue is already fixed in LTS
(bookworm, bullseye). The security team has been involved in
coordinating all of these uploads. I've also been discussing and
offering my help with u-boot maintainer.
diff -Nru u-boot-2025.01/debian/changelog u-boot-2025.01/debian/changelog
--- u-boot-2025.01/debian/changelog     2025-04-09 01:07:41.000000000 +0200
+++ u-boot-2025.01/debian/changelog     2026-06-24 11:07:15.000000000 +0200
@@ -1,3 +1,13 @@
+u-boot (2025.01-3+deb13u1) trixie; urgency=medium
+
+  * Non-maintainer upload by the LTS Team.
+  * CVE-2024-42040: buffer overread vulnerability in the DHCP implementation.
+    (Closes: #1081557)
+  * CVE-2026-46728: mishandles use of unit addresses in a FIT.
+    (Closes: #1136954)
+
+ -- Andreas Henriksson <[email protected]>  Wed, 24 Jun 2026 11:07:15 +0200
+
 u-boot (2025.01-3) unstable; urgency=medium
 
   [ Marek Vasut ]
diff -Nru u-boot-2025.01/debian/patches/CVE-2024-42040.patch 
u-boot-2025.01/debian/patches/CVE-2024-42040.patch
--- u-boot-2025.01/debian/patches/CVE-2024-42040.patch  1970-01-01 
01:00:00.000000000 +0100
+++ u-boot-2025.01/debian/patches/CVE-2024-42040.patch  2026-06-24 
11:05:18.000000000 +0200
@@ -0,0 +1,48 @@
+From: Paul HENRYS <[email protected]>
+Date: Thu, 9 Oct 2025 17:43:28 +0200
+Subject: net: bootp: Prevent buffer overflow to avoid leaking the RAM content
+
+CVE-2024-42040 describes a possible buffer overflow when calling
+bootp_process_vendor() in bootp_handler() since the total length
+of the packet is passed to bootp_process_vendor() without being
+reduced to len-(offsetof(struct bootp_hdr,bp_vend)+4).
+
+The packet length is also checked against its minimum size to avoid
+reading data from struct bootp_hdr outside of the packet length.
+
+Signed-off-by: Paul HENRYS <[email protected]>
+Signed-off-by: Philippe Reynes <[email protected]>
+(cherry picked from commit 81e5708cc2c865df606e49aed5415adb2a662171)
+---
+ net/bootp.c | 11 ++++++++++-
+ 1 file changed, 10 insertions(+), 1 deletion(-)
+
+diff --git a/net/bootp.c b/net/bootp.c
+index afd5b48..14be99e 100644
+--- a/net/bootp.c
++++ b/net/bootp.c
+@@ -361,6 +361,14 @@ static void bootp_handler(uchar *pkt, unsigned dest, 
struct in_addr sip,
+       debug("got BOOTP packet (src=%d, dst=%d, len=%d want_len=%zu)\n",
+             src, dest, len, sizeof(struct bootp_hdr));
+ 
++      /* Check the minimum size of a BOOTP packet is respected.
++       * A BOOTP packet is between 300 bytes and 576 bytes big
++       */
++      if (len < offsetof(struct bootp_hdr, bp_vend) + 64) {
++              printf("Error: got an invalid BOOTP packet (len=%u)\n", len);
++              return;
++      }
++
+       bp = (struct bootp_hdr *)pkt;
+ 
+       /* Filter out pkts we don't want */
+@@ -378,7 +386,8 @@ static void bootp_handler(uchar *pkt, unsigned dest, 
struct in_addr sip,
+ 
+       /* Retrieve extended information (we must parse the vendor area) */
+       if (net_read_u32((u32 *)&bp->bp_vend[0]) == htonl(BOOTP_VENDOR_MAGIC))
+-              bootp_process_vendor((uchar *)&bp->bp_vend[4], len);
++              bootp_process_vendor((uchar *)&bp->bp_vend[4], len -
++                                   (offsetof(struct bootp_hdr, bp_vend) + 4));
+ 
+       net_set_timeout_handler(0, (thand_f *)0);
+       bootstage_mark_name(BOOTSTAGE_ID_BOOTP_STOP, "bootp_stop");
diff -Nru u-boot-2025.01/debian/patches/CVE-2026-46728-1.patch 
u-boot-2025.01/debian/patches/CVE-2026-46728-1.patch
--- u-boot-2025.01/debian/patches/CVE-2026-46728-1.patch        1970-01-01 
01:00:00.000000000 +0100
+++ u-boot-2025.01/debian/patches/CVE-2026-46728-1.patch        2026-06-24 
11:05:18.000000000 +0200
@@ -0,0 +1,51 @@
+From: Quentin Schulz <[email protected]>
+Date: Wed, 3 Dec 2025 17:19:34 +0100
+Subject: boot/fit: declare (and use) new constant for conf's compatible prop
+
+Fit conf node may have a compatible property[1] which stores the root
+compatible of the first blob in the fdt property of the node. This can
+be used to automatically select the proper conf node based on the
+compatible from the running U-Boot (matching the former's compatible
+with the latter)[2].
+
+This adds (and uses) this constant for FIT node parsing.
+
+Note that this property may also appear in fpga image nodes[3] but that
+isn't done in this commit.
+
+[1] https://fitspec.osfw.foundation/#optional-properties compatible paragraph
+[2] https://fitspec.osfw.foundation/#select-a-configuration-to-boot
+[3] https://fitspec.osfw.foundation/#images-node 2.3.2 Conditionally mandatory 
property
+
+Signed-off-by: Quentin Schulz <[email protected]>
+(cherry picked from commit 3059eb0c27dd1b4a40a06bda4a47e10246185aca)
+---
+ boot/image-fit.c | 2 +-
+ include/image.h  | 1 +
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/boot/image-fit.c b/boot/image-fit.c
+index 7d56f0b..af98ccd 100644
+--- a/boot/image-fit.c
++++ b/boot/image-fit.c
+@@ -1756,7 +1756,7 @@ int fit_conf_find_compat(const void *fit, const void 
*fdt)
+                       continue;
+ 
+               /* If there's a compat property in the config node, use that. */
+-              if (fdt_getprop(fit, noffset, "compatible", NULL)) {
++              if (fdt_getprop(fit, noffset, FIT_COMPAT_PROP, NULL)) {
+                       fdt = fit;                /* search in FIT image */
+                       compat_noffset = noffset; /* search under config node */
+               } else {        /* Otherwise extract it from the kernel FDT. */
+diff --git a/include/image.h b/include/image.h
+index c52fced..52b26ed 100644
+--- a/include/image.h
++++ b/include/image.h
+@@ -1102,6 +1102,7 @@ int booti_setup(ulong image, ulong *relocated_addr, 
ulong *size,
+ #define FIT_STANDALONE_PROP   "standalone"
+ #define FIT_SCRIPT_PROP               "script"
+ #define FIT_PHASE_PROP                "phase"
++#define FIT_COMPAT_PROP               "compatible"
+ 
+ #define FIT_MAX_HASH_LEN      HASH_MAX_DIGEST_SIZE
+ 
diff -Nru u-boot-2025.01/debian/patches/CVE-2026-46728-2.patch 
u-boot-2025.01/debian/patches/CVE-2026-46728-2.patch
--- u-boot-2025.01/debian/patches/CVE-2026-46728-2.patch        1970-01-01 
01:00:00.000000000 +0100
+++ u-boot-2025.01/debian/patches/CVE-2026-46728-2.patch        2026-06-24 
11:05:18.000000000 +0200
@@ -0,0 +1,369 @@
+From: Simon Glass <[email protected]>
+Date: Thu, 5 Mar 2026 18:20:09 -0700
+Subject: boot: Add fit_config_get_hash_list() to build signed node list
+
+The hashed-nodes property in a FIT signature node lists which FDT paths
+are included in the signature hash. It is intended as a hint so should
+not be used for verification.
+
+Add a function to build the node list from scratch by iterating the
+configuration's image references. Skip properties known not to be image
+references. For each image, collect the path plus all hash and cipher
+subnodes.
+
+Use the new function in fit_config_check_sig() instead of reading
+'hashed-nodes'.
+
+Update the test_vboot kernel@ test case: fit_check_sign now catches the
+attack at signature-verification time (the @-suffixed node is hashed
+instead of the real one, causing a mismatch) rather than at
+fit_check_format() time.
+
+Update the docs to cover this. The FIT spec can be updated separately.
+
+Signed-off-by: Simon Glass <[email protected]>
+Closes: 
https://lore.kernel.org/u-boot/[email protected]/
+Reported-by: Apple Security Engineering and Architecture (SEAR)
+Tested-by: Tom Rini <[email protected]>
+(cherry picked from commit 2092322b31cc8b1f8c9e2e238d1043ae0637b241)
+---
+ boot/image-fit-sig.c        | 227 +++++++++++++++++++++++++++++++++++---------
+ doc/usage/fit/signature.rst |  19 ++--
+ test/py/tests/test_vboot.py |  10 +-
+ 3 files changed, 202 insertions(+), 54 deletions(-)
+
+diff --git a/boot/image-fit-sig.c b/boot/image-fit-sig.c
+index a121de6..62a016a 100644
+--- a/boot/image-fit-sig.c
++++ b/boot/image-fit-sig.c
+@@ -18,6 +18,7 @@ DECLARE_GLOBAL_DATA_PTR;
+ #include <u-boot/hash-checksum.h>
+ 
+ #define IMAGE_MAX_HASHED_NODES                100
++#define FIT_MAX_HASH_PATH_BUF         4096
+ 
+ /**
+  * fit_region_make_list() - Make a list of image regions
+@@ -224,6 +225,179 @@ int fit_image_verify_required_sigs(const void *fit, int 
image_noffset,
+       return 0;
+ }
+ 
++/**
++ * fit_config_add_hash() - Add hash nodes for one image to the node list
++ *
++ * Adds the image path, all its hash-* subnode paths, and its cipher
++ * subnode path (if present) to the packed buffer.
++ *
++ * @fit:              FIT blob
++ * @image_noffset:    Image node offset (e.g. /images/kernel-1)
++ * @node_inc:         Array of path pointers to fill
++ * @count:            Pointer to current count (updated on return)
++ * @max_nodes:                Maximum entries in @node_inc
++ * @buf:              Buffer for packed path strings
++ * @buf_used:         Pointer to bytes used in @buf (updated on return)
++ * @buf_len:          Total size of @buf
++ * Return: 0 on success, -ve on error
++ */
++static int fit_config_add_hash(const void *fit, int image_noffset,
++                             char **node_inc, int *count, int max_nodes,
++                             char *buf, int *buf_used, int buf_len)
++{
++      int noffset, hash_count, ret, len;
++
++      if (*count >= max_nodes)
++              return -ENOSPC;
++
++      ret = fdt_get_path(fit, image_noffset, buf + *buf_used,
++                         buf_len - *buf_used);
++      if (ret < 0)
++              return -ENOENT;
++      len = strlen(buf + *buf_used) + 1;
++      node_inc[(*count)++] = buf + *buf_used;
++      *buf_used += len;
++
++      /* Add all this image's hash subnodes */
++      hash_count = 0;
++      for (noffset = fdt_first_subnode(fit, image_noffset);
++           noffset >= 0;
++           noffset = fdt_next_subnode(fit, noffset)) {
++              const char *name = fit_get_name(fit, noffset, NULL);
++
++              if (strncmp(name, FIT_HASH_NODENAME,
++                          strlen(FIT_HASH_NODENAME)))
++                      continue;
++              if (*count >= max_nodes)
++                      return -ENOSPC;
++              ret = fdt_get_path(fit, noffset, buf + *buf_used,
++                                 buf_len - *buf_used);
++              if (ret < 0)
++                      return -ENOENT;
++              len = strlen(buf + *buf_used) + 1;
++              node_inc[(*count)++] = buf + *buf_used;
++              *buf_used += len;
++              hash_count++;
++      }
++
++      if (!hash_count) {
++              printf("No hash nodes in image '%s'\n",
++                     fdt_get_name(fit, image_noffset, NULL));
++              return -ENOMSG;
++      }
++
++      /* Add this image's cipher node if present */
++      noffset = fdt_subnode_offset(fit, image_noffset, FIT_CIPHER_NODENAME);
++      if (noffset != -FDT_ERR_NOTFOUND) {
++              if (noffset < 0)
++                      return -EIO;
++              if (*count >= max_nodes)
++                      return -ENOSPC;
++              ret = fdt_get_path(fit, noffset, buf + *buf_used,
++                                 buf_len - *buf_used);
++              if (ret < 0)
++                      return -ENOENT;
++              len = strlen(buf + *buf_used) + 1;
++              node_inc[(*count)++] = buf + *buf_used;
++              *buf_used += len;
++      }
++
++      return 0;
++}
++
++/**
++ * fit_config_get_hash_list() - Build the list of nodes to hash
++ *
++ * Works through every image referenced by the configuration and collects the
++ * node paths: root + config + all referenced images with their hash and
++ * cipher subnodes.
++ *
++ * Properties known not to be image references (description, compatible,
++ * default, load-only) are skipped, so any new image type is covered by 
default.
++ *
++ * @fit:      FIT blob
++ * @conf_noffset: Configuration node offset
++ * @node_inc: Array to fill with path string pointers
++ * @max_nodes:        Size of @node_inc array
++ * @buf:      Buffer for packed null-terminated path strings
++ * @buf_len:  Size of @buf
++ * Return: number of entries in @node_inc, or -ve on error
++ */
++static int fit_config_get_hash_list(const void *fit, int conf_noffset,
++                                  char **node_inc, int max_nodes,
++                                  char *buf, int buf_len)
++{
++      const char *conf_name;
++      int image_count;
++      int prop_offset;
++      int used = 0;
++      int count = 0;
++      int ret, len;
++
++      conf_name = fit_get_name(fit, conf_noffset, NULL);
++
++      /* Always include the root node and the configuration node */
++      if (max_nodes < 2)
++              return -ENOSPC;
++
++      len = 2;  /* "/" + nul */
++      if (len > buf_len)
++              return -ENOSPC;
++      strcpy(buf, "/");
++      node_inc[count++] = buf;
++      used += len;
++
++      len = snprintf(buf + used, buf_len - used, "%s/%s", FIT_CONFS_PATH,
++                     conf_name) + 1;
++      if (used + len > buf_len)
++              return -ENOSPC;
++      node_inc[count++] = buf + used;
++      used += len;
++
++      /* Process each image referenced by the config */
++      image_count = 0;
++      fdt_for_each_property_offset(prop_offset, fit, conf_noffset) {
++              const char *prop_name;
++              int img_count, i;
++
++              fdt_getprop_by_offset(fit, prop_offset, &prop_name, NULL);
++              if (!prop_name)
++                      continue;
++
++              /* Skip properties that are not image references */
++              if (!strcmp(prop_name, FIT_DESC_PROP) ||
++                  !strcmp(prop_name, FIT_COMPAT_PROP) ||
++                  !strcmp(prop_name, FIT_DEFAULT_PROP))
++                      continue;
++
++              img_count = fdt_stringlist_count(fit, conf_noffset, prop_name);
++              for (i = 0; i < img_count; i++) {
++                      int noffset;
++
++                      noffset = fit_conf_get_prop_node_index(fit,
++                                                             conf_noffset,
++                                                             prop_name, i);
++                      if (noffset < 0)
++                              continue;
++
++                      ret = fit_config_add_hash(fit, noffset, node_inc,
++                                                &count, max_nodes, buf, &used,
++                                                buf_len);
++                      if (ret < 0)
++                              return ret;
++
++                      image_count++;
++              }
++      }
++
++      if (!image_count) {
++              printf("No images in config '%s'\n", conf_name);
++              return -ENOMSG;
++      }
++
++      return count;
++}
++
+ /**
+  * fit_config_check_sig() - Check the signature of a config
+  *
+@@ -264,20 +438,16 @@ static int fit_config_check_sig(const void *fit, int 
noffset, int conf_noffset,
+               FIT_DATA_POSITION_PROP,
+               FIT_DATA_OFFSET_PROP,
+       };
+-
+-      const char *prop, *end, *name;
++      char *node_inc[IMAGE_MAX_HASHED_NODES];
++      char hash_buf[FIT_MAX_HASH_PATH_BUF];
+       struct image_sign_info info;
+       const uint32_t *strings;
+-      const char *config_name;
+       uint8_t *fit_value;
+       int fit_value_len;
+-      bool found_config;
+       int max_regions;
+-      int i, prop_len;
+       char path[200];
+       int count;
+ 
+-      config_name = fit_get_name(fit, conf_noffset, NULL);
+       debug("%s: fdt=%p, conf='%s', sig='%s'\n", __func__, key_blob,
+             fit_get_name(fit, noffset, NULL),
+             fit_get_name(key_blob, required_keynode, NULL));
+@@ -292,45 +462,12 @@ static int fit_config_check_sig(const void *fit, int 
noffset, int conf_noffset,
+               return -1;
+       }
+ 
+-      /* Count the number of strings in the property */
+-      prop = fdt_getprop(fit, noffset, "hashed-nodes", &prop_len);
+-      end = prop ? prop + prop_len : prop;
+-      for (name = prop, count = 0; name < end; name++)
+-              if (!*name)
+-                      count++;
+-      if (!count) {
+-              *err_msgp = "Can't get hashed-nodes property";
+-              return -1;
+-      }
+-
+-      if (prop && prop_len > 0 && prop[prop_len - 1] != '\0') {
+-              *err_msgp = "hashed-nodes property must be null-terminated";
+-              return -1;
+-      }
+-
+-      /* Add a sanity check here since we are using the stack */
+-      if (count > IMAGE_MAX_HASHED_NODES) {
+-              *err_msgp = "Number of hashed nodes exceeds maximum";
+-              return -1;
+-      }
+-
+-      /* Create a list of node names from those strings */
+-      char *node_inc[count];
+-
+-      debug("Hash nodes (%d):\n", count);
+-      found_config = false;
+-      for (name = prop, i = 0; name < end; name += strlen(name) + 1, i++) {
+-              debug("   '%s'\n", name);
+-              node_inc[i] = (char *)name;
+-              if (!strncmp(FIT_CONFS_PATH, name, strlen(FIT_CONFS_PATH)) &&
+-                  name[sizeof(FIT_CONFS_PATH) - 1] == '/' &&
+-                  !strcmp(name + sizeof(FIT_CONFS_PATH), config_name)) {
+-                      debug("      (found config node %s)", config_name);
+-                      found_config = true;
+-              }
+-      }
+-      if (!found_config) {
+-              *err_msgp = "Selected config not in hashed nodes";
++      /* Build the node list from the config, ignoring hashed-nodes */
++      count = fit_config_get_hash_list(fit, conf_noffset,
++                                       node_inc, IMAGE_MAX_HASHED_NODES,
++                                       hash_buf, sizeof(hash_buf));
++      if (count < 0) {
++              *err_msgp = "Failed to build hash node list";
+               return -1;
+       }
+ 
+diff --git a/doc/usage/fit/signature.rst b/doc/usage/fit/signature.rst
+index b868dcb..e8465d8 100644
+--- a/doc/usage/fit/signature.rst
++++ b/doc/usage/fit/signature.rst
+@@ -353,20 +353,27 @@ meantime.
+ Details
+ -------
+ The signature node contains a property ('hashed-nodes') which lists all the
+-nodes that the signature was made over.  The image is walked in order and each
+-tag processed as follows:
++nodes that the signature was made over.  The signer (mkimage) writes this
++property as a record of what was included in the hash.  During verification,
++however, U-Boot does not read 'hashed-nodes'. Instead it rebuilds the node
++list from the configuration's own image references (kernel, fdt, ramdisk,
++etc.), since 'hashed-nodes' is not itself covered by the signature. The
++rebuilt list always includes the root node, the configuration node, each
++referenced image node and its hash/cipher subnodes.
++
++The image is walked in order and each tag processed as follows:
+ 
+ DTB_BEGIN_NODE
+     The tag and the following name are included in the signature
+-    if the node or its parent are present in 'hashed-nodes'
++    if the node or its parent are present in the node list
+ 
+ DTB_END_NODE
+     The tag is included in the signature if the node or its parent
+-    are present in 'hashed-nodes'
++    are present in the node list
+ 
+ DTB_PROPERTY
+     The tag, the length word, the offset in the string table, and
+-    the data are all included if the current node is present in 'hashed-nodes'
++    the data are all included if the current node is present in the node list
+     and the property name is not 'data'.
+ 
+ DTB_END
+@@ -374,7 +381,7 @@ DTB_END
+ 
+ DTB_NOP
+     The tag is included in the signature if the current node is present
+-    in 'hashed-nodes'
++    in the node list
+ 
+ In addition, the signature contains a property 'hashed-strings' which contains
+ the offset and length in the string table of the strings that are to be
+diff --git a/test/py/tests/test_vboot.py b/test/py/tests/test_vboot.py
+index 7e0e8e4..47ca359 100644
+--- a/test/py/tests/test_vboot.py
++++ b/test/py/tests/test_vboot.py
+@@ -362,10 +362,14 @@ def test_vboot(u_boot_console, name, sha_algo, padding, 
sign_options, required,
+             shutil.copyfile(fit, efit)
+             vboot_evil.add_evil_node(fit, efit, evil_kernel, 'kernel@')
+ 
+-            msg = 'Signature checking prevents use of unit addresses (@) in 
nodes'
+-            util.run_and_log_expect_exception(
++            # fit_check_sign catches this via signature mismatch (the @
++            # node is hashed instead of the real one)
++            utils.run_and_log_expect_exception(
+                 cons, [fit_check_sign, '-f', efit, '-k', dtb],
+-                1, msg)
++                1, 'Failed to verify required signature')
++
++            # bootm catches it earlier, at fit_check_format() time
++            msg = 'Signature checking prevents use of unit addresses (@) in 
nodes'
+             run_bootm(sha_algo, 'evil kernel@', msg, False, efit)
+ 
+         # Create a new properly signed fit and replace header bytes
diff -Nru u-boot-2025.01/debian/patches/CVE-2026-46728-3.patch 
u-boot-2025.01/debian/patches/CVE-2026-46728-3.patch
--- u-boot-2025.01/debian/patches/CVE-2026-46728-3.patch        1970-01-01 
01:00:00.000000000 +0100
+++ u-boot-2025.01/debian/patches/CVE-2026-46728-3.patch        2026-06-24 
11:06:59.000000000 +0200
@@ -0,0 +1,134 @@
+From: Tom Rini <[email protected]>
+Date: Wed, 18 Mar 2026 11:02:33 -0600
+Subject: tests: FIT: Add "clone" image attack image test
+
+Related to the problem resolved with commit 2092322b31cc ("boot: Add
+fit_config_get_hash_list() to build signed node list"), add a testcase
+for the problem as well.
+
+Reported-by: Apple Security Engineering and Architecture (SEAR)
+Signed-off-by: Tom Rini <[email protected]>
+(cherry picked from commit a22e9e1b8ec7c96664072d7e629e811c318fb92a)
+---
+ test/py/tests/test_vboot.py | 10 +++++++
+ test/py/tests/vboot_evil.py | 65 +++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 75 insertions(+)
+
+diff --git a/test/py/tests/test_vboot.py b/test/py/tests/test_vboot.py
+index 47ca359..f6919df 100644
+--- a/test/py/tests/test_vboot.py
++++ b/test/py/tests/test_vboot.py
+@@ -372,6 +372,16 @@ def test_vboot(u_boot_console, name, sha_algo, padding, 
sign_options, required,
+             msg = 'Signature checking prevents use of unit addresses (@) in 
nodes'
+             run_bootm(sha_algo, 'evil kernel@', msg, False, efit)
+ 
++            # Try doing a clone of the images
++            efit = '%stest.evilclone.fit' % tmpdir
++            shutil.copyfile(fit, efit)
++            vboot_evil.add_evil_node(fit, efit, evil_kernel, 'clone')
++
++            utils.run_and_log_expect_exception(
++                cons, [fit_check_sign, '-f', efit, '-k', dtb],
++                1, 'Failed to verify required signature')
++            run_bootm(sha_algo, 'evil clone', 'Bad Data Hash', False, efit)
++
+         # Create a new properly signed fit and replace header bytes
+         make_fit('sign-configs-%s%s.its' % (sha_algo, padding), cons, 
mkimage, dtc_args, datadir, fit)
+         sign_fit(sha_algo, sign_options)
+diff --git a/test/py/tests/vboot_evil.py b/test/py/tests/vboot_evil.py
+index e2b0cd6..5720631 100644
+--- a/test/py/tests/vboot_evil.py
++++ b/test/py/tests/vboot_evil.py
+@@ -14,6 +14,7 @@ FDT_END = 0x9
+ 
+ FAKE_ROOT_ATTACK = 0
+ KERNEL_AT = 1
++IMAGE_CLONE = 2
+ 
+ MAGIC = 0xd00dfeed
+ 
+@@ -274,6 +275,66 @@ def get_prop_value(dt_struct, dt_strings, prop_path):
+ 
+     return tag_data
+ 
++def image_clone_attack(dt_struct, dt_strings, kernel_content, kernel_hash):
++    # retrieve the default configuration name
++    default_conf_name = get_prop_value(
++        dt_struct, dt_strings, '/configurations/default')
++    default_conf_name = str(default_conf_name[:-1], 'utf-8')
++
++    conf_path = '/configurations/' + default_conf_name
++
++    # fetch the loaded kernel name from the default configuration
++    loaded_kernel = get_prop_value(dt_struct, dt_strings, conf_path + 
'/kernel')
++
++    loaded_kernel = str(loaded_kernel[:-1], 'utf-8')
++
++    # since this is the last child in images!
++    loaded_fdt_name = get_prop_value(dt_struct, dt_strings, conf_path + 
'/fdt')
++
++    loaded_fdt_name = str(loaded_fdt_name[:-1], 'utf-8')
++
++     # determine boundaries of the images
++    (img_node_start, img_node_end) = (determine_offset(
++        dt_struct, dt_strings, '/images'))
++    if img_node_start is None and img_node_end is None:
++        print('Fatal error, unable to find images node')
++        sys.exit()
++
++    # copy the images node
++    img_node_copy = dt_struct[img_node_start:img_node_end]
++
++    # create an additional empty node
++    empty_node = struct.pack('>I', FDT_BEGIN_NODE) + b"EMPTYNO\0" + 
struct.pack('>I', FDT_END_NODE)
++    # right before the end, we add it!
++    img_node_copy = img_node_copy[:-4] + empty_node + img_node_copy[-4:]
++
++    # insert the copy inside the tree
++    dt_struct = dt_struct[:img_node_end-4] + \
++        img_node_copy + empty_node + dt_struct[img_node_end-4:]
++
++    # change the content of the kernel being loaded
++    dt_struct = change_property_value(
++        dt_struct, dt_strings, '/images/' + loaded_kernel + '/data', 
kernel_content)
++
++    # change the content of the kernel being loaded
++    dt_struct = change_property_value(
++        dt_struct, dt_strings, '/images/' + loaded_kernel + '/hash-1/value', 
kernel_hash)
++
++    # finally, the main bug: change the hashed nodes to use the images clone 
instead!
++    hashed_nodes: bytes = get_prop_value(dt_struct, dt_strings, conf_path + 
'/signature/hashed-nodes')
++    print(f"got hashed nodes: {hashed_nodes}")
++    nodes = hashed_nodes.split(b"\0")
++    patched_nodes = []
++    for node in nodes:
++        new_node = node
++        if node.startswith(b"/images/"):
++            # reparent the node
++            new_node = b"/images" + node
++        patched_nodes.append(new_node)
++    hashed_nodes = b"\0".join(patched_nodes)
++    dt_struct = change_property_value(
++        dt_struct, dt_strings, conf_path + '/signature/hashed-nodes', 
hashed_nodes)
++    return dt_struct
+ 
+ def kernel_at_attack(dt_struct, dt_strings, kernel_content, kernel_hash):
+     """Conduct the kernel@ attack
+@@ -419,6 +480,8 @@ def add_evil_node(in_fname, out_fname, kernel_fname, 
attack):
+         attack = FAKE_ROOT_ATTACK
+     elif attack == 'kernel@':
+         attack = KERNEL_AT
++    elif attack == 'clone':
++        attack = IMAGE_CLONE
+     else:
+         raise ValueError('Unknown attack name!')
+ 
+@@ -455,6 +518,8 @@ def add_evil_node(in_fname, out_fname, kernel_fname, 
attack):
+     elif attack == KERNEL_AT:
+         dt_struct = kernel_at_attack(dt_struct, dt_strings, kernel_content,
+                                      hash_digest)
++    elif attack == IMAGE_CLONE:
++        dt_struct = image_clone_attack(dt_struct, dt_strings, kernel_content, 
hash_digest)
+ 
+     # now rebuild the new file
+     size_dt_strings = len(dt_strings)
diff -Nru u-boot-2025.01/debian/patches/series 
u-boot-2025.01/debian/patches/series
--- u-boot-2025.01/debian/patches/series        2025-03-18 08:16:11.000000000 
+0100
+++ u-boot-2025.01/debian/patches/series        2026-06-24 11:05:43.000000000 
+0200
@@ -1,13 +1,11 @@
 arndale/board-spl-rule.diff
-
 test-imagetools-test-fixes
-
 exynos/0001-arm-config-fix-default-console-only-to-specify-the-d.patch
-
 disable-fit-image-tests
-
 qemu/efi-secure-boot.patch
-
 Makefile-Use-relative-paths-for-debugging-symbols.patch
-
 sitara/Don-t-attempt-to-build-final-firmware-images.patch
+CVE-2024-42040.patch
+CVE-2026-46728-1.patch
+CVE-2026-46728-2.patch
+CVE-2026-46728-3.patch

Reply via email to