[03/14] rework maphole

Message ID 20230518082854.3903342-4-stsp2@yandex.ru
State New
Headers
Series implement RTLD_NORELOCATE api [BZ #30007] |

Checks

Context Check Description
dj/TryBot-apply_patch success Patch applied to master at the time it was sent
redhat-pt-bot/TryBot-still_applies warning Patch no longer applies to master

Commit Message

stsp May 18, 2023, 8:28 a.m. UTC
  Remove "has_holes" argument that was used to mprotect the entire
initial mapping as PROT_NONE. Instead apply PROT_NONE at each
individual hole.
This is needed to make it possible to split the protection stage
from mmap stage.

The test-suite was run on x86_64/64 and showed no regressions.

Signed-off-by: Stas Sergeev <stsp2@yandex.ru>
---
 elf/dl-load.c         |  8 +++----
 elf/dl-load.h         |  3 +--
 elf/dl-map-segments.h | 52 ++++++++++++++++++++++++++-----------------
 3 files changed, 36 insertions(+), 27 deletions(-)
  

Patch

diff --git a/elf/dl-load.c b/elf/dl-load.c
index 39c63ff1b3..4007a4aae3 100644
--- a/elf/dl-load.c
+++ b/elf/dl-load.c
@@ -1089,7 +1089,6 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
     /* Scan the program header table, collecting its load commands.  */
     struct loadcmd loadcmds[l->l_phnum];
     size_t nloadcmds = 0;
-    bool has_holes = false;
     bool empty_dynamic = false;
     ElfW(Addr) p_align_max = 0;
 
@@ -1141,6 +1140,7 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
 	  if (powerof2 (ph->p_align) && ph->p_align > p_align_max)
 	    p_align_max = ph->p_align;
 	  c->mapoff = ALIGN_DOWN (ph->p_offset, GLRO(dl_pagesize));
+	  c->maphole = 0;
 
 	  DIAG_PUSH_NEEDS_COMMENT;
 
@@ -1153,8 +1153,8 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
 #endif
 	  /* Determine whether there is a gap between the last segment
 	     and this one.  */
-	  if (nloadcmds > 1 && c[-1].mapend != c->mapstart)
-	    has_holes = true;
+	  if (nloadcmds > 1 && c[-1].mapend < c->mapstart)
+	    c[-1].maphole = c->mapstart - c[-1].mapend;
 	  DIAG_POP_NEEDS_COMMENT;
 
 	  /* Optimize a common case.  */
@@ -1256,7 +1256,7 @@  _dl_map_object_from_fd (const char *name, const char *origname, int fd,
        l_map_start, l_map_end, l_addr, l_contiguous, l_text_end, l_phdr
      */
     errstring = _dl_map_segments (l, fd, header, type, loadcmds, nloadcmds,
-				  maplength, has_holes, loader);
+				  maplength, loader);
     if (__glibc_unlikely (errstring != NULL))
       {
 	/* Mappings can be in an inconsistent state: avoid unmap.  */
diff --git a/elf/dl-load.h b/elf/dl-load.h
index ecf6910c68..029181e8c8 100644
--- a/elf/dl-load.h
+++ b/elf/dl-load.h
@@ -75,7 +75,7 @@  ELF_PREFERRED_ADDRESS_DATA;
    Its details have been expanded out and converted.  */
 struct loadcmd
 {
-  ElfW(Addr) mapstart, mapend, dataend, allocend, mapalign;
+  ElfW(Addr) mapstart, mapend, dataend, allocend, mapalign, maphole;
   ElfW(Off) mapoff;
   int prot;                             /* PROT_* bits.  */
 };
@@ -118,7 +118,6 @@  static const char *_dl_map_segments (struct link_map *l, int fd,
                                      const struct loadcmd loadcmds[],
                                      size_t nloadcmds,
                                      const size_t maplength,
-                                     bool has_holes,
                                      struct link_map *loader);
 
 /* All the error message strings _dl_map_segments might return are
diff --git a/elf/dl-map-segments.h b/elf/dl-map-segments.h
index 6a6127f773..080199b76e 100644
--- a/elf/dl-map-segments.h
+++ b/elf/dl-map-segments.h
@@ -77,7 +77,7 @@  static __always_inline const char *
 _dl_map_segments (struct link_map *l, int fd,
                   const ElfW(Ehdr) *header, int type,
                   const struct loadcmd loadcmds[], size_t nloadcmds,
-                  const size_t maplength, bool has_holes,
+                  const size_t maplength,
                   struct link_map *loader)
 {
   const struct loadcmd *c = loadcmds;
@@ -106,25 +106,6 @@  _dl_map_segments (struct link_map *l, int fd,
 
       l->l_map_end = l->l_map_start + maplength;
       l->l_addr = l->l_map_start - c->mapstart;
-
-      if (has_holes)
-        {
-          /* Change protection on the excess portion to disallow all access;
-             the portions we do not remap later will be inaccessible as if
-             unallocated.  Then jump into the normal segment-mapping loop to
-             handle the portion of the segment past the end of the file
-             mapping.  */
-	  if (__glibc_unlikely (loadcmds[nloadcmds - 1].mapstart <
-				c->mapend))
-	    return N_("ELF load command address/offset not page-aligned");
-          if (__glibc_unlikely
-              (__mprotect ((caddr_t) (l->l_addr + c->mapend),
-                           loadcmds[nloadcmds - 1].mapstart - c->mapend,
-                           PROT_NONE) < 0))
-            return DL_MAP_SEGMENTS_ERROR_MPROTECT;
-        }
-
-      l->l_contiguous = 1;
     }
   else
     {
@@ -136,11 +117,14 @@  _dl_map_segments (struct link_map *l, int fd,
       if (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED))
         return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
       l->l_map_end = l->l_map_start + maplength;
-      l->l_contiguous = !has_holes;
     }
+  /* Reset to 0 later if hole found. */
+  l->l_contiguous = 1;
 
   while (c < &loadcmds[nloadcmds])
     {
+      ElfW(Addr) hole_start, hole_size;
+
       if (c->mapend > c->mapstart
           /* Map the segment contents from the file.  */
           && (__mmap ((void *) (l->l_addr + c->mapstart),
@@ -157,11 +141,15 @@  _dl_map_segments (struct link_map *l, int fd,
           /* Extra zero pages should appear at the end of this segment,
              after the data mapped from the file.   */
           ElfW(Addr) zero, zeroend, zeropage;
+          ElfW(Off) hole_off;
 
           zero = l->l_addr + c->dataend;
           zeroend = l->l_addr + c->allocend;
           zeropage = ((zero + GLRO(dl_pagesize) - 1)
                       & ~(GLRO(dl_pagesize) - 1));
+          hole_start = ALIGN_UP (c->allocend, GLRO(dl_pagesize));
+          hole_off = hole_start - c->mapend;
+          hole_size = c->maphole - hole_off;
 
           if (zeroend < zeropage)
             /* All the extra data is in the last page of the segment.
@@ -194,6 +182,28 @@  _dl_map_segments (struct link_map *l, int fd,
                 return DL_MAP_SEGMENTS_ERROR_MPROTECT;
             }
         }
+      else
+        {
+          hole_start = c->mapend;
+          hole_size = c->maphole;
+        }
+
+      if (__glibc_unlikely (c->maphole))
+        {
+          if (__glibc_likely (type == ET_DYN))
+            {
+              if (hole_size)
+                {
+                  if (__mprotect ((caddr_t) (l->l_addr + hole_start),
+                                   hole_size, PROT_NONE) < 0)
+                    return DL_MAP_SEGMENTS_ERROR_MPROTECT;
+                }
+            }
+          else if (l->l_contiguous)
+            {
+              l->l_contiguous = 0;
+            }
+        }
 
       ++c;
     }