summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2026-09-04 12:22:49 -0600
committerTom Rini <[email protected]>2026-09-04 16:55:10 -0600
commit5534f2c742916efabff3e1888912dafc570f20f2 (patch)
tree1ad0e57528769aef86a2cb7e3a4297495125d255 /test
parent7bf077ec5733a230bbce34d8ce324f92d8c46cc1 (diff)
parent3158b844335a4a84a5504a813f2daa6acff5985d (diff)
Merge patch series "fit: Harden handling of external-data properties"
Anton Ivanov <[email protected]> says: The data-offset, data-position and data-size FIT properties are 32-bit unsigned values, but were read through signed int. Also, they are excluded from the configuration signature, so they are attacker controlled. Patch 1 switches the accessors and their callers to u32, removing the ad-hoc handling of "negative" values in U-Boot proper. This transition was discussed and agreed on in [1]. The SPL loader has its own copy of this logic with the same problems and fewer checks. Patch 2 factors out a test helper, patch 3 makes the SPL offset/size arithmetic overflow-safe, and patch 4 adds the addressable-range and FIT_SIGNATURE_MAX_SIZE bounds check that U-Boot proper already performs in fit_image_get_data(). Patches 3 and 4 build on each other and on patch 1, so they are not intended to be cherry-picked individually. [1] https://lore.kernel.org/u-boot/CAPWaX55XWFLcMGRuaUuXXn__MX_UG4J8QTd6rvXZmJ4WSOCy9w@mail.gmail.com/ Link: https://lore.kernel.org/r/[email protected]
Diffstat (limited to 'test')
-rw-r--r--test/image/spl_load.c128
-rw-r--r--test/py/tests/test_vboot.py10
2 files changed, 114 insertions, 24 deletions
diff --git a/test/image/spl_load.c b/test/image/spl_load.c
index c43c977f784..334ec77b18e 100644
--- a/test/image/spl_load.c
+++ b/test/image/spl_load.c
@@ -368,55 +368,145 @@ SPL_IMG_TEST(spl_test_image, FIT_INTERNAL, 0);
SPL_IMG_TEST(spl_test_image, FIT_EXTERNAL, 0);
/*
- * A FIT image's data-size property is not covered by the configuration
- * signature, so it is untrusted input. load_simple_fit() must reject a
- * data-size larger than the destination rather than overrun it, because the
- * device read happens before the image hash is verified.
+ * Build a FIT with external data, overwrite one property of the image node
+ * with a hostile value and check that loading fails with the expected error.
+ * The external-data properties are excluded from the configuration signature,
+ * so load_simple_fit() must reject values that would wrap its offset/size
+ * arithmetic rather than read from a bogus location.
*/
-static int spl_test_fit_external_oversize(struct unit_test_state *uts)
+static int check_fit_ext_prop(struct unit_test_state *uts, const char *prop,
+ u32 value, uint bl_len, spl_load_reader h_read,
+ ulong fit_offset, int expected)
{
size_t img_size, img_data, data_size = SPL_TEST_DATA_SIZE;
struct spl_image_info info_write = {
- .name = "oversize",
+ .name = "ext-prop",
.size = data_size,
}, info_read = { };
struct spl_load_info load;
void *img;
int node;
- if (!image_supported(FIT_EXTERNAL))
- return -EAGAIN;
-
img_size = create_image(NULL, FIT_EXTERNAL, &info_write, &img_data);
ut_assert(img_size);
img = calloc(img_size, 1);
ut_assertnonnull(img);
- generate_data(img + img_data, data_size, "oversize");
+ generate_data(img + img_data, data_size, "ext-prop");
ut_asserteq(img_size, create_image(img, FIT_EXTERNAL, &info_write,
NULL));
- /*
- * Inflate data-size far beyond the image buffer and any plausible
- * load region. Without a bounds check, load_simple_fit() reads this
- * many bytes off the "device" before the hash is checked.
- */
node = fdt_path_offset(img, FIT_IMAGES_PATH);
ut_assert(node >= 0);
node = fdt_first_subnode(img, node);
ut_assert(node >= 0);
- ut_assertok(fdt_setprop_inplace_u32(img, node, FIT_DATA_SIZE_PROP,
- 0x40000000));
+ ut_assertok(fdt_setprop_inplace_u32(img, node, prop, value));
- spl_load_init(&load, spl_test_read, img, 1);
- ut_asserteq(-EFBIG, spl_load_simple_fit(&info_read, &load, 0, img));
+ spl_load_init(&load, h_read, img, bl_len);
+ ut_asserteq(expected,
+ spl_load_simple_fit(&info_read, &load, fit_offset, img));
free(img);
return 0;
}
+
+/*
+ * A FIT image's data-size property is not covered by the configuration
+ * signature, so it is untrusted input. load_simple_fit() must reject a
+ * data-size larger than the destination rather than overrun it, because the
+ * device read happens before the image hash is verified.
+ */
+static int spl_test_fit_external_oversize(struct unit_test_state *uts)
+{
+ if (!image_supported(FIT_EXTERNAL))
+ return -EAGAIN;
+
+ return check_fit_ext_prop(uts, FIT_DATA_SIZE_PROP, 0x40000000, 1,
+ spl_test_read, 0, -EFBIG);
+}
SPL_TEST(spl_test_fit_external_oversize, 0);
/*
+ * A data-offset which wraps past UINT32_MAX once the external-data base
+ * offset is added must be rejected before it is used as a read offset.
+ */
+static int spl_test_fit_data_offset_overflow(struct unit_test_state *uts)
+{
+ if (!image_supported(FIT_EXTERNAL))
+ return -EAGAIN;
+
+ return check_fit_ext_prop(uts, FIT_DATA_OFFSET_PROP, 0xffffffff, 1,
+ spl_test_read, 0, -EINVAL);
+}
+SPL_TEST(spl_test_fit_data_offset_overflow, 0);
+
+/*
+ * A data-size whose block-aligned read size wraps past ULONG_MAX must be
+ * rejected. Since data-size is a 32-bit property, the wrap is only reachable
+ * when ulong is 32 bits wide, so skip the test on other targets.
+ */
+static int spl_test_fit_data_size_overflow(struct unit_test_state *uts)
+{
+ if (!image_supported(FIT_EXTERNAL) || sizeof(ulong) != 4)
+ return -EAGAIN;
+
+ return check_fit_ext_prop(uts, FIT_DATA_SIZE_PROP, 0xffffffff, 2,
+ spl_test_read, 0, -EOVERFLOW);
+}
+SPL_TEST(spl_test_fit_data_size_overflow, 0);
+
+/* Device offset the reader pretends the FIT was loaded from */
+static ulong spl_test_fit_offset;
+
+static ulong spl_test_read_fit_offset(struct spl_load_info *load, ulong sector,
+ ulong count, void *buf)
+{
+ return spl_test_read(load, sector - spl_test_fit_offset, count, buf);
+}
+
+/*
+ * An aligned external-data offset which wraps past ULONG_MAX once the FIT's
+ * offset on the device is added must be rejected before it is used as a read
+ * offset.
+ */
+static int spl_test_fit_read_offset_overflow(struct unit_test_state *uts)
+{
+ if (!image_supported(FIT_EXTERNAL))
+ return -EAGAIN;
+
+ /* So that ULONG_MAX - fit_offset < the aligned external-data offset */
+ spl_test_fit_offset = ULONG_MAX - 0xfff;
+
+ return check_fit_ext_prop(uts, FIT_DATA_OFFSET_PROP, 0x1000, 1,
+ spl_test_read_fit_offset, spl_test_fit_offset,
+ -EINVAL);
+}
+SPL_TEST(spl_test_fit_read_offset_overflow, 0);
+
+/*
+ * A read whose end position (read_offset + size) wraps past the addressable
+ * range must be rejected.
+ */
+static int spl_test_fit_read_end_overflow(struct unit_test_state *uts)
+{
+ if (!image_supported(FIT_EXTERNAL))
+ return -EAGAIN;
+
+ /*
+ * The aligned external-data offset (0x2000 plus the size of the FIT
+ * itself) stays below the 0x2fff bytes remaining before ULONG_MAX, so
+ * read_offset passes the offset-wrap check above, but reading the
+ * SPL_TEST_DATA_SIZE bytes of data crosses past UINTPTR_MAX.
+ */
+ spl_test_fit_offset = ULONG_MAX - 0x2fff;
+
+ return check_fit_ext_prop(uts, FIT_DATA_OFFSET_PROP, 0x2000, 1,
+ spl_test_read_fit_offset, spl_test_fit_offset,
+ -EINVAL);
+}
+SPL_TEST(spl_test_fit_read_end_overflow, 0);
+
+/*
* LZMA is too complex to generate on the fly, so let's use some data I put in
* the oven^H^H^H^H compressed earlier
*/
diff --git a/test/py/tests/test_vboot.py b/test/py/tests/test_vboot.py
index 4b6707caf70..fd91b3f10a2 100644
--- a/test/py/tests/test_vboot.py
+++ b/test/py/tests/test_vboot.py
@@ -700,12 +700,12 @@ def test_vboot_ext_data_bounds(ubman):
fd.write(500 * b'\0')
testcases = [
- ('negative data-position',
- {'data-position': 0xffffffff}, 'Invalid external data position'),
- ('negative data-offset',
+ ('invalid data-position',
+ {'data-position': 0xffffffff}, 'FIT external data is out of bounds'),
+ ('invalid data-offset',
{'data-offset': 0xffffffff}, 'Invalid external data offset'),
- ('negative data-size',
- {'data-size': 0xffffffff}, 'Invalid external data size'),
+ ('invalid data-size',
+ {'data-size': 0xffffffff}, 'FIT external data is out of bounds'),
('off-bounds data-position',
{'data-position': 0x7fffffff}, 'FIT external data is out of bounds'),
('off-bounds data-offset',