> Date: Sat, 6 Aug 2016 18:58:57 -0700
> From: Philip Guenther <[email protected]>
> 
> 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?

ok kettenis@

> 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