F-5747: guard fdt_data_size_ and fdt_splice_string_ against off_dt_strings+size_dt_strings uint32_t overflow

fdt_data_size_() added the two FDT header uint32_t fields without overflow
protection; a crafted FDT with off_dt_strings+size_dt_strings>=2^32 caused
the sum to wrap to zero.  On 32-bit MMU targets (Cortex-M, RV32, PPC32) the
pointer arithmetic in fdt_splice_string_ and fdt_find_add_string_ also
wraps, placing 'p' and 'new' at the start of the FDT buffer.  All bounds
checks in fdt_splice_ then pass (p==end==fdt, oldlen==0, newlen<totalsize),
and the subsequent memcpy writes the property-name string directly over the
FDT header, corrupting magic, totalsize, and struct offsets.

Fix by computing the sum in 64-bit in fdt_data_size_ and returning
-FDT_ERR_BADOFFSET on overflow; add a symmetric early-return overflow check
in fdt_splice_string_ before the pointer is formed; and propagate the error
through fdt_shrink so it does not silently store a zero totalsize.
pull/792/head
Daniele Lacamera 2026-06-10 14:12:14 +02:00
parent 34231739a5
commit f7e773333e
2 changed files with 38 additions and 4 deletions

View File

@ -96,7 +96,11 @@ static inline int fdt_data_size_(void *fdt)
{
/* the last portion of a FDT is the DT string, so use its offset and size to
* determine total size */
return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
uint64_t off = (uint64_t)fdt_off_dt_strings(fdt);
uint64_t sz = (uint64_t)fdt_size_dt_strings(fdt);
if (off + sz > (uint64_t)UINT32_MAX)
return -FDT_ERR_BADOFFSET;
return (int)(off + sz);
}
static const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len)
@ -329,7 +333,13 @@ static int fdt_resize_property_(void *fdt, int nodeoffset, const char *name,
static int fdt_splice_string_(void *fdt, int newlen)
{
int err;
void *p = (char*)fdt + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt);
uint32_t off = fdt_off_dt_strings(fdt);
uint32_t sz = fdt_size_dt_strings(fdt);
void *p;
if (sz > UINT32_MAX - off)
return -FDT_ERR_BADOFFSET;
p = (char*)fdt + off + sz;
if ((err = fdt_splice_(fdt, p, 0, newlen))) {
return err;
@ -794,8 +804,10 @@ int fdt_del_node(void *fdt, int nodeoffset)
/* adjust the actual total size in the FDT header */
int fdt_shrink(void* fdt)
{
uint32_t total_size = fdt_data_size_(fdt);
return fdt_set_totalsize(fdt, total_size);
int total_size = fdt_data_size_(fdt);
if (total_size < 0)
return total_size;
return fdt_set_totalsize(fdt, (uint32_t)total_size);
}
/* FTD Fixup API's */

View File

@ -136,6 +136,27 @@ START_TEST(test_fit_load_image_rejects_oversized_prop_len)
}
END_TEST
/* off_dt_strings=4, size_dt_strings=0xFFFFFFFC: sum overflows uint32_t to 0.
* Before the fix, fdt_data_size_() returned 0 and fdt_shrink() silently set
* totalsize=0. After the fix fdt_shrink() must return an error and leave
* totalsize unchanged. */
START_TEST(test_fdt_shrink_rejects_dt_strings_area_overflow)
{
static uint8_t buf[256];
int rc;
memset(buf, 0, sizeof(buf));
fdt_set_totalsize(buf, sizeof(buf));
fdt_set_off_dt_strings(buf, 4);
fdt_set_size_dt_strings(buf, 0xFFFFFFFC);
rc = fdt_shrink(buf);
ck_assert_int_lt(rc, 0);
ck_assert_uint_eq(fdt_totalsize(buf), sizeof(buf));
}
END_TEST
static Suite *fdt_suite(void)
{
Suite *s = suite_create("fdt");
@ -144,6 +165,7 @@ static Suite *fdt_suite(void)
tcase_add_test(tc, test_fdt_get_string_rejects_out_of_range_offset);
tcase_add_test(tc, test_fdt_get_string_returns_string_with_valid_offset);
tcase_add_test(tc, test_fit_load_image_rejects_oversized_prop_len);
tcase_add_test(tc, test_fdt_shrink_rejects_dt_strings_area_overflow);
suite_add_tcase(s, tc);
return s;