]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
lib/buildid: take into account e_phoff when fetching program headers
authorAndrii Nakryiko <andrii@kernel.org>
Thu, 29 Aug 2024 17:42:25 +0000 (10:42 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Wed, 11 Sep 2024 16:58:30 +0000 (09:58 -0700)
Current code assumption is that program (segment) headers are following
ELF header immediately. This is a common case, but is not guaranteed. So
take into account e_phoff field of the ELF header when accessing program
headers.

Reviewed-by: Eduard Zingerman <eddyz87@gmail.com>
Reported-by: Alexey Dobriyan <adobriyan@gmail.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/r/20240829174232.3133883-4-andrii@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
lib/buildid.c

index bfe00b66b1e844ba8051a0fe9567dceaceb8a0f2..7fb08a1d98bd1aed36f994c19298df185fac0a6e 100644 (file)
@@ -213,28 +213,26 @@ static int get_build_id_32(struct freader *r, unsigned char *build_id, __u32 *si
 {
        const Elf32_Ehdr *ehdr;
        const Elf32_Phdr *phdr;
-       __u32 phnum, i;
+       __u32 phnum, phoff, i;
 
        ehdr = freader_fetch(r, 0, sizeof(Elf32_Ehdr));
        if (!ehdr)
                return r->err;
 
-       /*
-        * FIXME
-        * Neither ELF spec nor ELF loader require that program headers
-        * start immediately after ELF header.
-        */
-       if (ehdr->e_phoff != sizeof(Elf32_Ehdr))
-               return -EINVAL;
-
        /* subsequent freader_fetch() calls invalidate pointers, so remember locally */
        phnum = READ_ONCE(ehdr->e_phnum);
+       phoff = READ_ONCE(ehdr->e_phoff);
+
        /* only supports phdr that fits in one page */
        if (phnum > (PAGE_SIZE - sizeof(Elf32_Ehdr)) / sizeof(Elf32_Phdr))
                return -EINVAL;
 
+       /* check that phoff is not large enough to cause an overflow */
+       if (phoff + phnum * sizeof(Elf32_Phdr) < phoff)
+               return -EINVAL;
+
        for (i = 0; i < phnum; ++i) {
-               phdr = freader_fetch(r, i * sizeof(Elf32_Phdr), sizeof(Elf32_Phdr));
+               phdr = freader_fetch(r, phoff + i * sizeof(Elf32_Phdr), sizeof(Elf32_Phdr));
                if (!phdr)
                        return r->err;
 
@@ -252,27 +250,26 @@ static int get_build_id_64(struct freader *r, unsigned char *build_id, __u32 *si
        const Elf64_Ehdr *ehdr;
        const Elf64_Phdr *phdr;
        __u32 phnum, i;
+       __u64 phoff;
 
        ehdr = freader_fetch(r, 0, sizeof(Elf64_Ehdr));
        if (!ehdr)
                return r->err;
 
-       /*
-        * FIXME
-        * Neither ELF spec nor ELF loader require that program headers
-        * start immediately after ELF header.
-        */
-       if (ehdr->e_phoff != sizeof(Elf64_Ehdr))
-               return -EINVAL;
-
        /* subsequent freader_fetch() calls invalidate pointers, so remember locally */
        phnum = READ_ONCE(ehdr->e_phnum);
+       phoff = READ_ONCE(ehdr->e_phoff);
+
        /* only supports phdr that fits in one page */
        if (phnum > (PAGE_SIZE - sizeof(Elf64_Ehdr)) / sizeof(Elf64_Phdr))
                return -EINVAL;
 
+       /* check that phoff is not large enough to cause an overflow */
+       if (phoff + phnum * sizeof(Elf64_Phdr) < phoff)
+               return -EINVAL;
+
        for (i = 0; i < phnum; ++i) {
-               phdr = freader_fetch(r, i * sizeof(Elf64_Phdr), sizeof(Elf64_Phdr));
+               phdr = freader_fetch(r, phoff + i * sizeof(Elf64_Phdr), sizeof(Elf64_Phdr));
                if (!phdr)
                        return r->err;