From dfb58ee2be9dbc59b6d261eb5b754fe3d0b72500 Mon Sep 17 00:00:00 2001 From: Evgeny Leviant Date: Tue, 5 Sep 2023 13:29:33 +0300 Subject: [PATCH 01/16] [AArch64] Add initial support for PAC relocs --- arch/aarch64/reloc.h | 11 ++++ ldso/dynlink.c | 13 +++++ src/ldso/aarch64/reloc.c | 116 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+) create mode 100644 src/ldso/aarch64/reloc.c diff --git a/arch/aarch64/reloc.h b/arch/aarch64/reloc.h index b1b68c725..1c529bf3e 100644 --- a/arch/aarch64/reloc.h +++ b/arch/aarch64/reloc.h @@ -22,3 +22,14 @@ #define CRTJMP(pc,sp) __asm__ __volatile__( \ "mov sp,%1 ; br %0" : : "r"(pc), "r"(sp) : "memory" ) + +#ifdef MUSL_EXPERIMENTAL_PAC +#define TARGET_RELOCATE(dso, type, reladdr, sym, addend) \ + do_target_reloc(dso, type, reladdr, sym, addend) +#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); + +void do_pauth_relr(uint64_t base, uint64_t* dyn); +#endif diff --git a/ldso/dynlink.c b/ldso/dynlink.c index ceca3c98a..f4232ad26 100644 --- a/ldso/dynlink.c +++ b/ldso/dynlink.c @@ -45,6 +45,14 @@ 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 + struct debug { int ver; void *head; @@ -549,6 +557,8 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri #endif break; default: + if (TARGET_RELOCATE(type, reloc_addr, (size_t)base, sym_val, addend)) + break; error("Error relocating %s: unsupported relocation type %d", dso->name, type); if (runtime) longjmp(*rtld_fail, 1); @@ -1419,6 +1429,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), diff --git a/src/ldso/aarch64/reloc.c b/src/ldso/aarch64/reloc.c new file mode 100644 index 000000000..7b741c365 --- /dev/null +++ b/src/ldso/aarch64/reloc.c @@ -0,0 +1,116 @@ +#ifdef MUSL_EXPERIMENTAL_PAC + +#include +#include "reloc.h" + +#define R_AARCH64_AUTH_ABS64 0xe100 +#define R_AARCH64_AUTH_RELATIVE 0xe200 + +#define DT_AARCH64_AUTH_RELRSZ 0x70000011 +#define DT_AARCH64_AUTH_RELR 0x70000012 +#define DT_AARCH64_AUTH_RELRENT 0x70000013 + +static uint64_t do_sign_ia(uint64_t modifier, uint64_t value) +{ + __asm__ ("pacia %0, %1" : "+r" (value) : "r" (modifier)); + return value; +} + +static uint64_t do_sign_ib(uint64_t modifier, uint64_t value) +{ + __asm__ ("pacib %0, %1" : "+r" (value) : "r" (modifier)); + return value; +} + +static uint64_t do_sign_da(uint64_t modifier, uint64_t value) +{ + __asm__ ("pacda %0, %1" : "+r" (value) : "r" (modifier)); + return value; +} + +static uint64_t do_sign_db(uint64_t modifier, uint64_t value) +{ + __asm__ ("pacdb %0, %1" : "+r" (value) : "r" (modifier)); + return value; +} + +static int do_pauth_reloc(uint64_t* reladdr, uint64_t value) +{ + uint64_t schema = *reladdr; + unsigned discrim = (schema >> 32) & 0xFFFF; + int addr_div = schema >> 63; + int key = (schema >> 60) & 0x3; + uint64_t modifier = discrim; + if (addr_div) + modifier = (modifier << 48) | (uint64_t)reladdr; + + switch(key) { + default: + *reladdr = do_sign_ia(modifier, value); + break; + case 1: + *reladdr = do_sign_ib(modifier, value); + break; + case 2: + *reladdr = do_sign_da(modifier, value); + break; + case 3: + *reladdr = do_sign_db(modifier, value); + break; + } + return 1; +} + +int do_target_reloc(int type, uint64_t* reladdr, uint64_t base, + uint64_t symval, uint64_t addend) +{ + switch(type) + { + case R_AARCH64_AUTH_ABS64: + return do_pauth_reloc(reladdr, symval + addend); + case R_AARCH64_AUTH_RELATIVE: + return do_pauth_reloc(reladdr, base + addend); + default: + return 0; + } +} + +static uint64_t dyn_value(uint64_t* dyn, uint64_t tag) +{ + while(*dyn) + { + if (*dyn == tag) + return dyn[1]; + else + dyn += 2; + } + return 0; +} + +void do_pauth_relr(uint64_t base, uint64_t* dyn) +{ + uint64_t* relr = (uint64_t*)dyn_value(dyn, DT_AARCH64_AUTH_RELR); + if (relr == 0) + return; + uint64_t relr_size = dyn_value(dyn, DT_AARCH64_AUTH_RELRSZ); + uint64_t relr_ent = dyn_value(dyn, DT_AARCH64_AUTH_RELRENT); + if (relr_ent != sizeof(uint64_t)) + return; + uint64_t *reloc_addr; + for (; relr_size; relr++, relr_size-=relr_ent) + if ((relr[0]&1) == 0) { + reloc_addr = (uint64_t*)(base + relr[0]); + do_pauth_reloc(reloc_addr, base + (*reloc_addr & 0xFFFFFFFF)); + reloc_addr++; + } else { + int i = 0; + for (uint64_t bitmap=relr[0]; (bitmap>>=1); i++) + if (bitmap&1) { + uint64_t val = base + (reloc_addr[i] & 0xFFFFFFFF); + do_pauth_reloc(&reloc_addr[i], val); + } + reloc_addr += 8*sizeof(uint64_t)-1; + } +} + +#endif From e245acecb2bac30b11d251bb64756f0110d12af6 Mon Sep 17 00:00:00 2001 From: Evgeny Leviant Date: Fri, 29 Sep 2023 09:56:54 +0300 Subject: [PATCH 02/16] Check PAuth ABI compatibility when loading DSO TODO: do we need this to be done for vDSO as well? --- ldso/dynlink.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/ldso/dynlink.c b/ldso/dynlink.c index f4232ad26..3d3132ea0 100644 --- a/ldso/dynlink.c +++ b/ldso/dynlink.c @@ -119,6 +119,7 @@ struct dso { size_t *got; } *funcdescs; size_t *got; + size_t* pauth; char buf[]; }; @@ -707,6 +708,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); @@ -747,6 +749,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++; @@ -868,6 +877,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: @@ -1060,6 +1076,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]; @@ -1192,6 +1218,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 From 30c2c61a82eaf31daba69eb185d2b1348570455f Mon Sep 17 00:00:00 2001 From: Evgeny Leviant Date: Thu, 19 Oct 2023 16:00:42 +0300 Subject: [PATCH 03/16] Some fixes required to run llvm-testsuite with PAC enabled - Do not check pointer signature before we are relocated as this might be a pointer to global. - Sign function pointers returned from __vdsosym. - Do not apply PAC relocs in __dls2, because we may need to do this second time in __dls3. We can't do this as we overwrite authentication scheme when applying relocs. --- arch/aarch64/reloc.h | 14 +++++++++++--- ldso/dynlink.c | 14 +++++++++----- src/internal/vdso.c | 9 ++++++++- src/ldso/aarch64/reloc.c | 5 ++++- src/thread/aarch64/clone.s | 4 ++++ 5 files changed, 36 insertions(+), 10 deletions(-) diff --git a/arch/aarch64/reloc.h b/arch/aarch64/reloc.h index 1c529bf3e..a50a80281 100644 --- a/arch/aarch64/reloc.h +++ b/arch/aarch64/reloc.h @@ -24,12 +24,20 @@ "mov sp,%1 ; br %0" : : "r"(pc), "r"(sp) : "memory" ) #ifdef MUSL_EXPERIMENTAL_PAC -#define TARGET_RELOCATE(dso, type, reladdr, sym, addend) \ - do_target_reloc(dso, type, reladdr, sym, addend) +#define TARGET_RELOCATE(dso, type, reladdr, sym, addend, is_phase_2) \ + do_target_reloc(dso, type, reladdr, sym, addend, is_phase_2) #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); + uint64_t symval, uint64_t addend, int is_phase_2); 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 diff --git a/ldso/dynlink.c b/ldso/dynlink.c index 3d3132ea0..5f9c9196e 100644 --- a/ldso/dynlink.c +++ b/ldso/dynlink.c @@ -53,6 +53,10 @@ static void (*error)(const char *, ...) = error_noop; #define DO_TARGET_RELR(...) #endif +#ifndef FPTR_CAST +#define FPTR_CAST(fty, p) ((fty)(p)) +#endif + struct debug { int ver; void *head; @@ -558,7 +562,7 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri #endif break; default: - if (TARGET_RELOCATE(type, reloc_addr, (size_t)base, sym_val, addend)) + if (TARGET_RELOCATE(type, reloc_addr, (size_t)base, sym_val, addend, head == &ldso)) break; error("Error relocating %s: unsupported relocation type %d", dso->name, type); @@ -1529,7 +1533,7 @@ void __libc_exit_fini() if (dyn[0] & (1<