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