Let's teach ld.so to look for a PT_GNU_RELRO section per object and, if 
present, mprotect(PROT_READ) the range it covers *instead of* the 
__got_start .. __got_end range.

Two interesting bits are in here:
 1) we need to move up the handling of DT_DEBUG to before relocation is 
    done, so that the .dynamic segment can be covered by the PT_GNU_RELRO
    section.  That's the bulk of the loader.c diff, though diff shows it
    as moving the _dl_rtld() call and such *down* instead of moving
    the DT_DEBUG stuff *up*

 2) _dl_protect_segment() is used for both __got_start/end and 
    __plt_start/end.  The latter should be going away soonish, so
    to tell the calls apart I just test the 3rd character of the symbol.
    Gross, but it works and will go away once the PLT stuff is gone.
    As a plus, turning on LD_DEBUG will show whether GNU_RELRO is used.  
    For example, on my full-relro laptop:
...
examining: '/usr/lib/libc.so.88.0'
 flags /usr/libexec/ld.so = 0x0
obj /usr/libexec/ld.so has vis as head
protect start RELRO = 0x1557ed825b38 in /usr/lib/libc.so.88.0
protect end RELRO = 0x1557ed827000 in /usr/lib/libc.so.88.0
protect start RELRO = 0x155513801d88 in vis
protect end RELRO = 0x155513802000 in vis
        Start            End              Type Open Ref GrpRef Name
        0000155513600000 0000155513803000 exe  1    0   0      vis
        00001557ed56e000 00001557ed839000 rlib 0    1   0      
/usr/lib/libc.so.88.0
        0000155751c00000 0000155751c00000 rtld 0    1   0      
/usr/libexec/ld.so
...


ok?

Philip Guenther

Index: resolve.h
===================================================================
RCS file: /data/src/openbsd/src/libexec/ld.so/resolve.h,v
retrieving revision 1.78
diff -u -p -r1.78 resolve.h
--- resolve.h   4 Jul 2016 21:15:06 -0000       1.78
+++ resolve.h   4 Jul 2016 21:15:18 -0000
@@ -148,6 +148,10 @@ struct elf_object {
        const void      *tls_static_data;
        int             tls_offset;
 
+       /* relro bits */
+       Elf_Addr        relro_addr;
+       Elf_Addr        relro_size;
+
        /* generation number of last grpsym insert on this object */
        unsigned int grpsym_gen;
 
Index: library.c
===================================================================
RCS file: /data/src/openbsd/src/libexec/ld.so/library.c,v
retrieving revision 1.77
diff -u -p -r1.77 library.c
--- library.c   4 Jul 2016 21:15:06 -0000       1.77
+++ library.c   18 Jul 2016 12:41:48 -0000
@@ -98,6 +98,7 @@ _dl_tryload_shlib(const char *libname, i
        struct load_list *next_load, *load_list = NULL;
        Elf_Addr maxva = 0, minva = ELFDEFNNAME(NO_ADDR);
        Elf_Addr libaddr, loff, align = _dl_pagesz - 1;
+       Elf_Addr relro_addr = 0, relro_size = 0;
        elf_object_t *object;
        char    hbuf[4096];
        Elf_Dyn *dynp = NULL;
@@ -281,6 +282,11 @@ _dl_tryload_shlib(const char *libname, i
                            phdp->p_memsz);
                        break;
 
+               case PT_GNU_RELRO:
+                       relro_addr = phdp->p_vaddr + loff;
+                       relro_size = phdp->p_memsz;
+                       break;
+
                default:
                        break;
                }
@@ -299,6 +305,8 @@ _dl_tryload_shlib(const char *libname, i
                object->dev = sb.st_dev;
                object->inode = sb.st_ino;
                object->obj_flags |= flags;
+               object->relro_addr = relro_addr;
+               object->relro_size = relro_size;
                _dl_set_sod(object->load_name, &object->sod);
                if (ptls != NULL && ptls->p_memsz)
                        _dl_set_tls(object, ptls, libaddr, libname);
Index: library_mquery.c
===================================================================
RCS file: /data/src/openbsd/src/libexec/ld.so/library_mquery.c,v
retrieving revision 1.54
diff -u -p -r1.54 library_mquery.c
--- library_mquery.c    4 Jul 2016 21:15:06 -0000       1.54
+++ library_mquery.c    18 Jul 2016 12:41:54 -0000
@@ -108,6 +108,7 @@ _dl_tryload_shlib(const char *libname, i
        Elf_Addr load_end = 0;
        Elf_Addr align = _dl_pagesz - 1, off, size;
        Elf_Phdr *ptls = NULL;
+       Elf_Addr relro_addr = 0, relro_size = 0;
        struct stat sb;
        char hbuf[4096];
 
@@ -297,10 +298,15 @@ retry:
        }
 
        phdp = (Elf_Phdr *)(hbuf + ehdr->e_phoff);
-       for (i = 0; i < ehdr->e_phnum; i++, phdp++)
+       for (i = 0; i < ehdr->e_phnum; i++, phdp++) {
                if (phdp->p_type == PT_OPENBSD_RANDOMIZE)
                        _dl_randombuf((char *)(phdp->p_vaddr + LOFF),
                            phdp->p_memsz);
+               else if (phdp->p_type == PT_GNU_RELRO) {
+                       relro_addr = phdp->p_vaddr + LOFF;
+                       relro_size = phdp->p_memsz;
+               }
+       }
 
        _dl_close(libfile);
 
@@ -315,6 +321,8 @@ retry:
                object->dev = sb.st_dev;
                object->inode = sb.st_ino;
                object->obj_flags |= flags;
+               object->relro_addr = relro_addr;
+               object->relro_size = relro_size;
                _dl_set_sod(object->load_name, &object->sod);
                if (ptls != NULL && ptls->p_memsz)
                        _dl_set_tls(object, ptls, (Elf_Addr)lowld->start,
Index: loader.c
===================================================================
RCS file: /data/src/openbsd/src/libexec/ld.so/loader.c,v
retrieving revision 1.162
diff -u -p -r1.162 loader.c
--- loader.c    4 Jul 2016 21:15:06 -0000       1.162
+++ loader.c    18 Jul 2016 12:40:12 -0000
@@ -478,6 +478,10 @@ _dl_boot(const char **argv, char **envp,
                        }
                        ptls = phdp;
                        break;
+               case PT_GNU_RELRO:
+                       exe_obj->relro_addr = phdp->p_vaddr + exe_loff;
+                       exe_obj->relro_size = phdp->p_memsz;
+                       break;
                }
                phdp++;
        }
@@ -524,37 +528,9 @@ _dl_boot(const char **argv, char **envp,
        _dl_allocate_tls_offsets();
 
        /*
-        * Everything should be in place now for doing the relocation
-        * and binding. Call _dl_rtld to do the job. Fingers crossed.
-        */
-       failed = 0;
-       if (_dl_traceld == NULL)
-               failed = _dl_rtld(_dl_objects);
-
-       if (_dl_debug || _dl_traceld) {
-               if (_dl_traceld)
-                       _dl_pledge("stdio rpath", NULL);
-               _dl_show_objects();
-       }
-
-       DL_DEB(("dynamic loading done, %s.\n",
-           (failed == 0) ? "success":"failed"));
-
-       if (failed != 0)
-               _dl_exit(1);
-
-       if (_dl_traceld)
-               _dl_exit(0);
-
-       _dl_loading_object = NULL;
-
-       /* set up the TIB for the initial thread */
-       _dl_allocate_first_tib();
-
-       _dl_fixup_user_env();
-
-       /*
-        * Finally make something to help gdb when poking around in the code.
+        * Make something to help gdb when poking around in the code.
+        * Do this poking at the .dynamic section now, before relocation
+        * renders it read-only
         */
        map_link = NULL;
 #ifdef __mips__
@@ -593,6 +569,38 @@ _dl_boot(const char **argv, char **envp,
                            PROT_READ|PROT_EXEC);
 #endif
        }
+
+
+       /*
+        * Everything should be in place now for doing the relocation
+        * and binding. Call _dl_rtld to do the job. Fingers crossed.
+        */
+
+       failed = 0;
+       if (_dl_traceld == NULL)
+               failed = _dl_rtld(_dl_objects);
+
+       if (_dl_debug || _dl_traceld) {
+               if (_dl_traceld)
+                       _dl_pledge("stdio rpath", NULL);
+               _dl_show_objects();
+       }
+
+       DL_DEB(("dynamic loading done, %s.\n",
+           (failed == 0) ? "success":"failed"));
+
+       if (failed != 0)
+               _dl_exit(1);
+
+       if (_dl_traceld)
+               _dl_exit(0);
+
+       _dl_loading_object = NULL;
+
+       /* set up the TIB for the initial thread */
+       _dl_allocate_first_tib();
+
+       _dl_fixup_user_env();
 
        _dl_debug_state();
 
Index: resolve.c
===================================================================
RCS file: /data/src/openbsd/src/libexec/ld.so/resolve.c,v
retrieving revision 1.73
diff -u -p -r1.73 resolve.c
--- resolve.c   4 Jul 2016 21:15:06 -0000       1.73
+++ resolve.c   4 Jul 2016 21:15:18 -0000
@@ -442,29 +442,51 @@ _dl_protect_segment(elf_object_t *object
        const Elf_Sym *this;
        Elf_Addr ooff, start, end;
 
-       if (addr == 0) {
+       if (addr == 0 && start_sym[2] == 'g' &&
+           (addr = object->relro_addr) != 0) {
+               DL_DEB(("protect start RELRO = 0x%lx in %s\n",
+                   addr, object->load_name));
+       }
+       else if (addr == 0) {
                this = NULL;
                ooff = _dl_find_symbol(start_sym, &this,
                    SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL,
                    object, NULL);
                /* If not found, nothing to do */
-               if (this == NULL)
+               if (this == NULL) {
+                       DL_DEB(("protect start \"%s\" not found in %s\n",
+                           start_sym, object->load_name));
                        return (NULL);
+               }
                addr = ooff + this->st_value;
+               DL_DEB(("protect start \"%s\" to %x = 0x%lx in %s\n",
+                   start_sym, prot, addr, object->load_name));
        }
 
-       this = NULL;
-       ooff = _dl_find_symbol(end_sym, &this,
-           SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, object, NULL);
-       if (this == NULL)
-               addr = 0;
-       else {
-               end = ooff + this->st_value;
-               if (addr < end) {
-                       start = ELF_TRUNC(addr, _dl_pagesz);
-                       end = ELF_ROUND(end, _dl_pagesz);
-                       _dl_mprotect((void *)start, end - start, prot);
+       if (object->relro_addr != 0 && start_sym[2] == 'g') {
+               end = object->relro_addr + object->relro_size;
+               DL_DEB(("protect end RELRO = 0x%lx in %s\n",
+                   end, object->load_name));
+       } else {
+               this = NULL;
+               ooff = _dl_find_symbol(end_sym, &this,
+                   SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL,
+                   object, NULL);
+               if (this == NULL) {
+                       DL_DEB(("protect end \"%s\" not found in %s\n",
+                           end_sym, object->load_name));
+                       addr = 0;
+               } else {
+                       end = ooff + this->st_value;
+                       DL_DEB(("protect end \"%s\" = 0x%lx in %s\n",
+                           end_sym, end, object->load_name));
                }
+       }
+
+       if (addr != 0 && addr < end) {
+               start = ELF_TRUNC(addr, _dl_pagesz);
+               end = ELF_ROUND(end, _dl_pagesz);
+               _dl_mprotect((void *)start, end - start, prot);
        }
 
        return ((void *)addr);

Reply via email to