rtld: add RELRO support
authorJohn Marino <draco@marino.st>
Mon, 16 Jan 2012 14:39:15 +0000 (15:39 +0100)
committerJohn Marino <draco@marino.st>
Mon, 16 Jan 2012 18:47:31 +0000 (19:47 +0100)
"RELRO" means PLT (partial) or PLT+GOT (full) data is shifted to a
dedicated page by the linker which triggers the dynamic linker to
protect the page by setting the memory to read-only.  This feature
assists in detecting memory corruption and also prevents some types
of buffer overflow exploits.

Until now, this feature was only supported by Linux's glibc.  No BSD
had relro support in their dynamic linker.  I proposed the patch and
it was reviewed by a FreeBSD dynamic linker expert.  Attempts to
access protected data currently results in a bus error signal, but the
next commit will update the kernel trap files to cause the segfault
signal to be emitted instead.

Reviewed-by: Kostik Belousov
libexec/rtld-elf/map_object.c
libexec/rtld-elf/rtld.c
libexec/rtld-elf/rtld.h
sys/sys/elf_common.h

index 0c7a480..bfaa24f 100644 (file)
@@ -83,6 +83,8 @@ map_object(int fd, const char *path, const struct stat *sb)
     Elf_Addr bss_vaddr;
     Elf_Addr bss_vlimit;
     caddr_t bss_addr;
+    Elf_Addr relro_page;
+    size_t relro_size;
 
     hdr = get_elf_header(fd, path);
     if (hdr == NULL)
@@ -99,6 +101,8 @@ map_object(int fd, const char *path, const struct stat *sb)
     nsegs = -1;
     phdyn = phinterp = phtls = NULL;
     phdr_vaddr = 0;
+    relro_page = 0;
+    relro_size = 0;
     segs = alloca(sizeof(segs[0]) * hdr->e_phnum);
     while (phdr < phlimit) {
        switch (phdr->p_type) {
@@ -128,6 +132,11 @@ map_object(int fd, const char *path, const struct stat *sb)
        case PT_TLS:
            phtls = phdr;
            break;
+
+       case PT_GNU_RELRO:
+           relro_page = phdr->p_vaddr;
+           relro_size = phdr->p_memsz;
+           break;
        }
 
        ++phdr;
@@ -267,6 +276,11 @@ map_object(int fd, const char *path, const struct stat *sb)
        obj->tlsinitsize = phtls->p_filesz;
        obj->tlsinit = mapbase + phtls->p_vaddr;
     }
+
+    if (relro_size) {
+        obj->relro_page = obj->relocbase + trunc_page(relro_page);
+        obj->relro_size = round_page(relro_size);
+    }
     return obj;
 }
 
index 169bf32..5a37546 100644 (file)
@@ -1131,6 +1131,11 @@ digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path)
            obj->tlsinitsize = ph->p_filesz;
            obj->tlsinit = (void*)(ph->p_vaddr + obj->relocbase);
            break;
+
+       case PT_GNU_RELRO:
+           obj->relro_page = obj->relocbase + trunc_page(ph->p_vaddr);
+           obj->relro_size = round_page(ph->p_memsz);
+           break;
        }
     }
     if (nsegs < 1) {
@@ -1945,6 +1950,8 @@ relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj)
            if (reloc_jmpslots(obj) == -1)
                return -1;
 
+       /* Set the special PLT or GOT entries. */
+       init_pltgot(obj);
 
        /*
         * Set up the magic number and version in the Obj_Entry.  These
@@ -1954,8 +1961,17 @@ relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj)
        obj->magic = RTLD_MAGIC;
        obj->version = RTLD_VERSION;
 
-       /* Set the special PLT or GOT entries. */
-       init_pltgot(obj);
+       /*
+        * Set relocated data to read-only status if protection specified
+        */
+
+       if (obj->relro_size) {
+           if (mprotect(obj->relro_page, obj->relro_size, PROT_READ) == -1) {
+               _rtld_error("%s: Cannot enforce relro relocation: %s",
+                 obj->path, strerror(errno));
+               return -1;
+           }
+       }
     }
 
     return (0);
index 6589975..e675cc0 100644 (file)
@@ -145,6 +145,8 @@ typedef struct Struct_Obj_Entry {
     const Elf_Phdr *phdr;      /* Program header if it is mapped, else NULL */
     size_t phsize;             /* Size of program header in bytes */
     const char *interp;                /* Pathname of the interpreter, if any */
+    caddr_t relro_page;        /* Address of first page of read-only data */
+    size_t relro_size;         /* Size of relro page(s) in bytes */
 
     /* TLS information */
     int tlsindex;              /* Index in DTV for this module */
index 89aa5f1..b7ecb7b 100644 (file)
@@ -301,8 +301,9 @@ typedef struct {
 #define PT_COUNT       8       /* Number of defined p_type values. */
 
 #define PT_LOOS                0x60000000      /* First OS-specific. */
-#define PT_GNU_EH_FRAME        0x6474e550
-#define PT_GNU_STACK   0x6474e551
+#define PT_GNU_EH_FRAME        0x6474e550      /* GCC .eh_frame_hdr segment */
+#define PT_GNU_STACK   0x6474e551      /* Indicates stack executability */
+#define PT_GNU_RELRO   0x6474e552      /* Read-only after relocation */
 #define PT_HIOS                0x6fffffff      /* Last OS-specific. */
 #define PT_LOPROC      0x70000000      /* First processor-specific type. */
 #define PT_HIPROC      0x7fffffff      /* Last processor-specific type. */