]> xenbits.xensource.com Git - people/julieng/freebsd.git/commitdiff
Allow PT_NOTES segments to be located anywhere in the executable
authorkib <kib@FreeBSD.org>
Wed, 14 Oct 2015 18:29:21 +0000 (18:29 +0000)
committerkib <kib@FreeBSD.org>
Wed, 14 Oct 2015 18:29:21 +0000 (18:29 +0000)
image.

The dynamic linker still requires that program headers of the
executable or dso are mapped by a PT_LOAD segment.

Reviewed by: emaste, jhb
Sponsored by: The FreeBSD Foundation
MFC after: 2 weeks
Differential revision: https://reviews.freebsd.org/D3871

libexec/rtld-elf/map_object.c

index 2e17fbf06b7851f1ef583cdb13cdd2330006880d..6012015bcc28133217f3bf4ef70521971c71ef21 100644 (file)
@@ -88,6 +88,8 @@ map_object(int fd, const char *path, const struct stat *sb)
     size_t relro_size;
     Elf_Addr note_start;
     Elf_Addr note_end;
+    char *note_map;
+    size_t note_map_len;
 
     hdr = get_elf_header(fd, path);
     if (hdr == NULL)
@@ -108,6 +110,7 @@ map_object(int fd, const char *path, const struct stat *sb)
     relro_size = 0;
     note_start = 0;
     note_end = 0;
+    note_map = NULL;
     segs = alloca(sizeof(segs[0]) * hdr->e_phnum);
     stack_flags = RTLD_DEFAULT_STACK_PF_EXEC | PF_R | PF_W;
     while (phdr < phlimit) {
@@ -150,9 +153,20 @@ map_object(int fd, const char *path, const struct stat *sb)
 
        case PT_NOTE:
            if (phdr->p_offset > PAGE_SIZE ||
-             phdr->p_offset + phdr->p_filesz > PAGE_SIZE)
-               break;
-           note_start = (Elf_Addr)(char *)hdr + phdr->p_offset;
+             phdr->p_offset + phdr->p_filesz > PAGE_SIZE) {
+               note_map_len = round_page(phdr->p_offset +
+                 phdr->p_filesz) - trunc_page(phdr->p_offset);
+               note_map = mmap(NULL, note_map_len, PROT_READ,
+                 MAP_PRIVATE, fd, trunc_page(phdr->p_offset));
+               if (note_map == MAP_FAILED) {
+                   _rtld_error("%s: error mapping PT_NOTE (%d)", path, errno);
+                   goto error;
+               }
+               note_start = (Elf_Addr)(note_map + phdr->p_offset -
+                 trunc_page(phdr->p_offset));
+           } else {
+               note_start = (Elf_Addr)(char *)hdr + phdr->p_offset;
+           }
            note_end = note_start + phdr->p_filesz;
            break;
        }
@@ -295,12 +309,16 @@ map_object(int fd, const char *path, const struct stat *sb)
     obj->relro_size = round_page(relro_size);
     if (note_start < note_end)
        digest_notes(obj, note_start, note_end);
+    if (note_map != NULL)
+       munmap(note_map, note_map_len);
     munmap(hdr, PAGE_SIZE);
     return (obj);
 
 error1:
     munmap(mapbase, mapsize);
 error:
+    if (note_map != NULL && note_map != MAP_FAILED)
+       munmap(note_map, note_map_len);
     munmap(hdr, PAGE_SIZE);
     return (NULL);
 }