Libfdt lookup semantics can match a node by its base name even when the
actual node name includes a unit address. As a result, a request for
"kernel" can resolve to a node named "kernel@1".

This aliasing was the basis of CVE-2021-27138, which was fixed by two
commits:

  commit 3f04db891a35 ("image: Check for unit addresses in FITs")
  commit 79af75f7776f ("fit: Don't allow verification of images with @
  nodes")

Both address it by rejecting any FIT node whose name contains '@'. That is
overly broad: it also breaks FIT images that legitimately use unit
addresses in their node names.

Harden the lookup itself instead. Add a fit_subnode_offset_exact() helper
that wraps fdt_subnode_offset(), reads the resolved node name back with
fdt_get_name() and rejects the match unless it is identical to the
requested string, so a config referencing "kernel" no longer resolves to
"kernel@1".

Use it in place of fdt_subnode_offset() at the FIT reference lookups in
fit_image_get_node(), fit_conf_get_node() and fit_conf_find_compat(), and
at the matching image lookups in the SPL FIT loader.

Signed-off-by: Lorenz Kofler <[email protected]>
---
 boot/image-fit.c     |  7 +++----
 common/spl/spl_fit.c |  4 ++--
 include/image.h      | 28 ++++++++++++++++++++++++++++
 3 files changed, 33 insertions(+), 6 deletions(-)

diff --git a/boot/image-fit.c b/boot/image-fit.c
index b0fcaf6e17f..a6504cd8041 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -678,7 +678,7 @@ int fit_image_get_node(const void *fit, const char 
*image_uname)
                return images_noffset;
        }
 
-       noffset = fdt_subnode_offset(fit, images_noffset, image_uname);
+       noffset = fit_subnode_offset_exact(fit, images_noffset, image_uname);
        if (noffset < 0) {
                debug("Can't get node offset for image unit name: '%s' (%s)\n",
                      image_uname, fdt_strerror(noffset));
@@ -1781,8 +1781,7 @@ int fit_conf_find_compat(const void *fit, const void *fdt)
                                debug("No fdt property found.\n");
                                continue;
                        }
-                       kfdt_noffset = fdt_subnode_offset(fit, images_noffset,
-                                                         kfdt_name);
+                       kfdt_noffset = fit_subnode_offset_exact(fit, 
images_noffset, kfdt_name);
                        if (kfdt_noffset < 0) {
                                debug("No image node named \"%s\" found.\n",
                                      kfdt_name);
@@ -1881,7 +1880,7 @@ int fit_conf_get_node(const void *fit, const char 
*conf_uname)
                conf_uname = conf_uname_copy;
        }
 
-       noffset = fdt_subnode_offset(fit, confs_noffset, conf_uname);
+       noffset = fit_subnode_offset_exact(fit, confs_noffset, conf_uname);
        if (noffset < 0) {
                debug("Can't get node offset for configuration unit name: '%s' 
(%s)\n",
                      conf_uname, fdt_strerror(noffset));
diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c
index 46ebcabe56a..baa0214fa46 100644
--- a/common/spl/spl_fit.c
+++ b/common/spl/spl_fit.c
@@ -166,7 +166,7 @@ static int spl_fit_get_image_node(const struct spl_fit_info 
*ctx,
 
        debug("%s: '%s'\n", type, str);
 
-       node = fdt_subnode_offset(ctx->fit, ctx->images_node, str);
+       node = fit_subnode_offset_exact(ctx->fit, ctx->images_node, str);
        if (node < 0) {
                pr_err("cannot find image node '%s': %d\n", str, node);
                return -EINVAL;
@@ -456,7 +456,7 @@ static int spl_fit_append_fdt(struct spl_image_info 
*spl_image,
                        if (ret)
                                continue;
 
-                       node = fdt_subnode_offset(ctx->fit, ctx->images_node, 
str);
+                       node = fit_subnode_offset_exact(ctx->fit, 
ctx->images_node, str);
                        if (node < 0) {
                                debug("%s: unable to find FDT node %d\n",
                                      __func__, index);
diff --git a/include/image.h b/include/image.h
index 34efac6056d..b3ddc9202a5 100644
--- a/include/image.h
+++ b/include/image.h
@@ -1156,6 +1156,34 @@ static inline const char *fit_get_name(const void 
*fit_hdr,
        return fdt_get_name(fit_hdr, noffset, len);
 }
 
+/**
+ * fit_subnode_offset_exact() - Find a subnode by exact node name
+ * @fit: FIT image
+ * @parent: Parent node offset
+ * @name: Exact child node name to look up
+ *
+ * fdt_subnode_offset() may ignore the unit-address suffix, so a request for
+ * "kernel" may resolve to a node named "kernel@1". Verify that the returned
+ * node name exactly matches the requested name.
+ *
+ * Return: node offset on success, negative libfdt error value otherwise
+ */
+static inline int fit_subnode_offset_exact(const void *fit, int parent, const 
char *name)
+{
+       const char *actual;
+       int noffset;
+
+       noffset = fdt_subnode_offset(fit, parent, name);
+       if (noffset < 0)
+               return noffset;
+
+       actual = fdt_get_name(fit, noffset, NULL);
+       if (!actual || strcmp(actual, name))
+               return -FDT_ERR_NOTFOUND;
+
+       return noffset;
+}
+
 int fit_get_desc(const void *fit, int noffset, char **desc);
 int fit_get_timestamp(const void *fit, int noffset, time_t *timestamp);
 
-- 
2.54.0

Reply via email to