{"uuid": "bf6cc3f3-cdce-43f5-900f-f3d5ba1f244d", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2022-20498", "type": "seen", "source": "https://gist.github.com/Daniel-Pereira-Linux/2ab9fd72e0d6bd2f06ef4c920b17219e", "content": "# Security Advisory: OOB read/write in libfdt overlay application via unchecked `__local_fixups__` offsets\n\n- **Discovered by:** Daniel Pereira \n- **Component:** libfdt (Device Tree Compiler / dtc), `libfdt/fdt_overlay.c`\n- **Affected:** `dgibson/dtc` main branch (verified at commit `7a1e017`) and the\n  copy vendored as `scripts/dtc/libfdt` in the Linux source tree.\n- **NOT affected:** the Linux kernel *runtime* overlay path (`drivers/of/`),\n  which is a separate implementation.\n- **Type:** CWE-787 Out-of-bounds Write, CWE-125 Out-of-bounds Read\n- **Status:** confirmed by upstream maintainer (David Gibson) on 2026-09-20.\n- **Fix:** see `0001-libfdt-overlay-bounds-check-__local_fixups__-offsets.patch`\n\n## Summary\n\nWhen libfdt applies a device-tree overlay, it reads phandle-fixup offsets from\nthe overlay's `/__local_fixups__` node and uses each 32-bit offset to compute a\npointer into a target property, **without validating the offset against the\ntarget property length**. A crafted overlay therefore yields an out-of-bounds\nread and a conditional 4-byte out-of-bounds write relative to the overlay blob.\n\n`FDT_RO_PROBE` only validates the header and structural block bounds \u2014 it does\n**not** validate `/__local_fixups__` offset values.\n\n## Vulnerable code\n\n`libfdt/fdt_overlay.c`, two locations, both of the form:\n\n```c\nrefp = (fdt32_t *)(tree_val + fdt32_ld_(fixup_val + i));  /* offset unchecked */\n```\n\n1. `overlay_update_local_node_references()` \u2014 OOB read + unconditional OOB write:\n\n   ```c\n   refp = (fdt32_t *)(tree_val + fdt32_ld_(fixup_val + i));\n   fdt32_st(refp, fdt32_ld(refp) + delta);\n   ```\n\n2. `overlay_update_node_conflicting_references()` \u2014 OOB read + conditional OOB write:\n\n   ```c\n   refp = (fdt32_t *)(tree_val + fdt32_ld_(fixup_val + i));\n   valp = fdt32_ld(refp);\n   if (valp == fdto_phandle)\n           fdt32_st(refp, fdt_phandle);\n   ```\n\nIn both, `fdt32_ld_(fixup_val + i)` is an attacker-controlled `u32` taken\nverbatim from `/__local_fixups__`; nothing enforces\n`offset + sizeof(fdt32_t) &lt;= tree_len`.\n\n## Reachability\n\nFrom `fdt_overlay_apply(fdt, fdto)`:\n\n- `overlay_update_local_references()` -&gt; `overlay_update_local_node_references()`\n  \u2014 location (1); triggers for **any** `/__local_fixups__` entry.\n- `overlay_prevent_phandle_overwrite()` -&gt; `overlay_update_node_conflicting_references()`\n  \u2014 location (2); triggers on a phandle conflict between overlay and base tree.\n\n## Impact and trust boundary\n\n4-byte out-of-bounds write at an attacker-controlled offset, plus out-of-bounds\nread, relative to the overlay blob \u2014 a heap-corruption primitive, not merely a\nmalformed-hardware description.\n\nThis crosses a real trust boundary wherever an overlay from an **untrusted**\nsource is applied on top of a **trusted** base with libfdt, notably:\n\n- **Bootloaders (e.g. U-Boot)** applying an **unsigned** overlay onto a\n  **signed** base device tree under verified/secure boot. The base is\n  authenticated; the overlay is attacker-controlled. \"Lying about the hardware\"\n  stays inside the device-tree model; an OOB write corrupts memory outside the\n  blob \u2014 a categorically stronger primitive.\n- The `fdtoverlay` command-line tool.\n- Any application linking libfdt that applies overlays from untrusted input.\n\n## Prior art\n\nNo prior CVE/report was found for this specific `/__local_fixups__` OOB. Known\nlibfdt CVEs are in unrelated functions (CVE-2022-20454 `fdt_next_tag`,\nCVE-2022-20498 `fdt_property`).\n\n## Provenance\n\nLocation (1) was introduced by commit `24f60011` (\"libfdt: Add phandle-based\nfixup functions\"); location (2) was copied from it.\n\n## Proof of concept\n\nA minimal overlay built with the libfdt sw (serial-write) API, containing a\n`/__local_fixups__` entry whose offset is e.g. `0x10000000`, drives\n`overlay_update_local_node_references()` to dereference `tree_val + 0x10000000`.\n(PoC / ASan trace available on request from the discoverer.)\n\n## Fix\n\nBounds check the offset against the target property length with an\noverflow-safe unsigned comparison before forming the pointer, rejecting the\noverlay with `-FDT_ERR_BADOVERLAY` otherwise. See attached patch.\n\n## Suggested CVSS (estimate)\n\nCVSS:3.1 **AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:H** ~ **8.0 (High)** for the\nbootloader/secure-boot deployment. Score varies by consumer; for the offline\n`fdtoverlay` tool, UI:R lowers it. This is an estimate for the CNA to confirm.", "creation_timestamp": "2026-09-20T14:09:13.080858Z"}