Patch "fs/binfmt_elf: Fix AT_PHDR for unusual ELF files" has been added to the 5.15-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    fs/binfmt_elf: Fix AT_PHDR for unusual ELF files

to the 5.15-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     fs-binfmt_elf-fix-at_phdr-for-unusual-elf-files.patch
and it can be found in the queue-5.15 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit d3a6a714b699b02a849fc11b8911ca0e48d22119
Author: Akira Kawata <akirakawata1@xxxxxxxxx>
Date:   Thu Jan 27 21:40:16 2022 +0900

    fs/binfmt_elf: Fix AT_PHDR for unusual ELF files
    
    [ Upstream commit 0da1d5002745cdc721bc018b582a8a9704d56c42 ]
    
    BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=197921
    
    As pointed out in the discussion of buglink, we cannot calculate AT_PHDR
    as the sum of load_addr and exec->e_phoff.
    
    : The AT_PHDR of ELF auxiliary vectors should point to the memory address
    : of program header. But binfmt_elf.c calculates this address as follows:
    :
    : NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff);
    :
    : which is wrong since e_phoff is the file offset of program header and
    : load_addr is the memory base address from PT_LOAD entry.
    :
    : The ld.so uses AT_PHDR as the memory address of program header. In normal
    : case, since the e_phoff is usually 64 and in the first PT_LOAD region, it
    : is the correct program header address.
    :
    : But if the address of program header isn't equal to the first PT_LOAD
    : address + e_phoff (e.g.  Put the program header in other non-consecutive
    : PT_LOAD region), ld.so will try to read program header from wrong address
    : then crash or use incorrect program header.
    
    This is because exec->e_phoff
    is the offset of PHDRs in the file and the address of PHDRs in the
    memory may differ from it. This patch fixes the bug by calculating the
    address of program headers from PT_LOADs directly.
    
    Signed-off-by: Akira Kawata <akirakawata1@xxxxxxxxx>
    Reported-by: kernel test robot <lkp@xxxxxxxxx>
    Acked-by: Kees Cook <keescook@xxxxxxxxxxxx>
    Signed-off-by: Kees Cook <keescook@xxxxxxxxxxxx>
    Link: https://lore.kernel.org/r/20220127124014.338760-2-akirakawata1@xxxxxxxxx
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index a813b70f594e..3f6a7cac68fd 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -170,8 +170,8 @@ static int padzero(unsigned long elf_bss)
 
 static int
 create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
-		unsigned long load_addr, unsigned long interp_load_addr,
-		unsigned long e_entry)
+		unsigned long interp_load_addr,
+		unsigned long e_entry, unsigned long phdr_addr)
 {
 	struct mm_struct *mm = current->mm;
 	unsigned long p = bprm->p;
@@ -257,7 +257,7 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
 	NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP);
 	NEW_AUX_ENT(AT_PAGESZ, ELF_EXEC_PAGESIZE);
 	NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC);
-	NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff);
+	NEW_AUX_ENT(AT_PHDR, phdr_addr);
 	NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr));
 	NEW_AUX_ENT(AT_PHNUM, exec->e_phnum);
 	NEW_AUX_ENT(AT_BASE, interp_load_addr);
@@ -823,7 +823,7 @@ static int parse_elf_properties(struct file *f, const struct elf_phdr *phdr,
 static int load_elf_binary(struct linux_binprm *bprm)
 {
 	struct file *interpreter = NULL; /* to shut gcc up */
- 	unsigned long load_addr = 0, load_bias = 0;
+	unsigned long load_addr, load_bias = 0, phdr_addr = 0;
 	int load_addr_set = 0;
 	unsigned long error;
 	struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata = NULL;
@@ -1156,6 +1156,17 @@ static int load_elf_binary(struct linux_binprm *bprm)
 				reloc_func_desc = load_bias;
 			}
 		}
+
+		/*
+		 * Figure out which segment in the file contains the Program
+		 * Header table, and map to the associated memory address.
+		 */
+		if (elf_ppnt->p_offset <= elf_ex->e_phoff &&
+		    elf_ex->e_phoff < elf_ppnt->p_offset + elf_ppnt->p_filesz) {
+			phdr_addr = elf_ex->e_phoff - elf_ppnt->p_offset +
+				    elf_ppnt->p_vaddr;
+		}
+
 		k = elf_ppnt->p_vaddr;
 		if ((elf_ppnt->p_flags & PF_X) && k < start_code)
 			start_code = k;
@@ -1191,6 +1202,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
 	}
 
 	e_entry = elf_ex->e_entry + load_bias;
+	phdr_addr += load_bias;
 	elf_bss += load_bias;
 	elf_brk += load_bias;
 	start_code += load_bias;
@@ -1254,8 +1266,8 @@ static int load_elf_binary(struct linux_binprm *bprm)
 		goto out;
 #endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */
 
-	retval = create_elf_tables(bprm, elf_ex,
-			  load_addr, interp_load_addr, e_entry);
+	retval = create_elf_tables(bprm, elf_ex, interp_load_addr,
+				   e_entry, phdr_addr);
 	if (retval < 0)
 		goto out;
 



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux