Skip to content

Commit 3545def

Browse files
committed
binfmt_elf: Honor PT_LOAD alignment for static PIE
The p_align values in PT_LOAD were ignored for static PIE executables (i.e. ET_DYN without PT_INTERP). This is because there is no way to request a non-fixed mmap region with a specific alignment. ET_DYN with PT_INTERP uses a separate base address (ELF_ET_DYN_BASE) and binfmt_elf performs the ASLR itself, which means it can also apply alignment. For the mmap region, the address selection happens deep within the vm_mmap() implementation (when the requested address is 0). The earlier attempt to implement this: commit 9630f0d ("fs/binfmt_elf: use PT_LOAD p_align values for static PIE") commit 925346c ("fs/binfmt_elf: fix PT_LOAD p_align values for loaders") did not take into account the different base address origins, and were eventually reverted: aeb7923 ("revert "fs/binfmt_elf: use PT_LOAD p_align values for static PIE"") In order to get the correct alignment from an mmap base, binfmt_elf must perform a 0-address load first, then tear down the mapping and perform alignment on the resulting address. Since this is slightly more overhead, only do this when it is needed (i.e. the alignment is not the default ELF alignment). This does, however, have the benefit of being able to use MAP_FIXED_NOREPLACE, to avoid potential collisions. With this fixed, enable the static PIE self tests again. Reported-by: H.J. Lu <[email protected]> Closes: https://bugzilla.kernel.org/show_bug.cgi?id=215275 Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Kees Cook <[email protected]>
1 parent 2d4cf7b commit 3545def

File tree

2 files changed

+38
-6
lines changed

2 files changed

+38
-6
lines changed

fs/binfmt_elf.c

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,10 +1088,13 @@ static int load_elf_binary(struct linux_binprm *bprm)
10881088
goto out_free_dentry;
10891089
}
10901090

1091+
/* Calculate any requested alignment. */
1092+
alignment = maximum_alignment(elf_phdata, elf_ex->e_phnum);
1093+
10911094
/*
10921095
* There are effectively two types of ET_DYN
1093-
* binaries: programs (i.e. PIE: ET_DYN with INTERP)
1094-
* and loaders (ET_DYN without INTERP, since they
1096+
* binaries: programs (i.e. PIE: ET_DYN with PT_INTERP)
1097+
* and loaders (ET_DYN without PT_INTERP, since they
10951098
* _are_ the ELF interpreter). The loaders must
10961099
* be loaded away from programs since the program
10971100
* may otherwise collide with the loader (especially
@@ -1111,15 +1114,44 @@ static int load_elf_binary(struct linux_binprm *bprm)
11111114
* without MAP_FIXED nor MAP_FIXED_NOREPLACE).
11121115
*/
11131116
if (interpreter) {
1117+
/* On ET_DYN with PT_INTERP, we do the ASLR. */
11141118
load_bias = ELF_ET_DYN_BASE;
11151119
if (current->flags & PF_RANDOMIZE)
11161120
load_bias += arch_mmap_rnd();
1117-
alignment = maximum_alignment(elf_phdata, elf_ex->e_phnum);
1121+
/* Adjust alignment as requested. */
11181122
if (alignment)
11191123
load_bias &= ~(alignment - 1);
11201124
elf_flags |= MAP_FIXED_NOREPLACE;
1121-
} else
1122-
load_bias = 0;
1125+
} else {
1126+
/*
1127+
* For ET_DYN without PT_INTERP, we rely on
1128+
* the architectures's (potentially ASLR) mmap
1129+
* base address (via a load_bias of 0).
1130+
*
1131+
* When a large alignment is requested, we
1132+
* must do the allocation at address "0" right
1133+
* now to discover where things will load so
1134+
* that we can adjust the resulting alignment.
1135+
* In this case (load_bias != 0), we can use
1136+
* MAP_FIXED_NOREPLACE to make sure the mapping
1137+
* doesn't collide with anything.
1138+
*/
1139+
if (alignment > ELF_MIN_ALIGN) {
1140+
load_bias = elf_load(bprm->file, 0, elf_ppnt,
1141+
elf_prot, elf_flags, total_size);
1142+
if (BAD_ADDR(load_bias)) {
1143+
retval = IS_ERR_VALUE(load_bias) ?
1144+
PTR_ERR((void*)load_bias) : -EINVAL;
1145+
goto out_free_dentry;
1146+
}
1147+
vm_munmap(load_bias, total_size);
1148+
/* Adjust alignment as requested. */
1149+
if (alignment)
1150+
load_bias &= ~(alignment - 1);
1151+
elf_flags |= MAP_FIXED_NOREPLACE;
1152+
} else
1153+
load_bias = 0;
1154+
}
11231155

11241156
/*
11251157
* Since load_bias is used for all subsequent loading

tools/testing/selftests/exec/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ CFLAGS += -D_GNU_SOURCE
66
ALIGNS := 0x1000 0x200000 0x1000000
77
ALIGN_PIES := $(patsubst %,load_address.%,$(ALIGNS))
88
ALIGN_STATIC_PIES := $(patsubst %,load_address.static.%,$(ALIGNS))
9-
ALIGNMENT_TESTS := $(ALIGN_PIES)
9+
ALIGNMENT_TESTS := $(ALIGN_PIES) $(ALIGN_STATIC_PIES)
1010

1111
TEST_PROGS := binfmt_script.py
1212
TEST_GEN_PROGS := execveat non-regular $(ALIGNMENT_TESTS)

0 commit comments

Comments
 (0)