> So scripts/dtc/libfdt/fdt_ro.c comes from https://github.com/dgibson/dtc
> and should be sent upstream there too (and all of scripts/dtc/ is, for
> reference for the other patches), so that we don't drop this part when
> re-syncing later via the linux kernel.

The patch for fdt_check_full() was added in the upstream dtc code back in
2022:
https://github.com/dgibson/dtc/commit/fda71da26e7fa00966a58afeb2114269b5512e59

It was never ported to U-Boot, most likely because the function was moved
from fdt_ro.c to the fdt_check.c.
Linux kernel does not use fdt_check_full(), and thus it is not affected.

We also updated the BRLY-2026-037_BRLY-2026-038.patch to align with the
current dtc logic:
https://github.com/dgibson/dtc/commit/50454658f2b5c8968ccd7856d94020c893a4be46


> Next, please rewrite the commit message to be concise and follow best
> practices and examples found throughout the codebase. This applies to
> all of the patches you posted, thanks.

We sent the new version of patches with the updated commit messages.

Thank you,
Anton


On Fri, May 22, 2026 at 7:32 PM Tom Rini <[email protected]> wrote:

> On Fri, May 22, 2026 at 01:30:20PM +0100, Anton Ivanov wrote:
>
> > From: Binarly Vulnerability Research <[email protected]>
> >
> > fdt_get_name() in scripts/dtc/libfdt/fdt_ro.c can return a null
> > pointer. This happens when the fdt_ro_probe_() or
> > fdt_check_node_offset_() check fails, as well as when the
> > '!can_assume(LATEST) && fdt_version(fdt) < 0x10' condition is true
> > and the FDT node name doesn't contain the '/' character:
> >
> >       const char *fdt_get_name(const void *fdt, int nodeoffset, int *len)
> >       {
> >               const struct fdt_node_header *nh = fdt_offset_ptr_(fdt,
> nodeoffset);
> >               const char *nameptr;
> >               int err;
> >
> >               if (((err = fdt_ro_probe_(fdt)) < 0)
> >                       || ((err = fdt_check_node_offset_(fdt,
> nodeoffset)) < 0))
> >                               goto fail;
> >
> >               nameptr = nh->name;
> >
> >               if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
> >                       /*
> >                       * For old FDT versions, match the naming
> conventions of V16:
> >                       * give only the leaf name (after all /). The
> actual tree
> >                       * contents are loosely checked.
> >                       */
> >                       const char *leaf;
> >                       leaf = strrchr(nameptr, '/');
> >                       if (leaf == NULL) {
> >                               err = -FDT_ERR_BADSTRUCTURE;
> >                               goto fail;
> >                       }
> >                       nameptr = leaf+1;
> >               }
> >
> >               if (len)
> >                       *len = strlen(nameptr);
> >
> >               return nameptr;
> >
> >       fail:
> >               if (len)
> >                       *len = err;
> >               return NULL;
> >       }
> >
> > The fdt_get_name() function is used in fdt_find_regions() in
> > boot/fdt_region.c:
> >
> >       int fdt_find_regions(const void *fdt, char * const inc[], int
> inc_count,
> >                               char * const exc_prop[], int
> exc_prop_count,
> >                               struct fdt_region region[], int
> max_regions,
> >                               char *path, int path_len, int
> add_string_tab)
> >       {
> >               int stack[FDT_MAX_DEPTH] = { 0 };
> >               char *end;
> >               int nextoffset = 0;
> >               uint32_t tag;
> >               int count = 0;
> >               int start = -1;
> >               int depth = -1;
> >               int want = 0;
> >               int base = fdt_off_dt_struct(fdt);
> >               bool expect_end = false;
> >
> >               end = path;
> >               *end = '\0';
> >               do {
> >                       const struct fdt_property *prop;
> >                       const char *name;
> >                       const char *str;
> >                       int include = 0;
> >                       int stop_at = 0;
> >                       int offset;
> >                       int len;
> >
> >                       offset = nextoffset;
> >                       tag = fdt_next_tag(fdt, offset, &nextoffset);
> >                       stop_at = nextoffset;
> >
> >                       /* If we see two root nodes, something is wrong */
> >                       if (expect_end && tag != FDT_END)
> >                               return -FDT_ERR_BADLAYOUT;
> >
> >                       switch (tag) {
> >                       case FDT_PROP:
> >                               include = want >= 2;
> >                               stop_at = offset;
> >                               prop = fdt_get_property_by_offset(fdt,
> offset, NULL);
> >                               str = fdt_string(fdt,
> fdt32_to_cpu(prop->nameoff));
> >                               if (!str)
> >                                       return -FDT_ERR_BADSTRUCTURE;
> >                               if (str_in_list(str, exc_prop,
> exc_prop_count))
> >                                       include = 0;
> >                               break;
> >
> >                       case FDT_NOP:
> >                               include = want >= 2;
> >                               stop_at = offset;
> >                               break;
> >
> >                       case FDT_BEGIN_NODE:
> >                               depth++;
> >                               if (depth == FDT_MAX_DEPTH)
> >                                       return -FDT_ERR_BADSTRUCTURE;
> >                               name = fdt_get_name(fdt, offset, &len);
> >
> >                               /* The root node must have an empty name */
> >                               if (!depth && *name)
> >                                       return -FDT_ERR_BADLAYOUT;
> >                               if (end - path + 2 + len >= path_len)
> >                                       return -FDT_ERR_NOSPACE;
> >                               if (end != path + 1)
> >                                       *end++ = '/';
> >                               strcpy(end, name);
> >                               end += len;
> >                               stack[depth] = want;
> >                               if (want == 1)
> >                                       stop_at = offset;
> >                               if (str_in_list(path, inc, inc_count))
> >                                       want = 2;
> >                               else if (want)
> >                                       want--;
> >                               else
> >                                       stop_at = offset;
> >                               include = want;
> >                               break;
> >                       ...
> >
> > There is no check that the value returned by fdt_get_name() is not
> > null, which leads to a null pointer dereference (at *name and
> > strcpy(end, name)) and a potential stack overflow of the 'end'
> > buffer. However, if the strcpy call succeeds, the subsequent
> > 'end += len' decreases the 'end' pointer, since 'len' is set to the
> > negative error code returned by fdt_get_name(). As a result, the next
> > iteration of the loop writes data to a stack location before the
> > start of the 'path' buffer, which can be used to overwrite the
> > return address of fdt_find_regions() and achieve code execution in
> > the context of the bootloader. This attack is possible when the page
> > at address 0x0 is mapped, which is common for embedded devices, so
> > the strcpy() call doesn't fail.
> >
> > The same lack of validation exists in fdt_next_region() in
> > boot/fdt_region.c, fdt_check_full() in scripts/dtc/libfdt/fdt_ro.c,
> > and display_fdt_by_regions() in tools/fdtgrep.c.
> >
> > Fix: Validate both the returned pointer and the length before use.
> >
> > Signed-off-by: Binarly Vulnerability Research <[email protected]>
> > ---
> >  boot/fdt_region.c           | 4 ++++
> >  scripts/dtc/libfdt/fdt_ro.c | 2 ++
> >  tools/fdtgrep.c             | 2 ++
> >  3 files changed, 8 insertions(+)
>
> So scripts/dtc/libfdt/fdt_ro.c comes from https://github.com/dgibson/dtc
> and should be sent upstream there too (and all of scripts/dtc/ is, for
> reference for the other patches), so that we don't drop this part when
> re-syncing later via the linux kernel.
>
> Next, please rewrite the commit message to be concise and follow best
> practices and examples found throughout the codebase. This applies to
> all of the patches you posted, thanks.
>
> --
> Tom
>

Reply via email to