forked from mirrors/gecko-dev
Bug 635961 - Allow elfhack to relocate data under the GNU_RELRO segment. r=froydnj
--HG-- extra : rebase_source : 873898d5929414b754bf592ab4d60574700b646a
This commit is contained in:
parent
3229d39dba
commit
43b0a30fd0
3 changed files with 141 additions and 10 deletions
|
|
@ -89,8 +89,8 @@ private:
|
|||
|
||||
class ElfRelHackCode_Section: public ElfSection {
|
||||
public:
|
||||
ElfRelHackCode_Section(Elf_Shdr &s, Elf &e, unsigned int init)
|
||||
: ElfSection(s, nullptr, nullptr), parent(e), init(init) {
|
||||
ElfRelHackCode_Section(Elf_Shdr &s, Elf &e, unsigned int init, unsigned int mprotect_cb)
|
||||
: ElfSection(s, nullptr, nullptr), parent(e), init(init), mprotect_cb(mprotect_cb) {
|
||||
std::string file(rundir);
|
||||
file += "/inject/";
|
||||
switch (parent.getMachine()) {
|
||||
|
|
@ -125,9 +125,17 @@ public:
|
|||
if (symtab == nullptr)
|
||||
throw std::runtime_error("Couldn't find a symbol table for the injected code");
|
||||
|
||||
relro = parent.getSegmentByType(PT_GNU_RELRO);
|
||||
align = parent.getSegmentByType(PT_LOAD)->getAlign();
|
||||
|
||||
// Find the init symbol
|
||||
entry_point = -1;
|
||||
Elf_SymValue *sym = symtab->lookup(init ? "init" : "init_noinit");
|
||||
std::string symbol = "init";
|
||||
if (!init)
|
||||
symbol += "_noinit";
|
||||
if (relro)
|
||||
symbol += "_relro";
|
||||
Elf_SymValue *sym = symtab->lookup(symbol.c_str());
|
||||
if (!sym)
|
||||
throw std::runtime_error("Couldn't find an 'init' symbol in the injected code");
|
||||
|
||||
|
|
@ -353,6 +361,14 @@ private:
|
|||
addr = ehdr->getAddr();
|
||||
} else if (strcmp(name, "original_init") == 0) {
|
||||
addr = init;
|
||||
} else if (relro && strcmp(name, "mprotect_cb") == 0) {
|
||||
addr = mprotect_cb;
|
||||
} else if (relro && strcmp(name, "relro_start") == 0) {
|
||||
// Align relro segment start to the start of the page it starts in.
|
||||
addr = relro->getAddr() & ~(align - 1);
|
||||
// Align relro segment end to the start of the page it ends into.
|
||||
} else if (relro && strcmp(name, "relro_end") == 0) {
|
||||
addr = (relro->getAddr() + relro->getMemSize()) & ~(align - 1);
|
||||
} else if (strcmp(name, "_GLOBAL_OFFSET_TABLE_") == 0) {
|
||||
// We actually don't need a GOT, but need it as a reference for
|
||||
// GOTOFF relocations. We'll just use the start of the ELF file
|
||||
|
|
@ -403,7 +419,10 @@ private:
|
|||
Elf *elf, &parent;
|
||||
std::vector<ElfSection *> code;
|
||||
unsigned int init;
|
||||
unsigned int mprotect_cb;
|
||||
int entry_point;
|
||||
ElfSegment *relro;
|
||||
unsigned int align;
|
||||
};
|
||||
|
||||
unsigned int get_addend(Elf_Rel *rel, Elf *elf) {
|
||||
|
|
@ -503,8 +522,6 @@ int do_relocation_section(Elf *elf, unsigned int rel_type, unsigned int rel_type
|
|||
return -1;
|
||||
}
|
||||
|
||||
ElfSegment *relro = elf->getSegmentByType(PT_GNU_RELRO);
|
||||
|
||||
ElfRel_Section<Rel_Type> *section = (ElfRel_Section<Rel_Type> *)dyn->getSectionForType(Rel_Type::d_tag);
|
||||
if (section == nullptr) {
|
||||
fprintf(stderr, "No relocations\n");
|
||||
|
|
@ -599,9 +616,7 @@ int do_relocation_section(Elf *elf, unsigned int rel_type, unsigned int rel_type
|
|||
}
|
||||
new_rels.push_back(*i);
|
||||
init_array_reloc = new_rels.size();
|
||||
} else if (!(loc.getSection()->getFlags() & SHF_WRITE) || (ELF32_R_TYPE(i->r_info) != rel_type) ||
|
||||
(relro && (i->r_offset >= relro->getAddr()) &&
|
||||
(i->r_offset < relro->getAddr() + relro->getMemSize()))) {
|
||||
} else if (!(loc.getSection()->getFlags() & SHF_WRITE) || (ELF32_R_TYPE(i->r_info) != rel_type)) {
|
||||
// Don't pack relocations happening in non writable sections.
|
||||
// Our injected code is likely not to be allowed to write there.
|
||||
new_rels.push_back(*i);
|
||||
|
|
@ -670,10 +685,72 @@ int do_relocation_section(Elf *elf, unsigned int rel_type, unsigned int rel_type
|
|||
}
|
||||
}
|
||||
|
||||
unsigned int mprotect_cb = 0;
|
||||
// If there is a relro segment, our injected code will run after the linker sets the
|
||||
// corresponding pages read-only. We need to make our code change that to read-write
|
||||
// before applying relocations, which means it needs to call mprotect.
|
||||
// To do that, we need to find a reference to the mprotect symbol. In case the library
|
||||
// already has one, we use that, but otherwise, we add the symbol.
|
||||
// Then the injected code needs to be able to call the corresponding function, which
|
||||
// means it needs access to a pointer to it. We get such a pointer by making the linker
|
||||
// apply a relocation for the symbol at an address our code can read.
|
||||
// The problem here is that there is not much relocated space where we can put such a
|
||||
// pointer, so we abuse the bss section temporarily (it will be restored to a null
|
||||
// value before any code can actually use it)
|
||||
if (elf->getSegmentByType(PT_GNU_RELRO)) {
|
||||
Elf_SymValue *mprotect = symtab->lookup("mprotect", STT(FUNC));
|
||||
if (!mprotect) {
|
||||
symtab->syms.emplace_back();
|
||||
mprotect = &symtab->syms.back();
|
||||
symtab->grow(symtab->syms.size() * symtab->getEntSize());
|
||||
mprotect->name = ((ElfStrtab_Section *)symtab->getLink())->getStr("mprotect");
|
||||
mprotect->info = ELF32_ST_INFO(STB_GLOBAL, STT_FUNC);
|
||||
mprotect->other = STV_DEFAULT;
|
||||
new (&mprotect->value) ElfLocation(nullptr, 0, ElfLocation::ABSOLUTE);
|
||||
mprotect->size = 0;
|
||||
mprotect->defined = false;
|
||||
|
||||
// The DT_VERSYM data (in the .gnu.version section) has the same number of
|
||||
// entries as the symbols table. Since we added one entry there, we need to
|
||||
// add one entry here. Zeroes in the extra data means no version for that
|
||||
// symbol, which is the simplest thing to do.
|
||||
ElfSection *gnu_versym = dyn->getSectionForType(DT_VERSYM);
|
||||
if (gnu_versym) {
|
||||
gnu_versym->grow(gnu_versym->getSize() + gnu_versym->getEntSize());
|
||||
}
|
||||
}
|
||||
|
||||
// Add a relocation for the mprotect symbol.
|
||||
new_rels.emplace_back();
|
||||
Rel_Type &rel = new_rels.back();
|
||||
memset(&rel, 0, sizeof(rel));
|
||||
rel.r_info = ELF32_R_INFO(std::distance(symtab->syms.begin(), std::vector<Elf_SymValue>::iterator(mprotect)), rel_type2);
|
||||
|
||||
// Find the beginning of the bss section, and use an aligned location in there
|
||||
// for the relocation.
|
||||
for (ElfSegment *segment = elf->getSegmentByType(PT_LOAD); segment;
|
||||
segment = elf->getSegmentByType(PT_LOAD, segment)) {
|
||||
if (segment->getFlags() & PF_W == 0)
|
||||
continue;
|
||||
size_t ptr_size = Elf_Addr::size(elf->getClass());
|
||||
size_t aligned_mem_end = (segment->getAddr() + segment->getMemSize() + ptr_size - 1) & ~(ptr_size - 1);
|
||||
size_t aligned_file_end = (segment->getAddr() + segment->getFileSize() + ptr_size - 1) & ~(ptr_size - 1);
|
||||
if (aligned_mem_end - aligned_file_end >= Elf_Addr::size(elf->getClass())) {
|
||||
mprotect_cb = rel.r_offset = aligned_file_end;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mprotect_cb == 0) {
|
||||
fprintf(stderr, "Couldn't find .bss. Skipping\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
section->rels.assign(new_rels.begin(), new_rels.end());
|
||||
section->shrink(new_rels.size() * section->getEntSize());
|
||||
|
||||
ElfRelHackCode_Section *relhackcode = new ElfRelHackCode_Section(relhackcode_section, *elf, original_init);
|
||||
ElfRelHackCode_Section *relhackcode = new ElfRelHackCode_Section(relhackcode_section, *elf, original_init, mprotect_cb);
|
||||
relhackcode->insertBefore(section);
|
||||
relhack->insertAfter(relhackcode);
|
||||
if (section->getOffset() + section->getSize() >= old_end) {
|
||||
|
|
|
|||
|
|
@ -327,8 +327,19 @@ public:
|
|||
SectionInfo getInfo() { return info; }
|
||||
|
||||
void shrink(unsigned int newsize) {
|
||||
if (newsize < shdr.sh_size)
|
||||
if (newsize < shdr.sh_size) {
|
||||
shdr.sh_size = newsize;
|
||||
markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
void grow(unsigned int newsize) {
|
||||
if (newsize > shdr.sh_size) {
|
||||
data = static_cast<char*>(realloc(data, newsize));
|
||||
memset(data + shdr.sh_size, 0, newsize - shdr.sh_size);
|
||||
shdr.sh_size = newsize;
|
||||
markDirty();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int getOffset();
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <elf.h>
|
||||
|
||||
/* The Android NDK headers define those */
|
||||
|
|
@ -22,6 +24,10 @@ extern __attribute__((visibility("hidden"))) void original_init(int argc, char *
|
|||
extern __attribute__((visibility("hidden"))) Elf32_Rel relhack[];
|
||||
extern __attribute__((visibility("hidden"))) Elf_Ehdr elf_header;
|
||||
|
||||
extern __attribute__((visibility("hidden"))) int (*mprotect_cb)(void *addr, size_t len, int prot);
|
||||
extern __attribute__((visibility("hidden"))) char relro_start[];
|
||||
extern __attribute__((visibility("hidden"))) char relro_end[];
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
void do_relocations(void)
|
||||
{
|
||||
|
|
@ -50,3 +56,40 @@ int init(int argc, char **argv, char **env)
|
|||
// B.W instruction in Thumb for the call above.
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
void relro_pre()
|
||||
{
|
||||
// By the time the injected code runs, the relro segment is read-only. But
|
||||
// we want to apply relocations in it, so we set it r/w first. We'll restore
|
||||
// it to read-only in relro_post.
|
||||
mprotect_cb(relro_start, relro_end - relro_start, PROT_READ | PROT_WRITE);
|
||||
}
|
||||
|
||||
static inline __attribute__((always_inline))
|
||||
void relro_post()
|
||||
{
|
||||
mprotect_cb(relro_start, relro_end - relro_start, PROT_READ);
|
||||
// mprotect_cb is a pointer allocated in .bss, so we need to restore it to
|
||||
// a NULL value.
|
||||
mprotect_cb = NULL;
|
||||
}
|
||||
|
||||
__attribute__((section(".text._init_noinit_relro")))
|
||||
int init_noinit_relro(int argc, char **argv, char **env)
|
||||
{
|
||||
relro_pre();
|
||||
do_relocations();
|
||||
relro_post();
|
||||
return 0;
|
||||
}
|
||||
|
||||
__attribute__((section(".text._init_relro")))
|
||||
int init_relro(int argc, char **argv, char **env)
|
||||
{
|
||||
relro_pre();
|
||||
do_relocations();
|
||||
relro_post();
|
||||
original_init(argc, argv, env);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue