From: Binarly Vulnerability Research <[email protected]>

The fit_image_get_data() function in boot/image-fit.c resolves the
address and size of an image's payload. For "external" data, i.e.
images that declare data-position or data-offset rather than
carrying an inline 'data' property, it takes both the offset and the
size straight from FIT properties and returns them to the caller:

        int fit_image_get_data(const void *fit, int noffset, const void **data,
                                size_t *size)
        {
                bool external_data = false;
                int offset;
                int len;
                int ret;

                if (!fit_image_get_data_position(fit, noffset, &offset)) {
                        external_data = true;
                } else if (!fit_image_get_data_offset(fit, noffset, &offset)) {
                        external_data = true;
                        /*
                        * For FIT with external data, figure out where
                        * the external images start. This is the base
                        * for the data-offset properties in each image.
                        */
                        offset += ((fdt_totalsize(fit) + 3) & ~3);
                }

                if (external_data) {
                        debug("External Data\n");
                        ret = fit_image_get_data_size(fit, noffset, &len);
                        if (!ret) {
                                *data = fit + offset;
                                *size = len;
                        }
                } else {
                        ret = fit_image_get_emb_data(fit, noffset, data, size);
                }

                return ret;
        }

All three properties (data-position, data-offset, and data-size)
are attacker-controlled, and there is no validation of the size
against the FIT image bounds. Moreover, all three properties may
take negative values since they are read as signed int (although
during the hash calculation they are later cast to unsigned int).

The obtained 'data' and 'size' are then used to calculate the hash
of the image for signature verification. 'info->crypto->verify' for
'sha1,rsa2048' is rsa_verify(), located in lib/rsa/rsa-verify.c,
which calls 'info->checksum->calculate' (set to hash_calculate()
for SHA-based algorithms in boot/image-sig.c), defined in
lib/hash-checksum.c:

        for (i = 0; i < region_count - 1; i++) {
                ret = algo->hash_update(algo, ctx, region[i].data,
                                        region[i].size, 0);
                if (ret)
                        return ret;
        }

hash_update_sha1(), defined in common/hash.c, takes the per-region
address and size as an unsigned int and feeds it directly to
sha1_update(). If an attacker manipulates data-position or
data-offset, it is possible to trigger an out-of-bounds read by
pointing the region address beyond the mapped memory. Similarly,
with data-size manipulation, an out-of-bounds read can be triggered
by specifying a large region size that causes the hash calculation
to read beyond the intended memory region.

Fix: Validate the external data offset and size. Validate that the
external data is within the bounds of the FIT.

Signed-off-by: Binarly Vulnerability Research <[email protected]>
---
 boot/image-fit.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/boot/image-fit.c b/boot/image-fit.c
index b0fcaf6e17f..4d0e8ffc79f 100644
--- a/boot/image-fit.c
+++ b/boot/image-fit.c
@@ -1084,8 +1084,24 @@ int fit_image_get_data(const void *fit, int noffset, 
const void **data,
 
        if (external_data) {
                debug("External Data\n");
+               if (offset < 0 || offset > UINTPTR_MAX - (uintptr_t)fit) {
+                       printf("Invalid external data offset: %d\n", offset);
+                       return -1;
+               }
+
                ret = fit_image_get_data_size(fit, noffset, &len);
                if (!ret) {
+                       if (len < 0) {
+                               printf("Invalid external data size: %d\n", len);
+                               return -1;
+                       }
+#if CONFIG_IS_ENABLED(FIT_SIGNATURE)
+                       if (len > CONFIG_VAL(FIT_SIGNATURE_MAX_SIZE) - offset) {
+                               printf("FIT external data is out of bounds 
(offset=%d, size=%d)\n",
+                                      offset, len);
+                               return -1;
+                       }
+#endif
                        *data = fit + offset;
                        *size = len;
                }
-- 
2.53.0

Reply via email to