]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
boot: Introduce helper macros for offset checking 21922/head
authorJan Janssen <medhefgo@web.de>
Wed, 29 Dec 2021 13:35:07 +0000 (14:35 +0100)
committerJan Janssen <medhefgo@web.de>
Wed, 29 Dec 2021 13:39:54 +0000 (14:39 +0100)
This fixes a subtle sizeof overflow on 32bit machines.

src/boot/efi/bcd.c

index 85569deb09e7d8e1068725f96dfe68052e3bea1b..3eaabdd5387b7083edaf5aad46517edc69787fb7 100644 (file)
@@ -112,20 +112,30 @@ assert_cc(offsetof(KeyValue, data_offset) == 8);
 assert_cc(offsetof(KeyValue, data_type) == 12);
 assert_cc(offsetof(KeyValue, name) == 20);
 
+#define BAD_OFFSET(offset, len, max) \
+        ((UINT64) (offset) + (len) >= (max))
+
+#define BAD_STRUCT(type, offset, max) \
+        ((UINT64) (offset) + sizeof(type) >= (max))
+
+#define BAD_ARRAY(type, array, offset, array_len, max) \
+        ((UINT64) (offset) + offsetof(type, array) + \
+         sizeof((type){}.array[0]) * (UINT64) (array_len) >= (max))
+
 static const Key *get_key(const UINT8 *bcd, UINT32 bcd_len, UINT32 offset, const CHAR8 *name);
 
 static const Key *get_subkey(const UINT8 *bcd, UINT32 bcd_len, UINT32 offset, const CHAR8 *name) {
         assert(bcd);
         assert(name);
 
-        if ((UINT64) offset + sizeof(SubkeyFast) >= bcd_len)
+        if (BAD_STRUCT(SubkeyFast, offset, bcd_len))
                 return NULL;
 
         const SubkeyFast *subkey = (const SubkeyFast *) (bcd + offset);
         if (subkey->sig != SIG_SUBKEY_FAST)
                 return NULL;
 
-        if ((UINT64) offset + offsetof(SubkeyFast, entries) + sizeof(struct SubkeyFastEntry[subkey->n_entries]) >= bcd_len)
+        if (BAD_ARRAY(SubkeyFast, entries, offset, subkey->n_entries, bcd_len))
                 return NULL;
 
         for (UINT16 i = 0; i < subkey->n_entries; i++) {
@@ -147,14 +157,14 @@ static const Key *get_key(const UINT8 *bcd, UINT32 bcd_len, UINT32 offset, const
         assert(bcd);
         assert(name);
 
-        if ((UINT64) offset + sizeof(Key) >= bcd_len)
+        if (BAD_STRUCT(Key, offset, bcd_len))
                 return NULL;
 
         const Key *key = (const Key *) (bcd + offset);
         if (key->sig != SIG_KEY)
                 return NULL;
 
-        if ((UINT64) offset + offsetof(Key, key_name) + sizeof(CHAR8[key->key_name_len]) >= bcd_len)
+        if (BAD_ARRAY(Key, key_name, offset, key->key_name_len, bcd_len))
                 return NULL;
 
         if (*name) {
@@ -176,7 +186,7 @@ static const KeyValue *get_key_value(const UINT8 *bcd, UINT32 bcd_len, const Key
         if (key->n_key_values == 0)
                 return NULL;
 
-        if ((UINT64) key->key_values_offset + sizeof(UINT32[key->n_key_values]) >= bcd_len ||
+        if (BAD_OFFSET(key->key_values_offset, sizeof(UINT32) * (UINT64) key->n_key_values, bcd_len) ||
             (UINTN)(bcd + key->key_values_offset) % sizeof(UINT32) != 0)
                 return NULL;
 
@@ -184,14 +194,14 @@ static const KeyValue *get_key_value(const UINT8 *bcd, UINT32 bcd_len, const Key
         for (UINT32 i = 0; i < key->n_key_values; i++) {
                 UINT32 offset = *(key_value_list + i);
 
-                if ((UINT64) offset + sizeof(KeyValue) >= bcd_len)
+                if (BAD_STRUCT(KeyValue, offset, bcd_len))
                         continue;
 
                 const KeyValue *kv = (const KeyValue *) (bcd + offset);
                 if (kv->sig != SIG_KEY_VALUE)
                         continue;
 
-                if ((UINT64) offset + offsetof(KeyValue, name) + kv->name_len >= bcd_len)
+                if (BAD_ARRAY(KeyValue, name, offset, kv->name_len, bcd_len))
                         continue;
 
                 /* If most significant bit is set, data is stored in data_offset itself, but
@@ -200,7 +210,7 @@ static const KeyValue *get_key_value(const UINT8 *bcd, UINT32 bcd_len, const Key
                 if (FLAGS_SET(kv->data_size, UINT32_C(1) << 31))
                         continue;
 
-                if ((UINT64) kv->data_offset + kv->data_size >= bcd_len)
+                if (BAD_OFFSET(kv->data_offset, kv->data_size, bcd_len))
                         continue;
 
                 if (strncaseeqa(name, kv->name, kv->name_len) && strlena(name) == kv->name_len)