Skip to content
Open
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
12 changes: 7 additions & 5 deletions core/arch/arm64/runtime.S
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ lfi_ctx_entry:
mov x8, sp
str x8, [x1]
// put entrypoint in x30
mov x30, x2
mov x28, x2

#ifndef CTXREG
// set the sandbox thread control block by saving the old value of
Expand All @@ -52,8 +52,8 @@ lfi_ctx_entry:
ldp x22, x23, [x0, REGS_X(22)]
ldp x24, x25, [x0, REGS_X(24)]
ldp x26, x27, [x0, REGS_X(26)]
ldp x28, x29, [x0, REGS_X(28)]
ldp xzr, x1, [x0, REGS_X(30)]
ldp xzr, x29, [x0, REGS_X(28)]
ldp x30, x1, [x0, REGS_X(30)]
mov sp, x1
ldp x0, x1, [x0, REGS_X(0)]
// zero vector registers
Expand Down Expand Up @@ -92,9 +92,11 @@ lfi_ctx_entry:
// zero integer and fp flags
ands xzr, xzr, xzr
msr fpsr, xzr
// NOTE: x30 must contain a valid sandbox address
// NOTE: x28 and x30 must contain a valid sandbox address
// Entry with ret is not compatible with restorer, DO NOT overwrite x30
add x30, REG_BASE, w30, uxtw
ret
add x28, REG_BASE, w28, uxtw
br x28

// lfi_ctx_end(struct LFIContext *ctx, int val)
.global lfi_ctx_end
Expand Down
7 changes: 7 additions & 0 deletions core/ctx.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,17 @@ lfi_ctx_data(struct LFIContext *ctx)
EXPORT int
lfi_ctx_run(struct LFIContext *ctx, uintptr_t entry)
{
// Save and restore invoke_info.ctx so that nested calls (e.g. running a
// signal handler while sandbox execution is still live on this thread)
// leave the outer ctx intact.
struct LFIContext **saved_ctxp = lfi_invoke_info.ctx;

// Save the ctx in invoke info so it can be retrieved via lfi_cur_ctx.
lfi_invoke_info.ctx = &ctx;
// Enter the sandbox, saving the stack pointer to host_sp.
int ret = lfi_ctx_entry(ctx, (uintptr_t *) &ctx->regs.host_sp, entry);

lfi_invoke_info.ctx = saved_ctxp;
return ret;
}

Expand Down
6 changes: 6 additions & 0 deletions linux/arch/arm64/arch_sys.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "lfi_core.h"

#include <signal.h>

#define LINUX_SYS_io_setup 0
#define LINUX_SYS_io_destroy 1
#define LINUX_SYS_io_submit 2
Expand Down Expand Up @@ -314,3 +316,7 @@

void
arch_syshandle(struct LFIContext *ctx);

bool
arch_forward_signal(struct LFIContext *ctx, int sig, siginfo_t *si,
void *ucontext);
97 changes: 97 additions & 0 deletions linux/arch/arm64/arch_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,100 @@ struct Stat {
struct TimeSpec st_ctim;
uint32_t _unused[2];
};

struct SigAction {
uintptr_t handler;
unsigned long flags;
uintptr_t restorer;
uint64_t mask;
};

#define SA_RESTORER 0x04000000

struct SigInfo {
uint8_t signo[4];
uint8_t errno_[4];
uint8_t code[4];
uint8_t pad1_[4];
union {
struct {
union {
struct {
// signals sent by kill() and sigqueue() set these
uint8_t pid[4];
uint8_t uid[4];
};
struct {
// SIGALRM sets these
uint8_t timerid[4];
uint8_t overrun[4];
};
};
union {
uint8_t value[8]; // provided by third arg of sigqueue(2)
struct {
uint8_t status[4];
uint8_t pad2_[4];
uint8_t utime[8];
uint8_t stime[8];
};
};
};
struct {
// SIGILL, SIGFPE, SIGSEGV, SIGBUS, SIGTRAP
uint8_t addr[8];
uint8_t addr_lsb[2];
uint8_t pad3_[6];
union {
struct {
uint8_t lower[8];
uint8_t upper[8];
};
uint8_t pkey[4];
};
};
struct {
uint8_t band[8]; // SIGPOLL
uint8_t fd[4];
};
struct {
uint8_t call_addr[8];
uint8_t syscall[4];
uint8_t arch[4];
};
uint8_t payload[112];
};
};

struct UContext {
uint8_t uc__flags[8];
uint8_t uc__link[8];

// stack_t
uint8_t ss__sp[8];
uint8_t ss__flags[4];
uint8_t pad0_[4];
uint8_t ss_size[8];

// glibc uses a 1024-bit sigset
uint8_t sigmask[8];
uint8_t __sigmask[120];

uint8_t pad2_[8];
// sigcontext
uint8_t fault_address[8];
uint8_t regs[31][8];
uint8_t sp[8];
uint8_t pc[8];
uint8_t pstate[8];

uint8_t pad3_[8];
uint8_t __reserved[4096]; // TODO aligned 16 static assert
};

// TODO: FPSIMD context

struct SigFrame {
struct SigInfo si;
struct UContext uc;
};
155 changes: 155 additions & 0 deletions linux/arch/arm64/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,35 @@

#include "arch_sys.h"
#include "lfi_arch.h"
#include "lfi_core.h"
#include "linux.h"
#include "proc.h"
#include "lock.h"

#include <assert.h>
#include <sys/ucontext.h>

#define ROUNDDOWN(X, K) ((X) & -(K))

static void
put32(uint8_t *p, uint32_t v)
{
__builtin_memcpy(p, &v, 4);
}

static void
put64(uint8_t *p, uint64_t v)
{
__builtin_memcpy(p, &v, 8);
}

static uint64_t
read64(uint8_t *p)
{
uint64_t v;
__builtin_memcpy(&v, p, 8);
return v;
}

void
arch_syshandle(struct LFIContext *ctx)
Expand All @@ -17,3 +42,133 @@ arch_syshandle(struct LFIContext *ctx)
regs->x0 = syshandle(t, regs->x8, regs->x0, regs->x1, regs->x2, regs->x3,
regs->x4, regs->x5);
}

bool
arch_forward_signal(struct LFIContext *ctx, int sig, siginfo_t *si,
void *ucontext)
{
if (!ctx)
return false;
struct LFILinuxThread *t = lfi_ctx_data(ctx);
if (!t || !t->proc)
return false;
if (sig < 1 || sig >= LINUX_NSIG)
return false;

{
LOCK_WITH_DEFER(&t->proc->lk_signals, lk_signals);
if (!t->proc->signals[sig].valid)
return false;
}

// NOTE: before adding support for other signals, their host sighandler must be
// registered.
assert(sig == LINUX_SIGSEGV || sig == LINUX_SIGILL || sig == LINUX_SIGBUS);

struct SigAction sighand = t->proc->signals[sig].entry;

struct SigFrame sf = {0};

ucontext_t *uctx = (ucontext_t *)ucontext;

// Fill SigInfo.
put32(sf.si.signo, sig);
put32(sf.si.code, si->si_code);
if (sig == LINUX_SIGILL || sig == LINUX_SIGFPE || sig == LINUX_SIGSEGV ||
sig == LINUX_SIGBUS || sig == LINUX_SIGTRAP) {
put64(sf.si.addr, (uintptr_t)si->si_addr);
}

put64(sf.uc.fault_address, uctx->uc_mcontext.fault_address);
__builtin_memcpy(sf.uc.regs, uctx->uc_mcontext.regs, sizeof(sf.uc.regs));
put64(sf.uc.sp, uctx->uc_mcontext.sp);
put64(sf.uc.pc, uctx->uc_mcontext.pc);
put64(sf.uc.pstate, uctx->uc_mcontext.pstate);
// TODO: __reserved for fpsimd, esr, sve

uintptr_t sp = ROUNDDOWN((uintptr_t)uctx->uc_mcontext.sp, 16);
sp -= sizeof(sf);
if (!lfi_box_ptrvalid(t->proc->box, sp)) {
ERROR("%s: signal handler may access memory outside sandbox region.!\n", __PRETTY_FUNCTION__);
return false;
}

lfi_box_copyto(t->proc->box, sp, &sf, sizeof(sf));

// Set up sandbox registers for handler invocation.
struct LFIRegs *regs = lfi_ctx_regs(ctx);
regs->sp = sp;
regs->x0 = sig;
regs->x1 = sp + offsetof(struct SigFrame, si);
regs->x2 = sp + offsetof(struct SigFrame, uc);
regs->x30 = sighand.restorer; // SAFETY: lfiptr_t restorer

uintptr_t saved_host_sp = regs->host_sp;

// Run the sandbox signal handler. Returns when the handler calls
// rt_sigreturn, which invokes lfi_ctx_exit. siglongjmp is not supported.
lfi_ctx_run(ctx, sighand.handler);

regs->host_sp = saved_host_sp;

// before signal frame re-read
uintptr_t old_requested_pc = read64(sf.uc.pc);

uintptr_t frame_sp = lfi_ctx_regs(ctx)->sp;
lfi_box_copyfm(t->proc->box, &sf, frame_sp, sizeof(sf));

// Validate RIP the handler wants the sandbox context to resume at.
uintptr_t requested_pc = read64(sf.uc.pc);
if (!lfi_box_ptrvalid(t->proc->box, requested_pc)) {
ERROR("%s: requested pc is invalid!\n", __PRETTY_FUNCTION__);
return false;
}
if (old_requested_pc == requested_pc) {
// resume at the faulting instruction, possibly unaligned
LOG(t->proc->engine, "%s: returning to the faulting pc!\n", __PRETTY_FUNCTION__);
}

// Tell the kernel to resume sandbox execution at the validated RIP.
uctx->uc_mcontext.pc = requested_pc;

// SAFETY: X30 must be validated.
uintptr_t x30 = uctx->uc_mcontext.regs[30];
if (!lfi_box_ptrvalid(t->proc->box, x30)) {
ERROR("%s: x30 points to host memory!\n", __PRETTY_FUNCTION__);
return false;
}
regs->x30 = x30;

regs->x0 = uctx->uc_mcontext.regs[0];
regs->x1 = uctx->uc_mcontext.regs[1];
regs->x2 = uctx->uc_mcontext.regs[2];
regs->x3 = uctx->uc_mcontext.regs[3];
regs->x4 = uctx->uc_mcontext.regs[4];
regs->x5 = uctx->uc_mcontext.regs[5];
regs->x6 = uctx->uc_mcontext.regs[6];
regs->x7 = uctx->uc_mcontext.regs[7];
regs->x8 = uctx->uc_mcontext.regs[8];
regs->x9 = uctx->uc_mcontext.regs[9];
regs->x10 = uctx->uc_mcontext.regs[10];
regs->x11 = uctx->uc_mcontext.regs[11];
regs->x12 = uctx->uc_mcontext.regs[12];
regs->x13 = uctx->uc_mcontext.regs[13];
regs->x14 = uctx->uc_mcontext.regs[14];
regs->x15 = uctx->uc_mcontext.regs[15];
regs->x16 = uctx->uc_mcontext.regs[16];
regs->x17 = uctx->uc_mcontext.regs[17];
regs->x18 = uctx->uc_mcontext.regs[18];
regs->x19 = uctx->uc_mcontext.regs[19];
regs->x20 = uctx->uc_mcontext.regs[20];
regs->x21 = uctx->uc_mcontext.regs[21];
regs->x22 = uctx->uc_mcontext.regs[22];
regs->x23 = uctx->uc_mcontext.regs[23];
regs->x24 = uctx->uc_mcontext.regs[24];
regs->x25 = uctx->uc_mcontext.regs[25];
regs->x26 = uctx->uc_mcontext.regs[26];
regs->x27 = uctx->uc_mcontext.regs[27];
regs->x28 = uctx->uc_mcontext.regs[28];
regs->x29 = uctx->uc_mcontext.regs[29];

return true;
}
6 changes: 6 additions & 0 deletions linux/arch/x64/arch_sys.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#include "lfi_core.h"

#include <signal.h>

#define LINUX_SYS_read 0
#define LINUX_SYS_write 1
#define LINUX_SYS_open 2
Expand Down Expand Up @@ -97,3 +99,7 @@

void
arch_syshandle(struct LFIContext *ctx);

bool
arch_forward_signal(struct LFIContext *ctx, int sig, siginfo_t *si,
void *ucontext);
Loading
Loading