Add relro support
Add support for PT_GNU_RELRO. This allows the static linker to indicate that certain regions of memory should be marked as "read-only" after dynamic linking is complete. See: * http://www.akkadia.org/drepper/nonselsec.pdf (section 6) * http://tk-blog.blogspot.com/2009/02/relro-not-so-well-known-memory.html Note that this change has no effect on Android right now, because we don't compile our code with relro enabled. Change-Id: I6541f8775367e8558b4388f7d105b1ae6e8f046b
This commit is contained in:
@@ -130,4 +130,6 @@
|
|||||||
#define STT_LOPROC 13 /* reserved range for processor */
|
#define STT_LOPROC 13 /* reserved range for processor */
|
||||||
#define STT_HIPROC 15 /* specific symbol types */
|
#define STT_HIPROC 15 /* specific symbol types */
|
||||||
|
|
||||||
|
#define PT_GNU_RELRO 0x6474e552 /* Read-only post relocation */
|
||||||
|
|
||||||
#endif /* _SYS_EXEC_ELF_H_ */
|
#endif /* _SYS_EXEC_ELF_H_ */
|
||||||
|
@@ -12,7 +12,7 @@ LOCAL_SRC_FILES:= \
|
|||||||
|
|
||||||
LOCAL_LDFLAGS := -shared
|
LOCAL_LDFLAGS := -shared
|
||||||
|
|
||||||
LOCAL_CFLAGS += -fno-stack-protector
|
LOCAL_CFLAGS += -fno-stack-protector -Wstrict-overflow=5
|
||||||
|
|
||||||
# Set LINKER_DEBUG to either 1 or 0
|
# Set LINKER_DEBUG to either 1 or 0
|
||||||
#
|
#
|
||||||
|
@@ -906,10 +906,10 @@ load_segments(int fd, void *header, soinfo *si)
|
|||||||
{
|
{
|
||||||
Elf32_Ehdr *ehdr = (Elf32_Ehdr *)header;
|
Elf32_Ehdr *ehdr = (Elf32_Ehdr *)header;
|
||||||
Elf32_Phdr *phdr = (Elf32_Phdr *)((unsigned char *)header + ehdr->e_phoff);
|
Elf32_Phdr *phdr = (Elf32_Phdr *)((unsigned char *)header + ehdr->e_phoff);
|
||||||
unsigned char *base = (unsigned char *)si->base;
|
Elf32_Addr base = (Elf32_Addr) si->base;
|
||||||
int cnt;
|
int cnt;
|
||||||
unsigned len;
|
unsigned len;
|
||||||
unsigned char *tmp;
|
Elf32_Addr tmp;
|
||||||
unsigned char *pbase;
|
unsigned char *pbase;
|
||||||
unsigned char *extra_base;
|
unsigned char *extra_base;
|
||||||
unsigned extra_len;
|
unsigned extra_len;
|
||||||
@@ -933,7 +933,7 @@ load_segments(int fd, void *header, soinfo *si)
|
|||||||
TRACE("[ %d - Trying to load segment from '%s' @ 0x%08x "
|
TRACE("[ %d - Trying to load segment from '%s' @ 0x%08x "
|
||||||
"(0x%08x). p_vaddr=0x%08x p_offset=0x%08x ]\n", pid, si->name,
|
"(0x%08x). p_vaddr=0x%08x p_offset=0x%08x ]\n", pid, si->name,
|
||||||
(unsigned)tmp, len, phdr->p_vaddr, phdr->p_offset);
|
(unsigned)tmp, len, phdr->p_vaddr, phdr->p_offset);
|
||||||
pbase = mmap(tmp, len, PFLAGS_TO_PROT(phdr->p_flags),
|
pbase = mmap((void *)tmp, len, PFLAGS_TO_PROT(phdr->p_flags),
|
||||||
MAP_PRIVATE | MAP_FIXED, fd,
|
MAP_PRIVATE | MAP_FIXED, fd,
|
||||||
phdr->p_offset & (~PAGE_MASK));
|
phdr->p_offset & (~PAGE_MASK));
|
||||||
if (pbase == MAP_FAILED) {
|
if (pbase == MAP_FAILED) {
|
||||||
@@ -975,7 +975,7 @@ load_segments(int fd, void *header, soinfo *si)
|
|||||||
* | |
|
* | |
|
||||||
* _+---------------------+ page boundary
|
* _+---------------------+ page boundary
|
||||||
*/
|
*/
|
||||||
tmp = (unsigned char *)(((unsigned)pbase + len + PAGE_SIZE - 1) &
|
tmp = (Elf32_Addr)(((unsigned)pbase + len + PAGE_SIZE - 1) &
|
||||||
(~PAGE_MASK));
|
(~PAGE_MASK));
|
||||||
if (tmp < (base + phdr->p_vaddr + phdr->p_memsz)) {
|
if (tmp < (base + phdr->p_vaddr + phdr->p_memsz)) {
|
||||||
extra_len = base + phdr->p_vaddr + phdr->p_memsz - tmp;
|
extra_len = base + phdr->p_vaddr + phdr->p_memsz - tmp;
|
||||||
@@ -1030,6 +1030,17 @@ load_segments(int fd, void *header, soinfo *si)
|
|||||||
DEBUG_DUMP_PHDR(phdr, "PT_DYNAMIC", pid);
|
DEBUG_DUMP_PHDR(phdr, "PT_DYNAMIC", pid);
|
||||||
/* this segment contains the dynamic linking information */
|
/* this segment contains the dynamic linking information */
|
||||||
si->dynamic = (unsigned *)(base + phdr->p_vaddr);
|
si->dynamic = (unsigned *)(base + phdr->p_vaddr);
|
||||||
|
} else if (phdr->p_type == PT_GNU_RELRO) {
|
||||||
|
if ((phdr->p_vaddr >= si->size)
|
||||||
|
|| ((phdr->p_vaddr + phdr->p_memsz) >= si->size)
|
||||||
|
|| ((base + phdr->p_vaddr + phdr->p_memsz) < base)) {
|
||||||
|
DL_ERR("%d invalid GNU_RELRO in '%s' "
|
||||||
|
"p_vaddr=0x%08x p_memsz=0x%08x", pid, si->name,
|
||||||
|
phdr->p_vaddr, phdr->p_memsz);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
si->gnu_relro_start = (Elf32_Addr) (base + phdr->p_vaddr);
|
||||||
|
si->gnu_relro_len = (unsigned) phdr->p_memsz;
|
||||||
} else {
|
} else {
|
||||||
#ifdef ANDROID_ARM_LINKER
|
#ifdef ANDROID_ARM_LINKER
|
||||||
if (phdr->p_type == PT_ARM_EXIDX) {
|
if (phdr->p_type == PT_ARM_EXIDX) {
|
||||||
@@ -1251,10 +1262,29 @@ unsigned unload_library(soinfo *si)
|
|||||||
TRACE("%5d unloading '%s'\n", pid, si->name);
|
TRACE("%5d unloading '%s'\n", pid, si->name);
|
||||||
call_destructors(si);
|
call_destructors(si);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure that we undo the PT_GNU_RELRO protections we added
|
||||||
|
* in link_image. This is needed to undo the DT_NEEDED hack below.
|
||||||
|
*/
|
||||||
|
if ((si->gnu_relro_start != 0) && (si->gnu_relro_len != 0)) {
|
||||||
|
Elf32_Addr start = (si->gnu_relro_start & ~PAGE_MASK);
|
||||||
|
unsigned len = (si->gnu_relro_start - start) + si->gnu_relro_len;
|
||||||
|
if (mprotect((void *) start, len, PROT_READ | PROT_WRITE) < 0)
|
||||||
|
DL_ERR("%5d %s: could not undo GNU_RELRO protections. "
|
||||||
|
"Expect a crash soon. errno=%d (%s)",
|
||||||
|
pid, si->name, errno, strerror(errno));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
for(d = si->dynamic; *d; d += 2) {
|
for(d = si->dynamic; *d; d += 2) {
|
||||||
if(d[0] == DT_NEEDED){
|
if(d[0] == DT_NEEDED){
|
||||||
soinfo *lsi = (soinfo *)d[1];
|
soinfo *lsi = (soinfo *)d[1];
|
||||||
|
|
||||||
|
// The next line will segfault if the we don't undo the
|
||||||
|
// PT_GNU_RELRO protections (see comments above and in
|
||||||
|
// link_image().
|
||||||
d[1] = 0;
|
d[1] = 0;
|
||||||
|
|
||||||
if (validate_soinfo(lsi)) {
|
if (validate_soinfo(lsi)) {
|
||||||
TRACE("%5d %s needs to unload %s\n", pid,
|
TRACE("%5d %s needs to unload %s\n", pid,
|
||||||
si->name, lsi->name);
|
si->name, lsi->name);
|
||||||
@@ -1749,6 +1779,17 @@ static int link_image(soinfo *si, unsigned wr_offset)
|
|||||||
}
|
}
|
||||||
DEBUG_DUMP_PHDR(phdr, "PT_DYNAMIC", pid);
|
DEBUG_DUMP_PHDR(phdr, "PT_DYNAMIC", pid);
|
||||||
si->dynamic = (unsigned *) (si->base + phdr->p_vaddr);
|
si->dynamic = (unsigned *) (si->base + phdr->p_vaddr);
|
||||||
|
} else if (phdr->p_type == PT_GNU_RELRO) {
|
||||||
|
if ((phdr->p_vaddr >= si->size)
|
||||||
|
|| ((phdr->p_vaddr + phdr->p_memsz) >= si->size)
|
||||||
|
|| ((si->base + phdr->p_vaddr + phdr->p_memsz) < si->base)) {
|
||||||
|
DL_ERR("%d invalid GNU_RELRO in '%s' "
|
||||||
|
"p_vaddr=0x%08x p_memsz=0x%08x", pid, si->name,
|
||||||
|
phdr->p_vaddr, phdr->p_memsz);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
si->gnu_relro_start = (Elf32_Addr) (si->base + phdr->p_vaddr);
|
||||||
|
si->gnu_relro_len = (unsigned) phdr->p_memsz;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1890,7 +1931,7 @@ static int link_image(soinfo *si, unsigned wr_offset)
|
|||||||
of the DT_NEEDED entry itself, so that we can retrieve the
|
of the DT_NEEDED entry itself, so that we can retrieve the
|
||||||
soinfo directly later from the dynamic segment. This is a hack,
|
soinfo directly later from the dynamic segment. This is a hack,
|
||||||
but it allows us to map from DT_NEEDED to soinfo efficiently
|
but it allows us to map from DT_NEEDED to soinfo efficiently
|
||||||
later on when we resolve relocations, trying to look up a symgol
|
later on when we resolve relocations, trying to look up a symbol
|
||||||
with dlsym().
|
with dlsym().
|
||||||
*/
|
*/
|
||||||
d[1] = (unsigned)lsi;
|
d[1] = (unsigned)lsi;
|
||||||
@@ -1938,6 +1979,16 @@ static int link_image(soinfo *si, unsigned wr_offset)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (si->gnu_relro_start != 0 && si->gnu_relro_len != 0) {
|
||||||
|
Elf32_Addr start = (si->gnu_relro_start & ~PAGE_MASK);
|
||||||
|
unsigned len = (si->gnu_relro_start - start) + si->gnu_relro_len;
|
||||||
|
if (mprotect((void *) start, len, PROT_READ) < 0) {
|
||||||
|
DL_ERR("%5d GNU_RELRO mprotect of library '%s' failed: %d (%s)\n",
|
||||||
|
pid, si->name, errno, strerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* If this is a SET?ID program, dup /dev/null to opened stdin,
|
/* If this is a SET?ID program, dup /dev/null to opened stdin,
|
||||||
stdout and stderr to close a security hole described in:
|
stdout and stderr to close a security hole described in:
|
||||||
|
|
||||||
@@ -2147,6 +2198,8 @@ sanitize:
|
|||||||
si->wrprotect_start = 0xffffffff;
|
si->wrprotect_start = 0xffffffff;
|
||||||
si->wrprotect_end = 0;
|
si->wrprotect_end = 0;
|
||||||
si->refcount = 1;
|
si->refcount = 1;
|
||||||
|
si->gnu_relro_start = 0;
|
||||||
|
si->gnu_relro_len = 0;
|
||||||
|
|
||||||
/* Use LD_LIBRARY_PATH if we aren't setuid/setgid */
|
/* Use LD_LIBRARY_PATH if we aren't setuid/setgid */
|
||||||
if (ldpath_env)
|
if (ldpath_env)
|
||||||
@@ -2264,6 +2317,8 @@ unsigned __linker_init(unsigned **elfdata) {
|
|||||||
linker_so.flags |= FLAG_LINKER;
|
linker_so.flags |= FLAG_LINKER;
|
||||||
linker_so.wrprotect_start = 0xffffffff;
|
linker_so.wrprotect_start = 0xffffffff;
|
||||||
linker_so.wrprotect_end = 0;
|
linker_so.wrprotect_end = 0;
|
||||||
|
linker_so.gnu_relro_start = 0;
|
||||||
|
linker_so.gnu_relro_len = 0;
|
||||||
|
|
||||||
if (link_image(&linker_so, 0)) {
|
if (link_image(&linker_so, 0)) {
|
||||||
// It would be nice to print an error message, but if the linker
|
// It would be nice to print an error message, but if the linker
|
||||||
|
@@ -31,7 +31,8 @@
|
|||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <linux/elf.h>
|
#include <elf.h>
|
||||||
|
#include <sys/exec_elf.h>
|
||||||
|
|
||||||
#undef PAGE_MASK
|
#undef PAGE_MASK
|
||||||
#undef PAGE_SIZE
|
#undef PAGE_SIZE
|
||||||
@@ -143,6 +144,10 @@ struct soinfo
|
|||||||
struct link_map linkmap;
|
struct link_map linkmap;
|
||||||
|
|
||||||
int constructors_called;
|
int constructors_called;
|
||||||
|
|
||||||
|
Elf32_Addr gnu_relro_start;
|
||||||
|
unsigned gnu_relro_len;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user