Fix linker self-exec detection

When the linker is invoked on itself, (`linker64 /system/bin/linker64`),
the linker prints an error, because self-invocation isn't allowed. The
current method for detecting self-invocation fails because the second
linker instance can crash in a constructor function before reaching
__linker_init.

Fix the problem by moving the error check into a constructor function,
which finishes initializing libc sufficiently to call async_safe_fatal.
The only important thing missing is __libc_sysinfo on 32-bit x86. The aux
vector isn't readily accessible, so use the fallback int 0x80.

Bug: http://b/123637025
Test: bionic unit tests (32-bit x86)
Change-Id: I8be6369e8be3938906628ae1f82be13e6c510119
This commit is contained in:
Ryan Prichard 2019-02-07 21:31:31 -08:00
parent f827d82cdd
commit 1990ba5601
4 changed files with 25 additions and 15 deletions

View file

@ -32,6 +32,10 @@
// This file is compiled without stack protection, because it runs before TLS
// has been set up.
__LIBC_HIDDEN__ __attribute__((__naked__)) void __libc_int0x80() {
__asm__ volatile("int $0x80; ret");
}
__LIBC_HIDDEN__ void __libc_init_sysinfo() {
bool dummy;
__libc_sysinfo = reinterpret_cast<void*>(__bionic_getauxval(AT_SYSINFO, dummy));

View file

@ -66,9 +66,6 @@ extern "C" {
// Use an initializer so __libc_sysinfo will have a fallback implementation
// while .preinit_array constructors run.
#if defined(__i386__)
static __attribute__((__naked__)) void __libc_int0x80() {
__asm__ volatile("int $0x80; ret");
}
__LIBC_HIDDEN__ void* __libc_sysinfo = reinterpret_cast<void*>(__libc_int0x80);
#endif

View file

@ -87,6 +87,7 @@ __LIBC_HIDDEN__ void __libc_init_vdso(libc_globals* globals);
#if defined(__i386__)
__LIBC_HIDDEN__ extern void* __libc_sysinfo;
__LIBC_HIDDEN__ void __libc_int0x80();
__LIBC_HIDDEN__ void __libc_init_sysinfo();
#endif

View file

@ -553,6 +553,24 @@ static void get_elf_base_from_phdr(const ElfW(Phdr)* phdr_table, size_t phdr_cou
async_safe_fatal("Could not find a PHDR: broken executable?");
}
// Detect an attempt to run the linker on itself. e.g.:
// /system/bin/linker64 /system/bin/linker64
// Use priority-1 to run this constructor before other constructors.
__attribute__((constructor(1))) static void detect_self_exec() {
// Normally, the linker initializes the auxv global before calling its
// constructors. If the linker loads itself, though, the first loader calls
// the second loader's constructors before calling __linker_init.
if (__libc_shared_globals()->auxv != nullptr) {
return;
}
#if defined(__i386__)
// We don't have access to the auxv struct from here, so use the int 0x80
// fallback.
__libc_sysinfo = reinterpret_cast<void*>(__libc_int0x80);
#endif
__linker_error("error: linker cannot load itself\n");
}
static ElfW(Addr) __attribute__((noinline))
__linker_init_post_relocation(KernelArgumentBlock& args, soinfo& linker_so);
@ -575,18 +593,8 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) {
// another program), AT_BASE is 0.
ElfW(Addr) linker_addr = getauxval(AT_BASE);
if (linker_addr == 0) {
// Detect an attempt to run the linker on itself (e.g.
// `linker64 /system/bin/linker64`). If the kernel loaded this instance of
// the linker, then AT_ENTRY will refer to &_start. If it doesn't, then
// something else must have loaded this instance of the linker. It's
// simpler if we only allow one copy of the linker to be loaded at a time.
if (getauxval(AT_ENTRY) != reinterpret_cast<uintptr_t>(&_start)) {
// The first linker already relocated this one and set up TLS, so we don't
// need further libc initialization.
__linker_error("error: linker cannot load itself\n");
}
// Otherwise, the AT_PHDR and AT_PHNUM aux values describe this linker
// instance, so use the phdr to find the linker's base address.
// The AT_PHDR and AT_PHNUM aux values describe this linker instance, so use
// the phdr to find the linker's base address.
ElfW(Addr) load_bias;
get_elf_base_from_phdr(
reinterpret_cast<ElfW(Phdr)*>(getauxval(AT_PHDR)), getauxval(AT_PHNUM),