Skip to content

[AArch64] Add initial support for PAC relocs #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions arch/aarch64/reloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,32 @@
#define REL_DTPMOD R_AARCH64_TLS_DTPMOD64
#define REL_DTPOFF R_AARCH64_TLS_DTPREL64
#define REL_TPOFF R_AARCH64_TLS_TPREL64

#if __has_feature(ptrauth_elf_got)
#define R_AARCH64_AUTH_TLSDESC 0xe202
#define REL_TLSDESC R_AARCH64_AUTH_TLSDESC
#else
#define REL_TLSDESC R_AARCH64_TLSDESC
#endif

#define CRTJMP(pc,sp) __asm__ __volatile__( \
"mov sp,%1 ; br %0" : : "r"(pc), "r"(sp) : "memory" )

#if __has_feature(ptrauth_calls)
#define TARGET_RELOCATE(dso, type, reladdr, sym, addend, is_phase_2, dyn, error_sym) \
do_target_reloc(dso, type, reladdr, sym, addend, is_phase_2, dyn, error_sym)
#define DO_TARGET_RELR(dso, dyn) do_pauth_relr(dso, dyn)

int do_target_reloc(int type, uint64_t* reladdr, uint64_t base, uint64_t symval,
uint64_t addend, int is_phase_2, uint64_t* dyn, uint64_t error_sym);

void do_pauth_relr(uint64_t base, uint64_t* dyn);

#define GETFUNCSYM(fp, sym, got) do { \
hidden void sym(); \
*(fp) = sym; } while(0)

#define FPTR_CAST(fty, p) \
((fty)__builtin_ptrauth_sign_unauthenticated((void*)(p), 0, 0))

#endif
6 changes: 6 additions & 0 deletions conf.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export CC=/usr/bin/clang

TARGET=aarch64-linux-gnu
export CFLAGS="-O0 --target=$TARGET -mcpu=cortex-a78c -DMUSL_EXPERIMENTAL_PAC"
export LDFLAGS="--target=$TARGET -fuse-ld=lld"
./configure --enable-debug --host=$TARGET
2 changes: 2 additions & 0 deletions crt/aarch64/crti.s
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
.global _init
.type _init,%function
_init:
paciasp
stp x29,x30,[sp,-16]!
mov x29,sp

.section .fini
.global _fini
.type _fini,%function
_fini:
paciasp
stp x29,x30,[sp,-16]!
mov x29,sp
2 changes: 2 additions & 0 deletions crt/aarch64/crtn.s
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
.section .init
ldp x29,x30,[sp],#16
autiasp
ret

.section .fini
ldp x29,x30,[sp],#16
autiasp
ret
100 changes: 96 additions & 4 deletions ldso/dynlink.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#include <dlfcn.h>
#include <semaphore.h>
#include <sys/membarrier.h>
#if __has_feature(ptrauth_init_fini)
#include <ptrauth.h>
#endif
#include "pthread_impl.h"
#include "fork_impl.h"
#include "dynlink.h"
Expand All @@ -45,6 +48,18 @@ static void (*error)(const char *, ...) = error_noop;
#define container_of(p,t,m) ((t*)((char *)(p)-offsetof(t,m)))
#define countof(a) ((sizeof (a))/(sizeof (a)[0]))

#ifndef TARGET_RELOCATE
#define TARGET_RELOCATE(...) 0
#endif

#ifndef DO_TARGET_RELR
#define DO_TARGET_RELR(...)
#endif

#ifndef FPTR_CAST
#define FPTR_CAST(fty, p) ((fty)(p))
#endif

struct debug {
int ver;
void *head;
Expand Down Expand Up @@ -111,6 +126,7 @@ struct dso {
size_t *got;
} *funcdescs;
size_t *got;
size_t* pauth;
char buf[];
};

Expand Down Expand Up @@ -469,6 +485,7 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
case REL_GOT:
case REL_PLT:
*reloc_addr = sym_val + addend;
TARGET_RELOCATE(type, reloc_addr, (size_t)base, sym_val, addend, head == &ldso, dso->dynv, (uint64_t)error);
break;
case REL_USYMBOLIC:
memcpy(reloc_addr, &(size_t){sym_val + addend}, sizeof(size_t));
Expand Down Expand Up @@ -516,6 +533,12 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
#endif
case REL_TLSDESC:
if (stride<3) addend = reloc_addr[1];
#ifdef __aarch64__
if (sym && sym->st_info>>4 == STB_WEAK && sym->st_shndx == SHN_UNDEF) {
reloc_addr[0] = (size_t)__tlsdesc_undef_weak;
reloc_addr[1] = 0;
} else
#endif
if (def.dso->tls_id > static_tls_cnt) {
struct td_index *new = malloc(sizeof *new);
if (!new) {
Expand Down Expand Up @@ -546,9 +569,18 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri
size_t tmp = reloc_addr[0];
reloc_addr[0] = reloc_addr[1];
reloc_addr[1] = tmp;
#endif
#if __has_feature(ptrauth_elf_got)
/* FIXME: actually, signing scheme is written in-place, and we should read and use that.
* However, the scheme is known (IA key + addr div for function and DA key + addr div for data).
* So, we just hard-code that. */
reloc_addr[0] = (size_t)(__builtin_ptrauth_auth_and_resign((void*)(reloc_addr[0]), 0, 0, 0, (size_t)(reloc_addr)));
reloc_addr[1] = (size_t)(__builtin_ptrauth_sign_unauthenticated((void*)(reloc_addr[1]), 2, (size_t)(reloc_addr) + 8));
#endif
break;
default:
if (TARGET_RELOCATE(type, reloc_addr, (size_t)base, sym_val, addend, head == &ldso, dso->dynv, (uint64_t)error))
break;
error("Error relocating %s: unsupported relocation type %d",
dso->name, type);
if (runtime) longjmp(*rtld_fail, 1);
Expand Down Expand Up @@ -697,6 +729,7 @@ static void *map_library(int fd, struct dso *dso)
unsigned char *map=MAP_FAILED, *base;
size_t dyn=0;
size_t tls_image=0;
size_t notes[8] = {};
size_t i;

ssize_t l = read(fd, buf, sizeof buf);
Expand Down Expand Up @@ -737,6 +770,13 @@ static void *map_library(int fd, struct dso *dso)
ph->p_memsz < DEFAULT_STACK_MAX ?
ph->p_memsz : DEFAULT_STACK_MAX;
}
} else if (ph->p_type == PT_NOTE && ph->p_memsz >= 32) {
for (unsigned in = 0; in < sizeof(notes)/sizeof(notes[0]); ++in) {
if (notes[in] == 0) {
notes[in] = ph->p_vaddr;
break;
}
}
}
if (ph->p_type != PT_LOAD) continue;
nsegs++;
Expand Down Expand Up @@ -858,6 +898,13 @@ static void *map_library(int fd, struct dso *dso)
dso->base = base;
dso->dynv = laddr(dso, dyn);
if (dso->tls.size) dso->tls.image = laddr(dso, tls_image);
for (unsigned in = 0; in < sizeof(notes)/sizeof(notes[0]) && notes[in]; ++in) {
uint32_t* pnote = laddr(dso, notes[in]);
if (pnote[2] == NT_GNU_ABI_TAG && !strncmp((char*)&pnote[3], "ARM", 4)) {
dso->pauth = (size_t*)&pnote[4];
break;
}
}
free(allocated_buf);
return map;
noexec:
Expand Down Expand Up @@ -1050,6 +1097,16 @@ static void makefuncdescs(struct dso *p)
}
}

static int check_pauth_abi_compatible(struct dso* first, struct dso* second)
{
if (first->pauth == second->pauth)
return 1;
if (first->pauth == 0 || second->pauth == 0)
return 0;
return first->pauth[0] == second->pauth[0] &&
first->pauth[1] == second->pauth[1];
}

static struct dso *load_library(const char *name, struct dso *needed_by)
{
char buf[2*NAME_MAX+2];
Expand Down Expand Up @@ -1182,6 +1239,11 @@ static struct dso *load_library(const char *name, struct dso *needed_by)
close(fd);
if (!map) return 0;

if (!check_pauth_abi_compatible(head, &temp_dso)) {
dprintf(2, "incompatible PAuth ABI between %s and %s\n", head->name, name);
return 0;
}

/* Avoid the danger of getting two versions of libc mapped into the
* same process when an absolute pathname was used. The symbols
* checked are chosen to catch both musl and glibc, and to avoid
Expand Down Expand Up @@ -1419,6 +1481,9 @@ static void reloc_all(struct dso *p)
do_relocs(p, laddr(p, dyn[DT_RELA]), dyn[DT_RELASZ], 3);
if (!DL_FDPIC)
do_relr_relocs(p, laddr(p, dyn[DT_RELR]), dyn[DT_RELRSZ]);
if (p != &ldso) {
DO_TARGET_RELR((uint64_t)p->base, p->dynv);
}

if (head != &ldso && p->relro_start != p->relro_end) {
long ret = __syscall(SYS_mprotect, laddr(p, p->relro_start),
Expand Down Expand Up @@ -1485,7 +1550,20 @@ void __libc_exit_fini()
if (dyn[0] & (1<<DT_FINI_ARRAY)) {
size_t n = dyn[DT_FINI_ARRAYSZ]/sizeof(size_t);
size_t *fn = (size_t *)laddr(p, dyn[DT_FINI_ARRAY])+n;
while (n--) ((void (*)(void))*--fn)();
#if __has_feature(ptrauth_init_fini)
while (n--) {
ptrauth_auth_function(
(void (*)(void))*--fn,
ptrauth_key_asia,
#if __has_feature(ptrauth_init_fini_address_discrimination)
ptrauth_blend_discriminator(fn, __ptrauth_init_fini_discriminator))();
#else
__ptrauth_init_fini_discriminator)();
#endif
}
#else
while (n--) FPTR_CAST(void (*)(void), (void*)*(--fn))();
#endif
}
#ifndef NO_LEGACY_INITFINI
if ((dyn[0] & (1<<DT_FINI)) && dyn[DT_FINI])
Expand Down Expand Up @@ -1603,7 +1681,21 @@ static void do_init_fini(struct dso **queue)
if (dyn[0] & (1<<DT_INIT_ARRAY)) {
size_t n = dyn[DT_INIT_ARRAYSZ]/sizeof(size_t);
size_t *fn = laddr(p, dyn[DT_INIT_ARRAY]);
while (n--) ((void (*)(void))*fn++)();
#if __has_feature(ptrauth_init_fini)
while (n--) {
ptrauth_auth_function(
(void (*)(void))*fn,
ptrauth_key_asia,
#if __has_feature(ptrauth_init_fini_address_discrimination)
ptrauth_blend_discriminator(fn, __ptrauth_init_fini_discriminator))();
#else
__ptrauth_init_fini_discriminator)();
#endif
++fn;
}
#else
while (n--) FPTR_CAST(void (*)(void), (void*)*fn++)();
#endif
}

pthread_mutex_lock(&init_fini_lock);
Expand Down Expand Up @@ -1762,7 +1854,7 @@ hidden void __dls2(unsigned char *base, size_t *sp)
* load across the above relocation processing. */
struct symdef dls2b_def = find_sym(&ldso, "__dls2b", 0);
if (DL_FDPIC) ((stage3_func)&ldso.funcdescs[dls2b_def.sym-ldso.syms])(sp, auxv);
else ((stage3_func)laddr(&ldso, dls2b_def.sym->st_value))(sp, auxv);
else FPTR_CAST(stage3_func, laddr(&ldso, dls2b_def.sym->st_value))(sp, auxv);
}

/* Stage 2b sets up a valid thread pointer, which requires relocations
Expand All @@ -1786,7 +1878,7 @@ void __dls2b(size_t *sp, size_t *auxv)

struct symdef dls3_def = find_sym(&ldso, "__dls3", 0);
if (DL_FDPIC) ((stage3_func)&ldso.funcdescs[dls3_def.sym-ldso.syms])(sp, auxv);
else ((stage3_func)laddr(&ldso, dls3_def.sym->st_value))(sp, auxv);
else FPTR_CAST(stage3_func, laddr(&ldso, dls3_def.sym->st_value))(sp, auxv);
}

/* Stage 3 of the dynamic linker is called with the dynamic linker/libc
Expand Down
2 changes: 1 addition & 1 deletion src/internal/dynlink.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ hidden void __dl_seterr(const char *, ...);
hidden int __dl_invalid_handle(void *);
hidden void __dl_vseterr(const char *, va_list);

hidden ptrdiff_t __tlsdesc_static(), __tlsdesc_dynamic();
hidden ptrdiff_t __tlsdesc_static(), __tlsdesc_dynamic(), __tlsdesc_undef_weak();

hidden extern int __malloc_replaced;
hidden extern int __aligned_alloc_replaced;
Expand Down
9 changes: 8 additions & 1 deletion src/internal/vdso.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <string.h>
#include "libc.h"
#include "syscall.h"
#include "reloc.h"

#ifdef VDSO_USEFUL

Expand All @@ -22,6 +23,10 @@ typedef Elf64_Verdef Verdef;
typedef Elf64_Verdaux Verdaux;
#endif

#ifndef FPTR_CAST
#define FPTR_CAST(fty, p) ((fty)(p))
#endif

static int checkver(Verdef *def, int vsym, const char *vername, char *strings)
{
vsym &= 0x7fff;
Expand Down Expand Up @@ -84,7 +89,9 @@ void *__vdsosym(const char *vername, const char *name)
if (strcmp(name, strings+syms[i].st_name)) continue;
if (versym && !checkver(verdef, versym[i], vername, strings))
continue;
return (void *)(base + syms[i].st_value);
return (syms[i].st_info & 0xF) == STT_FUNC ?
FPTR_CAST(void*, base + syms[i].st_value) :
(void *)(base + syms[i].st_value);
}

return 0;
Expand Down
Loading