diff --git a/arch/aarch64/reloc.h b/arch/aarch64/reloc.h index b1b68c725..a1760abe5 100644 --- a/arch/aarch64/reloc.h +++ b/arch/aarch64/reloc.h @@ -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 diff --git a/conf.sh b/conf.sh new file mode 100755 index 000000000..bb5bdb584 --- /dev/null +++ b/conf.sh @@ -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 diff --git a/crt/aarch64/crti.s b/crt/aarch64/crti.s index 775df0ac0..5c16d50ba 100644 --- a/crt/aarch64/crti.s +++ b/crt/aarch64/crti.s @@ -2,6 +2,7 @@ .global _init .type _init,%function _init: + paciasp stp x29,x30,[sp,-16]! mov x29,sp @@ -9,5 +10,6 @@ _init: .global _fini .type _fini,%function _fini: + paciasp stp x29,x30,[sp,-16]! mov x29,sp diff --git a/crt/aarch64/crtn.s b/crt/aarch64/crtn.s index 73cab6926..4da1882ac 100644 --- a/crt/aarch64/crtn.s +++ b/crt/aarch64/crtn.s @@ -1,7 +1,9 @@ .section .init ldp x29,x30,[sp],#16 + autiasp ret .section .fini ldp x29,x30,[sp],#16 + autiasp ret diff --git a/ldso/dynlink.c b/ldso/dynlink.c index ceca3c98a..b6541eddf 100644 --- a/ldso/dynlink.c +++ b/ldso/dynlink.c @@ -19,6 +19,9 @@ #include #include #include +#if __has_feature(ptrauth_init_fini) +#include +#endif #include "pthread_impl.h" #include "fork_impl.h" #include "dynlink.h" @@ -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; @@ -111,6 +126,7 @@ struct dso { size_t *got; } *funcdescs; size_t *got; + size_t* pauth; char buf[]; }; @@ -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)); @@ -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) { @@ -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); @@ -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); @@ -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++; @@ -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: @@ -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]; @@ -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 @@ -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), @@ -1485,7 +1550,20 @@ void __libc_exit_fini() if (dyn[0] & (1<