Skip to content
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
19 changes: 19 additions & 0 deletions arch/aarch64/reloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,22 @@

#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, 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, 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
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
77 changes: 73 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>
#ifdef MUSL_EXPERIMENTAL_PAC_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 @@ -549,6 +565,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, head == &ldso))
break;
error("Error relocating %s: unsupported relocation type %d",
dso->name, type);
if (runtime) longjmp(*rtld_fail, 1);
Expand Down Expand Up @@ -697,6 +715,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 +756,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 +884,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 +1083,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 +1225,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 +1467,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 +1536,16 @@ 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)();
#ifdef MUSL_EXPERIMENTAL_PAC_INIT_FINI
while (n--) {
ptrauth_auth_function(
(void (*)(void))*--fn,
ptrauth_key_asia,
__ptrauth_init_fini_discriminator)();
}
#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 +1663,16 @@ 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++)();
#ifdef MUSL_EXPERIMENTAL_PAC_INIT_FINI
while (n--) {
ptrauth_auth_function(
(void (*)(void))*fn++,
ptrauth_key_asia,
__ptrauth_init_fini_discriminator)();
}
#else
while (n--) FPTR_CAST(void (*)(void), (void*)*fn++)();
#endif
}

pthread_mutex_lock(&init_fini_lock);
Expand Down Expand Up @@ -1762,7 +1831,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 +1855,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
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
123 changes: 123 additions & 0 deletions src/ldso/aarch64/reloc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#ifdef MUSL_EXPERIMENTAL_PAC

#include <stdint.h>
#include "reloc.h"

#define R_AARCH64_AUTH_ABS64 0x410
#define R_AARCH64_AUTH_RELATIVE 0x411

#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)
{
if (value == 0) {
*reladdr = 0;
return 1;
}
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, int is_phase_2)
{
// We don't process auth relocs until we load all dependencies
if (is_phase_2)
return 1;
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
4 changes: 4 additions & 0 deletions src/thread/aarch64/clone.s
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ __clone:
ret
// child
1: ldp x1,x0,[sp],#16
#if MUSL_EXPERIMENTAL_PAC
blraaz x1
#else
blr x1
#endif
mov x8,#93 // SYS_exit
svc #0