Skip to content

Commit e0943c4

Browse files
akawashirogregkh
authored andcommitted
fs/binfmt_elf: Fix AT_PHDR for unusual ELF files
[ Upstream commit 0da1d50 ] 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 <[email protected]> Reported-by: kernel test robot <[email protected]> Acked-by: Kees Cook <[email protected]> Signed-off-by: Kees Cook <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Sasha Levin <[email protected]>
1 parent 757322b commit e0943c4

File tree

1 file changed

+18
-6
lines changed

1 file changed

+18
-6
lines changed

fs/binfmt_elf.c

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,8 @@ static int padzero(unsigned long elf_bss)
170170

171171
static int
172172
create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
173-
unsigned long load_addr, unsigned long interp_load_addr,
174-
unsigned long e_entry)
173+
unsigned long interp_load_addr,
174+
unsigned long e_entry, unsigned long phdr_addr)
175175
{
176176
struct mm_struct *mm = current->mm;
177177
unsigned long p = bprm->p;
@@ -257,7 +257,7 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
257257
NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP);
258258
NEW_AUX_ENT(AT_PAGESZ, ELF_EXEC_PAGESIZE);
259259
NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC);
260-
NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff);
260+
NEW_AUX_ENT(AT_PHDR, phdr_addr);
261261
NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr));
262262
NEW_AUX_ENT(AT_PHNUM, exec->e_phnum);
263263
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,
823823
static int load_elf_binary(struct linux_binprm *bprm)
824824
{
825825
struct file *interpreter = NULL; /* to shut gcc up */
826-
unsigned long load_addr = 0, load_bias = 0;
826+
unsigned long load_addr, load_bias = 0, phdr_addr = 0;
827827
int load_addr_set = 0;
828828
unsigned long error;
829829
struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata = NULL;
@@ -1156,6 +1156,17 @@ static int load_elf_binary(struct linux_binprm *bprm)
11561156
reloc_func_desc = load_bias;
11571157
}
11581158
}
1159+
1160+
/*
1161+
* Figure out which segment in the file contains the Program
1162+
* Header table, and map to the associated memory address.
1163+
*/
1164+
if (elf_ppnt->p_offset <= elf_ex->e_phoff &&
1165+
elf_ex->e_phoff < elf_ppnt->p_offset + elf_ppnt->p_filesz) {
1166+
phdr_addr = elf_ex->e_phoff - elf_ppnt->p_offset +
1167+
elf_ppnt->p_vaddr;
1168+
}
1169+
11591170
k = elf_ppnt->p_vaddr;
11601171
if ((elf_ppnt->p_flags & PF_X) && k < start_code)
11611172
start_code = k;
@@ -1191,6 +1202,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
11911202
}
11921203

11931204
e_entry = elf_ex->e_entry + load_bias;
1205+
phdr_addr += load_bias;
11941206
elf_bss += load_bias;
11951207
elf_brk += load_bias;
11961208
start_code += load_bias;
@@ -1254,8 +1266,8 @@ static int load_elf_binary(struct linux_binprm *bprm)
12541266
goto out;
12551267
#endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */
12561268

1257-
retval = create_elf_tables(bprm, elf_ex,
1258-
load_addr, interp_load_addr, e_entry);
1269+
retval = create_elf_tables(bprm, elf_ex, interp_load_addr,
1270+
e_entry, phdr_addr);
12591271
if (retval < 0)
12601272
goto out;
12611273

0 commit comments

Comments
 (0)