From: John Marino Date: Mon, 16 Jan 2012 14:39:15 +0000 (+0100) Subject: rtld: add RELRO support X-Git-Tag: v3.0.0~55 X-Git-Url: http://gitweb.dragonflybsd.org/dragonfly.git/commitdiff_plain/007f494e0a1eec2db98703bbe5e4b29a890c01d1 rtld: add RELRO support "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 --- diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index 0c7a480..bfaa24f 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -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; } diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 169bf32..5a37546 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -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); diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 6589975..e675cc0 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -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 */ diff --git a/sys/sys/elf_common.h b/sys/sys/elf_common.h index 89aa5f1..b7ecb7b 100644 --- a/sys/sys/elf_common.h +++ b/sys/sys/elf_common.h @@ -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. */