[Devel] [PATCH RHEL9 COMMIT] oracle/elf: reintroduce using MAP_FIXED_NOREPLACE for elf executable mappings
Konstantin Khorenko
khorenko at virtuozzo.com
Thu Jan 23 23:35:45 MSK 2025
The commit is pushed to "branch-rh9-5.14.0-427.44.1.vz9.80.x-ovz" and will appear at git at bitbucket.org:openvz/vzkernel.git
after rh9-5.14.0-427.44.1.vz9.80.5
------>
commit 606584a3e4aea5dcdce233a013c5e9e809d58c7a
Author: Anthony Yznaga <anthony.yznaga at oracle.com>
Date: Wed Jul 22 15:12:16 2020 -0700
oracle/elf: reintroduce using MAP_FIXED_NOREPLACE for elf executable mappings
Commit b212921b13bd ("elf: don't use MAP_FIXED_NOREPLACE for elf
executable mappings") reverted back to using MAP_FIXED to map elf load
segments because it was found that the load segments in some binaries
overlap and can cause MAP_FIXED_NOREPLACE to fail. The original intent
of MAP_FIXED_NOREPLACE was to prevent the silent clobbering of an
existing mapping (e.g. the stack) by the elf image. To achieve this,
expand on the logic used when loading ET_DYN binaries which calculates a
total size for the image when the first segment is mapped, maps the
entire image, and then unmaps the remainder before remaining segments
are mapped. Apply this to ET_EXEC binaries as well as ET_DYN binaries
as is done now, and for both ET_EXEC and ET_DYN+INTERP use
MAP_FIXED_NOREPLACE for the initial total size mapping and MAP_FIXED for
remaining mappings. For ET_DYN w/out INTERP, continue to map at a
system-selected address in the mmap region.
Orabug: 32387875
Signed-off-by: Anthony Yznaga <anthony.yznaga at oracle.com>
Reviewed-by: Liam R. Howlett <Liam.Howlett at Oracle.com>
https://virtuozzo.atlassian.net/browse/VSTOR-96305
https://lore.kernel.org/lkml/1595869887-23307-1-git-send-email-anthony.yznaga@oracle.com/
The patch series has been backported from Oracle Unbrakable Linux git:
https://github.com/oracle/linux-uek/tree/v5.15.0-300.163.18
(cherry picked from Oracle commit 30189feec70a89277181cfe4e06a250522e8d71d)
Signed-off-by: Konstantin Khorenko <khorenko at virtuozzo.com>
Feature: oracle/mm: MADV_DOEXEC madvise() flag
---
fs/binfmt_elf.c | 118 ++++++++++++++++++++++++++++++++------------------------
1 file changed, 67 insertions(+), 51 deletions(-)
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index c4f8afdb7df8..a930f0005a4f 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -1075,61 +1075,25 @@ static int load_elf_binary(struct linux_binprm *bprm)
vaddr = elf_ppnt->p_vaddr;
/*
- * If we are loading ET_EXEC or we have already performed
- * the ET_DYN load_addr calculations, proceed normally.
+ * Map remaining segments with MAP_FIXED once the first
+ * total size mapping has been done.
*/
- if (elf_ex->e_type == ET_EXEC || load_addr_set) {
+ if (load_addr_set) {
elf_flags |= MAP_FIXED;
- } else if (elf_ex->e_type == ET_DYN) {
- /*
- * This logic is run once for the first LOAD Program
- * Header for ET_DYN binaries to calculate the
- * randomization (load_bias) for all the LOAD
- * Program Headers, and to calculate the entire
- * size of the ELF mapping (total_size). (Note that
- * load_addr_set is set to true later once the
- * initial mapping is performed.)
- *
- * There are effectively two types of ET_DYN
- * binaries: programs (i.e. PIE: ET_DYN with INTERP)
- * and loaders (ET_DYN without INTERP, since they
- * _are_ the ELF interpreter). The loaders must
- * be loaded away from programs since the program
- * may otherwise collide with the loader (especially
- * for ET_EXEC which does not have a randomized
- * position). For example to handle invocations of
- * "./ld.so someprog" to test out a new version of
- * the loader, the subsequent program that the
- * loader loads must avoid the loader itself, so
- * they cannot share the same load range. Sufficient
- * room for the brk must be allocated with the
- * loader as well, since brk must be available with
- * the loader.
- *
- * Therefore, programs are loaded offset from
- * ELF_ET_DYN_BASE and loaders are loaded into the
- * independently randomized mmap region (0 load_bias
- * without MAP_FIXED).
- */
- if (interpreter) {
- load_bias = ELF_ET_DYN_BASE;
- if (current->flags & PF_RANDOMIZE)
- load_bias += arch_mmap_rnd();
- alignment = maximum_alignment(elf_phdata, elf_ex->e_phnum);
- if (alignment)
- load_bias &= ~(alignment - 1);
- elf_flags |= MAP_FIXED;
- } else
- load_bias = 0;
-
+ } else {
/*
- * Since load_bias is used for all subsequent loading
- * calculations, we must lower it by the first vaddr
- * so that the remaining calculations based on the
- * ELF vaddrs will be correctly offset. The result
- * is then page aligned.
+ * To ensure loading does not continue if an ELF
+ * LOAD segment overlaps an existing mapping (e.g.
+ * the stack), for the first LOAD Program Header
+ * calculate the the entire size of the ELF mapping
+ * and map it with MAP_FIXED_NOREPLACE. On success,
+ * the remainder will be unmapped and subsequent
+ * LOAD segments mapped with MAP_FIXED rather than
+ * MAP_FIXED_NOREPLACE because some binaries may
+ * have overlapping segments that would cause the
+ * mmap to fail
*/
- load_bias = ELF_PAGESTART(load_bias - vaddr);
+ elf_flags |= MAP_FIXED_NOREPLACE;
total_size = total_mapping_size(elf_phdata,
elf_ex->e_phnum);
@@ -1137,6 +1101,58 @@ static int load_elf_binary(struct linux_binprm *bprm)
retval = -EINVAL;
goto out_free_dentry;
}
+
+ if (elf_ex->e_type == ET_DYN) {
+ /*
+ * This logic is run once for the first LOAD
+ * Program Header for ET_DYN binaries to
+ * calculate the randomization (load_bias) for
+ * all the LOAD Program Headers.
+ *
+ * There are effectively two types of ET_DYN
+ * binaries: programs (i.e. PIE: ET_DYN with
+ * INTERP) and loaders (ET_DYN without INTERP,
+ * since they _are_ the ELF interpreter). The
+ * loaders must be loaded away from programs
+ * since the program may otherwise collide with
+ * the loader (especially for ET_EXEC which does
+ * not have a randomized position). For example
+ * to handle invocations of "./ld.so someprog"
+ * to test out a new version of the loader, the
+ * subsequent program that the loader loads must
+ * avoid the loader itself, so they cannot share
+ * the same load range. Sufficient room for the
+ * brk must be allocated with the loader as
+ * well, since brk must be available with the
+ * loader.
+ *
+ * Therefore, programs are loaded offset from
+ * ELF_ET_DYN_BASE and loaders are loaded into
+ * the independently randomized mmap region
+ * (0 load_bias without MAP_FIXED*).
+ */
+ if (interpreter) {
+ load_bias = ELF_ET_DYN_BASE;
+ if (current->flags & PF_RANDOMIZE)
+ load_bias += arch_mmap_rnd();
+ alignment = maximum_alignment(elf_phdata, elf_ex->e_phnum);
+ if (alignment)
+ load_bias &= ~(alignment - 1);
+ } else {
+ load_bias = 0;
+ elf_flags &= ~MAP_FIXED_NOREPLACE;
+ }
+
+ /*
+ * Since load_bias is used for all subsequent
+ * loading calculations, we must lower it by
+ * the first vaddr so that the remaining
+ * calculations based on the ELF vaddrs will
+ * be correctly offset. The result is then
+ * page aligned.
+ */
+ load_bias = ELF_PAGESTART(load_bias - vaddr);
+ }
}
error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
More information about the Devel
mailing list