Revision 2 of this patch, fixes wrong boundary checks of previous attempt.
Can definitely need some review here. :)


The function elf_get_mem properly checks if offset is smaller than elf->size,
but does not perform further memory size validations.

Supply desired size as third argument to elf_get_mem, which is in sync
with elf_get_uint/elf_set_uint then.

As you can see, further unbounded string operations like strlen are still
prone to lead to out-of-bounds access, which will be covered in another patch.
---
 libkmod/libkmod-elf.c | 54 +++++++++++++++++++++++++++++++++------------------
 1 file changed, 35 insertions(+), 19 deletions(-)

diff --git a/libkmod/libkmod-elf.c b/libkmod/libkmod-elf.c
index 2f50ad2..ebd3dea 100644
--- a/libkmod/libkmod-elf.c
+++ b/libkmod/libkmod-elf.c
@@ -197,12 +197,15 @@ static inline int elf_set_uint(struct kmod_elf *elf, 
uint64_t offset, uint64_t s
        return 0;
 }
 
-static inline const void *elf_get_mem(const struct kmod_elf *elf, uint64_t 
offset)
+static inline const void *elf_get_mem(const struct kmod_elf *elf, uint64_t 
offset, uint64_t size)
 {
-       assert(offset < elf->size);
-       if (offset >= elf->size) {
-               ELFDBG(elf, "out-of-bounds: %"PRIu64" >= %"PRIu64" (ELF 
size)\n",
-                      offset, elf->size);
+       uint64_t end;
+
+       assert(!addu64_overflow(offset, size, &end));
+       assert(end <= elf->size);
+       if (addu64_overflow(offset, size, &end) || end > elf->size) {
+               ELFDBG(elf, "out-of-bounds: %"PRIu64" + %"PRIu64" > %"PRIu64" 
(ELF size)\n",
+                      offset, size, elf->size);
                return NULL;
        }
        return elf->memory + offset;
@@ -218,7 +221,8 @@ static inline const void *elf_get_section_header(const 
struct kmod_elf *elf, uin
                return NULL;
        }
        return elf_get_mem(elf, elf->header.section.offset +
-                          idx * elf->header.section.entry_size);
+                          idx * elf->header.section.entry_size,
+                          elf->header.section.entry_size);
 }
 
 static inline int elf_get_section_info(const struct kmod_elf *elf, uint16_t 
idx, uint64_t *offset, uint64_t *size, uint32_t *nameoff)
@@ -266,7 +270,8 @@ static inline int elf_get_section_info(const struct 
kmod_elf *elf, uint16_t idx,
 static const char *elf_get_strings_section(const struct kmod_elf *elf, 
uint64_t *size)
 {
        *size = elf->header.strings.size;
-       return elf_get_mem(elf, elf->header.strings.offset);
+       return elf_get_mem(elf, elf->header.strings.offset,
+                          elf->header.strings.size);
 }
 
 struct kmod_elf *kmod_elf_new(const void *memory, off_t size)
@@ -307,11 +312,13 @@ struct kmod_elf *kmod_elf_new(const void *memory, off_t 
size)
        elf->header.strings.section = READV(e_shstrndx);        \
        elf->header.machine = READV(e_machine)
        if (elf->class & KMOD_ELF_32) {
-               const Elf32_Ehdr *hdr _unused_ = elf_get_mem(elf, 0);
+               size_t hdr_size = sizeof(Elf32_Ehdr);
+               const Elf32_Ehdr *hdr _unused_ = elf_get_mem(elf, 0, hdr_size);
                LOAD_HEADER;
                shdr_size = sizeof(Elf32_Shdr);
        } else {
-               const Elf64_Ehdr *hdr _unused_ = elf_get_mem(elf, 0);
+               size_t hdr_size = sizeof(Elf64_Ehdr);
+               const Elf64_Ehdr *hdr _unused_ = elf_get_mem(elf, 0, hdr_size);
                LOAD_HEADER;
                shdr_size = sizeof(Elf64_Shdr);
        }
@@ -417,7 +424,7 @@ int kmod_elf_get_section(const struct kmod_elf *elf, const 
char *section, const
                if (!streq(section, n))
                        continue;
 
-               *buf = elf_get_mem(elf, off);
+               *buf = elf_get_mem(elf, off, size);
                *buf_size = size;
                return 0;
        }
@@ -534,7 +541,8 @@ int kmod_elf_get_modversions(const struct kmod_elf *elf, 
struct kmod_modversion
        slen = 0;
 
        for (i = 0; i < count; i++, off += MODVERSION_SEC_SIZE) {
-               const char *symbol = elf_get_mem(elf, off + offcrc);
+               const char *symbol = elf_get_mem(elf, off + offcrc,
+                                                MODVERSION_SEC_SIZE - offcrc);
 
                if (symbol[0] == '.')
                        symbol++;
@@ -551,7 +559,8 @@ int kmod_elf_get_modversions(const struct kmod_elf *elf, 
struct kmod_modversion
 
        for (i = 0; i < count; i++, off += MODVERSION_SEC_SIZE) {
                uint64_t crc = elf_get_uint(elf, off, offcrc);
-               const char *symbol = elf_get_mem(elf, off + offcrc);
+               const char *symbol = elf_get_mem(elf, off + offcrc,
+                                                MODVERSION_SEC_SIZE - offcrc);
                size_t symbollen;
 
                if (symbol[0] == '.')
@@ -806,7 +815,8 @@ int kmod_elf_get_symbols(const struct kmod_elf *elf, struct 
kmod_modversion **ar
                        goto fallback;
                }
 
-               name = elf_get_mem(elf, str_off + name_off);
+               name = elf_get_mem(elf, str_off + name_off,
+                                  strtablen - name_off);
 
                if (strncmp(name, crc_str, crc_strlen) != 0)
                        continue;
@@ -846,7 +856,8 @@ int kmod_elf_get_symbols(const struct kmod_elf *elf, struct 
kmod_modversion **ar
                        info = READV(st_info);
                }
 #undef READV
-               name = elf_get_mem(elf, str_off + name_off);
+               name = elf_get_mem(elf, str_off + name_off,
+                                  strtablen - name_off);
                if (strncmp(name, crc_str, crc_strlen) != 0)
                        continue;
                name += crc_strlen;
@@ -889,7 +900,8 @@ static int kmod_elf_crc_find(const struct kmod_elf *elf, 
const void *versions, u
 
        off = (const uint8_t *)versions - elf->memory;
        for (i = 0; i < versionslen; i += verlen) {
-               const char *symbol = elf_get_mem(elf, off + i + crclen);
+               const char *symbol = elf_get_mem(elf, off + i + crclen,
+                                                verlen - crclen);
                if (!streq(name, symbol))
                        continue;
                *crc = elf_get_uint(elf, off + i, crclen);
@@ -1038,7 +1050,8 @@ int kmod_elf_get_dependency_symbols(const struct kmod_elf 
*elf, struct kmod_modv
                        return -EINVAL;
                }
 
-               name = elf_get_mem(elf, str_off + name_off);
+               name = elf_get_mem(elf, str_off + name_off,
+                                  strtablen - name_off);
                if (name[0] == '\0') {
                        ELFDBG(elf, "empty symbol name at index %"PRIu64"\n", 
i);
                        continue;
@@ -1059,7 +1072,8 @@ int kmod_elf_get_dependency_symbols(const struct kmod_elf 
*elf, struct kmod_modv
                for (i = 0; i < vercount; i++) {
                        if (visited_versions[i] == 0) {
                                const char *name;
-                               name = elf_get_mem(elf, ver_off + i * verlen + 
crclen);
+                               name = elf_get_mem(elf, ver_off + i * verlen + 
crclen,
+                                                  verlen - crclen);
                                slen += strlen(name) + 1;
 
                                count++;
@@ -1126,7 +1140,8 @@ int kmod_elf_get_dependency_symbols(const struct kmod_elf 
*elf, struct kmod_modv
                                continue;
                }
 
-               name = elf_get_mem(elf, str_off + name_off);
+               name = elf_get_mem(elf, str_off + name_off,
+                                  strtablen - name_off);
                if (name[0] == '\0') {
                        ELFDBG(elf, "empty symbol name at index %"PRIu64"\n", 
i);
                        continue;
@@ -1168,7 +1183,8 @@ int kmod_elf_get_dependency_symbols(const struct kmod_elf 
*elf, struct kmod_modv
                if (visited_versions[i] != 0)
                        continue;
 
-               name = elf_get_mem(elf, ver_off + i * verlen + crclen);
+               name = elf_get_mem(elf, ver_off + i * verlen + crclen,
+                                  verlen - crclen);
                slen = strlen(name);
                crc = elf_get_uint(elf, ver_off + i * verlen, crclen);
 
-- 
2.3.0

--
To unsubscribe from this list: send the line "unsubscribe linux-modules" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to