> 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);
>
>