1
0
Fork 0
mirror of https://gitlab.com/sortix/sortix.git synced 2023-02-13 20:55:38 -05:00

Enforce Control Flow Integrity using System Calls.

This commit is contained in:
Jonas 'Sortie' Termansen 2016-04-22 17:34:59 +02:00
parent 5351bd0719
commit 666b84846e
87 changed files with 768 additions and 110 deletions

View file

@ -41,7 +41,9 @@ SORTIX_PORTS_DIR=$(make_dir_path_absolute "$SORTIX_PORTS_DIR")
SORTIX_REPOSITORY_DIR=$(make_dir_path_absolute "$SORTIX_REPOSITORY_DIR")
# Decide the optimization options with which the ports will be built.
if [ -z "${OPTLEVEL+x}" ]; then OPTLEVEL="-Os -s"; fi
# -fno-ipa-ra prohibits the compiler from locally diverging from the calling
# convention, which breaks CFI.
if [ -z "${OPTLEVEL+x}" ]; then OPTLEVEL="-Os -s -fno-ipa-ra"; fi
if [ -z "${PORTS_OPTLEVEL+x}" ]; then PORTS_OPTLEVEL="$OPTLEVEL"; fi
if [ -z "${PORTS_CFLAGS+x}" ]; then PORTS_CFLAGS="$PORTS_OPTLEVEL"; fi
if [ -z "${PORTS_CXXFLAGS+x}" ]; then PORTS_CXXFLAGS="$PORTS_OPTLEVEL"; fi

View file

@ -121,7 +121,9 @@ ifdef SYSROOT
endif
# Determine default optimization level.
DEFAULT_GENERIC_OPTLEVEL_BASE:=-Os -s
# -fno-ipa-ra prohibits the compiler from locally diverging from the calling
# convention, which breaks CFI.
DEFAULT_GENERIC_OPTLEVEL_BASE:=-Os -s -fno-ipa-ra
DEFAULT_OPTLEVEL_FOR_BUILD:=$(DEFAULT_GENERIC_OPTLEVEL_BASE)
ifeq ($(BUILD_IS_SORTIX),1)
DEFAULT_OPTLEVEL_FOR_BUILD+=
@ -130,3 +132,8 @@ DEFAULT_OPTLEVEL:=$(DEFAULT_GENERIC_OPTLEVEL_BASE)
ifeq ($(HOST_IS_SORTIX),1)
DEFAULT_OPTLEVEL+=
endif
# Enable Control Flow Integrity by default. The compiler wrapper will transform
# the compiled assembly if this environment variable is set to 1.
CFI_ENABLE?=1
export CFI_ENABLE

View file

@ -207,11 +207,13 @@ vgafont.h: vgafont.f16
*.cpp */*.cpp */*/*.cpp: | kb/default-kblayout.h vgafont.h
# The kernel cannot use system calls to call itself, so it must be compiled
# without CFI.
%.o: %.cpp
$(CXX) -c $< -o $@ $(CPPFLAGS) $(CXXFLAGS)
CFI_ENABLE=0 $(CXX) -c $< -o $@ $(CPPFLAGS) $(CXXFLAGS)
%.o: %.S
$(CXX) -c $< -o $@ $(CPPFLAGS) $(CXXFLAGS)
CFI_ENABLE=0 $(CXX) -c $< -o $@ $(CPPFLAGS) $(CXXFLAGS)
clean:
rm -f $(ALLOBJS) sortix.bin

View file

@ -52,6 +52,13 @@ struct ioctx_struct;
typedef struct ioctx_struct ioctx_t;
struct segment;
// The control flow graph is a set of such pairs.
struct control_flow
{
uintptr_t from;
uintptr_t to;
};
class Process
{
friend void Process__OnLastThreadExit(void*);
@ -82,6 +89,10 @@ private:
Ref<MountTable> mtable;
Ref<DescriptorTable> dtable;
// If this file is open, then the indirect control flow is recorded to it.
public:
Ref<Descriptor> cfi_dump;
public:
Ref<ProcessTable> ptable;
@ -145,6 +156,13 @@ public:
kthread_mutex_t segment_write_lock;
kthread_mutex_t segment_lock;
// The control flow graph, how many entries it contains, and how many are room
// for (for efficient expansion).
public:
struct control_flow* cfg;
size_t cfg_length;
size_t cfg_allocated;
public:
kthread_mutex_t user_timers_lock;
UserTimer user_timers[PROCESS_TIMER_NUM_MAX];

View file

@ -53,6 +53,28 @@ Thread* RunKernelThread(Process* process, void (*entry)(void*), void* user,
size_t stacksize = 0);
Thread* RunKernelThread(void (*entry)(void*), void* user, size_t stacksize = 0);
// Structure to identify each live setjmp buffers. The call_level is the call
// stack depth of the setjmp caller. The setjmp buffer goes out of scope if that
// function returns. rip is the address it will return to. jmpbuf_ptr is the
// pointer to the user-space jmp_buf. Each function can have many jmp_buf
// objects, so they must be kept track of separately.
struct secure_setjmp
{
size_t call_level;
uintptr_t rip;
uintptr_t jmpbuf_ptr;
};
// Limit protected shadow stacks to 4096 pointers. This works well in practice,
// though some programs using a lot of recursion may not work. For simplicity,
// this limit is currently hard-coded, but if nessesary, could be changed to
// allocate a larger array on overflow.
#define CFI_MAX_CALL_DEPTH 4096
// setjmp is rarely used, especially recursively, so 16 should be enough for all
// sensible programs.
#define CFI_MAX_SETJMP 16
class Thread
{
public:
@ -85,6 +107,16 @@ public:
bool signal_single;
Clock execute_clock;
Clock system_clock;
// Per-thread call stack array. call_count is the current depth, the amount
// of return pointers stored in calls. This is the protected shadow stack.
size_t call_count;
uintptr_t calls[CFI_MAX_CALL_DEPTH];
// Per-thread live setjmp objects array. setjmp_count is the number of such
// live entries. It is sorted in increasing call stack depth and elements
// will be removed when they go out of function scope. This is the kernel
// jmp_buf registation.
size_t setjmp_count;
struct secure_setjmp setjmps[CFI_MAX_SETJMP];
public:
void HandleSignal(struct interrupt_context* intctx);

View file

@ -142,6 +142,11 @@ Process::Process()
segment_write_lock = KTHREAD_MUTEX_INITIALIZER;
segment_lock = KTHREAD_MUTEX_INITIALIZER;
// A new process does not have any CFG yet.
cfg = NULL;
cfg_length = 0;
cfg_allocated = 0;
user_timers_lock = KTHREAD_MUTEX_INITIALIZER;
memset(&user_timers, 0, sizeof(user_timers));
// alarm_timer initialized in member constructor.
@ -166,6 +171,11 @@ Process::~Process()
assert(!mtable);
assert(!cwd);
assert(!root);
// TODO: Investigate whether cfi_dump needs to be specially handled during
// process shutdown.
// Delete the CFG.
delete[] cfg;
assert(ptable);
ptable->Free(pid);
@ -680,6 +690,8 @@ Process* Process::Fork()
kthread_mutex_lock(&ptrlock);
clone->root = root;
clone->cwd = cwd;
// TODO: fork cfi_dump
// TODO: fork cfg
kthread_mutex_unlock(&ptrlock);
kthread_mutex_lock(&idlock);
@ -742,6 +754,13 @@ void Process::ResetForExecute()
signal_stack->ss_flags = SS_DISABLE;
ResetAddressSpace();
// A new program is being loaded into this process. Clean up the CFI state.
cfi_dump.Reset();
delete[] cfg;
cfg = NULL;
cfg_length = 0;
cfg_allocated = 0;
}
bool Process::MapSegment(struct segment* result, void* hint, size_t size,
@ -786,6 +805,67 @@ int Process::Execute(const char* programname, const uint8_t* program,
delete[] program_image_path;
program_image_path = programname_clone; programname_clone = NULL;
// If there is a control flow graph, load it into the current process such
// that it can be enforced.
{
char* cfg_path;
if ( asprintf(&cfg_path, "%s.cfg", program_image_path) < 0 )
return -1;
ioctx_t ctx;
SetupKernelIOCtx(&ctx);
Ref<Descriptor> from = cfg_path[0] == '/' ? GetRoot() : GetCWD();
Ref<Descriptor> cfg_desc = from->open(&ctx, cfg_path, O_READ, 0);
free(cfg_path);
// TODO: Potentially refuse to run securely on some errors, except some
// safe ones like ENOENT. But which are safe?
if ( cfg_desc )
{
struct stat st;
if ( cfg_desc->stat(&ctx, &st) < 0 )
return -1;
// TODO: off_t to size_t truncation check.
size_t length = st.st_size / sizeof(struct control_flow);
size_t size = length * sizeof(struct control_flow);
cfg = new struct control_flow[length];
if ( !cfg )
return -1;
// TODO: read could technically read too little, read in a loop.
if ( cfg_desc->read(&ctx, (uint8_t*) cfg, size) < (ssize_t) size )
{
delete[] cfg;
cfg = NULL;
return -1;
}
cfg_length = length;
cfg_allocated = length;
}
}
// CFI recording is enabled by default, unless the CFI_RECORD environment
// variable is set to 0.
bool enable_cfg_record = true;
for ( int i = 0; i < envc; i++ )
{
if ( !strcmp(envp[i], "CFI_RECORD=0") )
enable_cfg_record = false;
}
// If no CFG was loaded, and CFI recording is enabled, record all indirect
// control flow to a trace file next to the program itself.
if ( !cfg && enable_cfg_record )
{
char* cfi_path;
if ( asprintf(&cfi_path, "%s.%ji.cfi", program_image_path, (intmax_t) pid) < 0 )
return -1;
ioctx_t ctx;
SetupKernelIOCtx(&ctx);
Ref<Descriptor> from = cfi_path[0] == '/' ? GetRoot() : GetCWD();
cfi_dump = from->open(&ctx, cfi_path, O_WRITE | O_APPEND | O_CREATE, 0);
free(cfi_path);
if ( !cfi_dump )
return -1;
}
uintptr_t userspace_addr;
size_t userspace_size;
Memory::GetUserVirtualArea(&userspace_addr, &userspace_size);
@ -1485,6 +1565,28 @@ pid_t sys_tfork(int flags, struct tfork* user_regs)
memcpy(&thread->signal_mask, &regs.sigmask, sizeof(sigset_t));
memcpy(&thread->signal_stack, &regs.altstack, sizeof(stack_t));
// If making a clone of the current process (fork), make a copy of the CFI
// information as well. The registers of the new thread was set in the sfork
// function in libc, which is written in asesembly, and it then calls the
// tfork libc function which invokes the system call. That means we're one
// more call level further down than the thread we are creating. The
// call_count of the new thread is therefore one less than this thread.
if ( making_process )
{
if ( curthread->call_count != 0 )
{
memcpy(thread->calls, curthread->calls, sizeof(thread->calls));
thread->call_count = curthread->call_count - 1 /* subtract tfork syscall */;
memcpy(thread->setjmps, curthread->setjmps, sizeof(thread->setjmps));
thread->setjmp_count = curthread->setjmp_count;
}
else
{
// TODO: Ensure this logic is correct if the program loaded into
// the current process is not CFI enabled.
}
}
StartKernelThread(thread);
return child_process->pid;

View file

@ -713,7 +713,7 @@ retry_another_signal:
handler_regs.eip = (unsigned long) handler_ptr;
handler_regs.eflags &= ~FLAGS_DIRECTION;
#elif defined(__x86_64__)
stack_location -= 128; /* Red zone. */
stack_location -= 128 + 16; /* Red zone and two CFI stack parameters. */
stack_location -= sizeof(stack_frame);
stack_location &= ~(16UL-1UL); /* 16-byte align */
struct stack_frame* stack = (struct stack_frame*) stack_location;
@ -803,6 +803,25 @@ retry_another_signal:
if ( (signal_single = signal_count == 1) )
signal_single_frame = (uintptr_t) stack;
// TODO: This shouldn't be done for non-CFI programs.
// Signals handlers are normally invoked by transferring control to the
// signal handler, while setting up the stack such that the function returns
// to a sigreturn tramboline, which invokes a system call that make the
// thread return from the system. Since we're enforcing CFI, we will rather
// fake a call to the signal handler from the sigreturn function. That makes
// the signal handler return to the sigreturn function, which will then
// invoke the system call and the signal is properly returned from.
Thread* thread = CurrentThread();
if ( CFI_MAX_CALL_DEPTH - thread->call_count < 2 )
{
process->ExitThroughSignal(SIGABRT);
Log::PrintF("%s[%ji]: CFI violation: signal delivery secure stack overflow\n",
process->program_image_path, (intmax_t) process->pid);
kthread_exit();
}
thread->calls[thread->call_count++] = stack_frame.ucontext.uc_mcontext.gregs[REG_RIP];
thread->calls[thread->call_count++] = (uintptr_t) process->sigreturn;
// Run the signal handler by returning to user-space.
return;
}
@ -902,6 +921,18 @@ void Thread::HandleSigreturn(struct interrupt_context* intctx)
DecodeMachineContext(&stack_frame.ucontext.uc_mcontext, &resume_regs);
Scheduler::LoadInterruptedContext(intctx, &resume_regs);
// Restore the instruction pointer after returning from a signal using the
// protected shadow stack, rather than from the state saved in user-space.
Thread* thread = CurrentThread();
if ( thread->call_count == 0 )
{
process->ExitThroughSignal(SIGABRT);
Log::PrintF("%s[%ji]: CFI violation: signal return secure stack underflow\n",
process->program_image_path, (intmax_t) process->pid);
kthread_exit();
}
intctx->rip = thread->calls[--thread->call_count];
if ( signal_count != SIZE_MAX )
signal_count--;
signal_single = false;

View file

@ -100,6 +100,9 @@ Thread::Thread()
sigemptyset(&signal_mask);
memset(&signal_stack, 0, sizeof(signal_stack));
signal_stack.ss_flags = SS_DISABLE;
// Initialize the CFI information to empty arrays on thread creation.
call_count = 0;
setjmp_count = 0;
// execute_clock initialized in member constructor.
// system_clock initialized in member constructor.
Time::InitializeThreadClocks(this);

View file

@ -337,6 +337,47 @@ thread_exit_handler:
pushq $0 # err_code
pushq $132 # int_no
jmp interrupt_handler_prepare
# Raw interrupt handlers for each of the 6 new CFI system calls. The interface
# is described in kernel/interrupt.cpp at the real implementations. These are
# just trambolines that remember which interrupt happened and then invokes the
# common handler, which will invoke the real handlers after preserving
# user-space registers.
.global secure_call_handler
.type secure_call_handler, @function
secure_call_handler:
pushq $0 # err_code
pushq $0x90 # int_no
jmp interrupt_handler_prepare
.global secure_ret_handler
.type secure_ret_handler, @function
secure_ret_handler:
pushq $0 # err_code
pushq $0x91 # int_no
jmp interrupt_handler_prepare
.global secure_indirect_call_handler
.type secure_indirect_call_handler, @function
secure_indirect_call_handler:
pushq $0 # err_code
pushq $0x92 # int_no
jmp interrupt_handler_prepare
.global secure_indirect_jmp_handler
.type secure_indirect_jmp_handler, @function
secure_indirect_jmp_handler:
pushq $0 # err_code
pushq $0x93 # int_no
jmp interrupt_handler_prepare
.global secure_setjmp_handler
.type secure_setjmp_handler, @function
secure_setjmp_handler:
pushq $0 # err_code
pushq $0x94 # int_no
jmp interrupt_handler_prepare
.global secure_longjmp_handler
.type secure_longjmp_handler, @function
secure_longjmp_handler:
pushq $0 # err_code
pushq $0x95 # int_no
jmp interrupt_handler_prepare
interrupt_handler_prepare:
movq $1, asm_is_cpu_interrupted

View file

@ -20,11 +20,16 @@
#include <assert.h>
#include <errno.h>
#include <msr.h>
#include <setjmp.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sortix/kernel/copy.h>
#include <sortix/kernel/cpu.h>
#include <sortix/kernel/descriptor.h>
#include <sortix/kernel/interrupt.h>
#include <sortix/kernel/ioctx.h>
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/process.h>
#include <sortix/kernel/scheduler.h>
@ -91,6 +96,13 @@ extern "C" void interrupt_handler_null();
extern "C" void syscall_handler();
extern "C" void yield_cpu_handler();
extern "C" void thread_exit_handler();
// Forward declare the raw handlers in kernel/x64/interrupt.S.
extern "C" void secure_call_handler();
extern "C" void secure_ret_handler();
extern "C" void secure_indirect_call_handler();
extern "C" void secure_indirect_jmp_handler();
extern "C" void secure_setjmp_handler();
extern "C" void secure_longjmp_handler();
namespace Sortix {
namespace Interrupt {
@ -131,6 +143,211 @@ static struct interrupt_handler Scheduler__InterruptYieldCPU_handler;
static struct interrupt_handler Signal__DispatchHandler_handler;
static struct interrupt_handler Signal__ReturnHandler_handler;
static struct interrupt_handler Scheduler__ThreadExitCPU_handler;
// Storage for the registration of the CFI interrupt handers.
static struct interrupt_handler CFI__secure_call_handler;
static struct interrupt_handler CFI__secure_ret_handler;
static struct interrupt_handler CFI__secure_indirect_call_handler;
static struct interrupt_handler CFI__secure_indirect_jmp_handler;
static struct interrupt_handler CFI__secure_setjmp_handler;
static struct interrupt_handler CFI__secure_longjmp_handler;
// Terminates the process abnormally with SIGABRT due to a CFI violation.
__attribute__((noreturn))
static void CFIViolation(const char* event)
{
Process* process = CurrentProcess();
process->ExitThroughSignal(SIGABRT);
Log::PrintF("%s[%ji]: CFI violation: %s\n",
process->program_image_path, (intmax_t) process->pid, event);
kthread_exit();
}
// The secure call system call. The destination address is in the rax register
// and the return address is the current instruction (as the int $0x90
// instruction has already been executed, rip will point to the next one). The
// return pointer is saved in the call stack array, and rip is changed to the
// destination address (rax). The stack is still decremented by 8, as the call
// instruction normally would, but the memory is unchanged (uninitialized) and
// unused (the called function would use secure ret). It's important to still
// decrement the stack by 8 because the System V ABI for x86_64 requires 16-byte
// stack alignment, which we must preserve, and the call instruction pushes 8
// bytes. The rax register is safe to clobber because it is used for the return
// value of the called function and there is no expectation that it would stay
// intact.
void SecureCallHandler(struct interrupt_context* intctx, void*)
{
Thread* thread = CurrentThread();
if ( thread->call_count == CFI_MAX_CALL_DEPTH )
CFIViolation("secure call stack overflow");
thread->calls[thread->call_count++] = intctx->rip;
intctx->rip = intctx->rax;
intctx->rsp -= 8;
}
// The secure return system call. There are no parameters. It simply pops the
// saved return pointer in the call stack, transfers execution to that location
// by setting rip, and then incrementing the stack by 8 as the ret instruction
// would do. If any setjmp objects go out of scope, they are cleaned up.
void SecureRetHandler(struct interrupt_context* intctx, void*)
{
Thread* thread = CurrentThread();
if ( thread->call_count == 0 )
CFIViolation("secure call stack underflow");
intctx->rip = thread->calls[--thread->call_count];
intctx->rsp += 8;
// jmp_buf registations may have gone out of scope.
while ( thread->setjmp_count &&
thread->call_count < thread->setjmps[thread->setjmp_count-1].call_level )
thread->setjmp_count--;
}
// Control flow comparison function used for binary search of valid control
// flow in the control flow graph.
static int SearchControlFlow(const void* key_ptr, const void* cand_ptr)
{
const struct control_flow* key = (const struct control_flow*) key_ptr;
const struct control_flow* cand = (const struct control_flow*) cand_ptr;
if ( cand->from < key->from )
return 1;
if ( cand->from > key->from )
return -1;
if ( cand->to < key->to )
return 1;
if ( cand->to > key->to )
return -1;
return 0;
}
// Verifies control flow is in the control flow graph.
static void CheckControlFlow(const struct control_flow* cf)
{
Process* process = CurrentProcess();
// If recording, don't enforce the control flow graph, but record instead.
if ( process->cfi_dump )
{
// Don't record this control flow if already seen.
for ( size_t i = 0; i < process->cfg_length; i++ )
{
if ( process->cfg[i].from == cf->from &&
process->cfg[i].to == cf->to )
return;
}
// Double the control flow graph array in size if it is full.
if ( process->cfg_length == process->cfg_allocated )
{
size_t new_allocated = process->cfg_allocated * 2;
if ( new_allocated == 0 )
new_allocated = 16;
struct control_flow* new_cfg = new struct control_flow[new_allocated];
if ( !new_cfg )
CFIViolation("recording CFG: Out of memory");
if ( process->cfg )
memcpy(new_cfg, process->cfg, sizeof(struct control_flow) * process->cfg_length);
delete[] process->cfg;
process->cfg = new_cfg;
process->cfg_allocated = new_allocated;
}
// Add the control flow to the (unsorted) control flow graph.
process->cfg[process->cfg_length++] = *cf;
ioctx_t ctx;
SetupKernelIOCtx(&ctx);
// Write the control flow to the trace file.
if ( process->cfi_dump->write(&ctx, (const uint8_t*) cf, sizeof(*cf)) != sizeof(*cf) )
CFIViolation("recording CFG: Trace file write failure");
return;
}
// If there is no control flow graph loaded, don't enforce.
if ( !process->cfg )
return;
// Use binary search to look up the control flow in the sorted control
// flow graph.
if ( bsearch(cf, process->cfg, process->cfg_length,
sizeof(struct control_flow), SearchControlFlow) )
return;
CFIViolation("control flow failure");
}
// Call a function, but only after verifying the control flow using the
// control flow graph. The destination is in the rax register.
void SecureIndirectCallHandler(struct interrupt_context* intctx, void* ctx)
{
struct control_flow cf;
cf.from = intctx->rip - 2 /* point to start of int 0x92, not the end */;
cf.to = intctx->rax;
CheckControlFlow(&cf);
SecureCallHandler(intctx, ctx);
}
// Jump to code, but only after verifying the control flow. The destination
// is on the stack, after the red zone has skipped.
void SecureIndirectJmpHandler(struct interrupt_context* intctx, void*)
{
uintptr_t dest;
if ( !CopyFromUser(&dest, (uintptr_t*) intctx->rsp, sizeof(dest)) )
CFIViolation("secure indirect jmp: Failed to read stack parameter");
intctx->rsp += 8; // Unwind the parameter.
intctx->rsp += 128; // Unwind the red zone.
struct control_flow cf;
cf.from = intctx->rip - 2 /* point to start of int 0x92, not the end */;
cf.to = dest;
CheckControlFlow(&cf);
intctx->rip = dest;
}
// The secure setjmp handler. Registers a jmp_buf object such that it can be
// securely longjmp'd to later.
void SecureSetJmpHandler(struct interrupt_context* intctx, void*)
{
Thread* thread = CurrentThread();
// TODO: Check if overwriting existing setjmp.
if ( thread->setjmp_count == 16 )
CFIViolation("secure setjmp: jmp_buf registation stack overflow");
if ( thread->call_count == 0 )
CFIViolation("secure setjmp: secure stack underflow");
struct secure_setjmp* setjmp = &thread->setjmps[thread->setjmp_count++];
// Subtracted by one here, to remember the function that called setjmp, not
// the libc setjmp function itself.
setjmp->call_level = thread->call_count - 1;
setjmp->rip = thread->calls[thread->call_count - 1];
setjmp->jmpbuf_ptr = intctx->rdi;
}
// The secure longjmp handler.
void SecureLongJmpHandler(struct interrupt_context* intctx, void*)
{
Thread* thread = CurrentThread();
// Search for a maatching registation.
for ( size_t i = 0; i < thread->setjmp_count; i++ )
{
struct secure_setjmp* setjmp = &thread->setjmps[i];
if ( setjmp->jmpbuf_ptr != intctx->rdi )
continue;
jmp_buf buf;
if ( !CopyFromUser(&buf, (const jmp_buf*) setjmp->jmpbuf_ptr, sizeof(buf)) )
CFIViolation("secure longjmp: Failed to read jmp_buf");
// Restore all the registers from the jmp_buf.
intctx->rbx = buf[0];
intctx->rsp = buf[1];
intctx->rbp = buf[2];
intctx->r12 = buf[3];
intctx->r13 = buf[4];
intctx->r14 = buf[5];
intctx->r15 = buf[6];
intctx->rip = setjmp->rip;
intctx->rax = intctx->rsi;
// Unwind the protected shadow stack.
thread->call_count = setjmp->call_level;
// jmp_buf registations may have gone out of scope.
while ( thread->setjmp_count &&
thread->call_count < thread->setjmps[thread->setjmp_count-1].call_level )
thread->setjmp_count--;
return;
}
CFIViolation("secure longjmp: No such jmp_buf registation");
}
// Temporarily to see if this is the source of the assertion failure.
void DispatchHandlerWrap(struct interrupt_context* intctx, void* user)
@ -243,6 +460,13 @@ void Init()
RegisterRawHandler(130, isr130, true, true);
RegisterRawHandler(131, isr131, true, true);
RegisterRawHandler(132, thread_exit_handler, true, false);
// Register the raw interrupt handlers for the 6 new CFI interrupts.
RegisterRawHandler(0x90, secure_call_handler, true, true);
RegisterRawHandler(0x91, secure_ret_handler, true, true);
RegisterRawHandler(0x92, secure_indirect_call_handler, true, true);
RegisterRawHandler(0x93, secure_indirect_jmp_handler, true, true);
RegisterRawHandler(0x94, secure_setjmp_handler, true, true);
RegisterRawHandler(0x95, secure_longjmp_handler, true, true);
Scheduler__InterruptYieldCPU_handler.handler = Scheduler::InterruptYieldCPU;
RegisterHandler(129, &Scheduler__InterruptYieldCPU_handler);
@ -252,6 +476,19 @@ void Init()
RegisterHandler(131, &Signal__ReturnHandler_handler);
Scheduler__ThreadExitCPU_handler.handler = Scheduler::ThreadExitCPU;
RegisterHandler(132, &Scheduler__ThreadExitCPU_handler);
CFI__secure_call_handler.handler = SecureCallHandler;
// Register handles for each of the six new CFI interrupts.
RegisterHandler(0x90, &CFI__secure_call_handler);
CFI__secure_ret_handler.handler = SecureRetHandler;
RegisterHandler(0x91, &CFI__secure_ret_handler);
CFI__secure_indirect_call_handler.handler = SecureIndirectCallHandler;
RegisterHandler(0x92, &CFI__secure_indirect_call_handler);
CFI__secure_indirect_jmp_handler.handler = SecureIndirectJmpHandler;
RegisterHandler(0x93, &CFI__secure_indirect_jmp_handler);
CFI__secure_setjmp_handler.handler = SecureSetJmpHandler;
RegisterHandler(0x94, &CFI__secure_setjmp_handler);
CFI__secure_longjmp_handler.handler = SecureLongJmpHandler;
RegisterHandler(0x95, &CFI__secure_longjmp_handler);
IDT::Set(interrupt_table, NUM_INTERRUPTS);

View file

@ -807,13 +807,13 @@ headers:
# libk
%.libk.o: %.c
$(CC) -c $< -o $@ $(LIBK_CPPFLAGS) $(LIBK_FLAGS) $(LIBK_CFLAGS)
CFI_ENABLE=0 $(CC) -c $< -o $@ $(LIBK_CPPFLAGS) $(LIBK_FLAGS) $(LIBK_CFLAGS)
%.libk.o: %.c++
$(CXX) -c $< -o $@ $(LIBK_CPPFLAGS) $(LIBK_FLAGS) $(LIBK_CXXFLAGS)
CFI_ENABLE=0 $(CXX) -c $< -o $@ $(LIBK_CPPFLAGS) $(LIBK_FLAGS) $(LIBK_CXXFLAGS)
%.libk.o: %.S
$(CC) -c $< -o $@ $(LIBK_CPPFLAGS) $(LIBK_FLAGS) $(LIBK_CFLAGS)
CFI_ENABLE=0 $(CC) -c $< -o $@ $(LIBK_CPPFLAGS) $(LIBK_FLAGS) $(LIBK_CFLAGS)
clean:
rm -f *.o */*.o */*/*.o *.a

View file

@ -39,20 +39,24 @@ __start:
pushq %rsi
pushq %rdi
pushq %rcx
call initialize_standard_library
mov $initialize_standard_library, %rax
int $0x90
# Run the global constructors.
call _init
mov $_init, %rax
int $0x90
# Run main
popq %rdx # Note! envp is now %rdx (previously %rcx)
popq %rdi
popq %rsi
addq $8, %rsp
call main
mov $main, %rax
int $0x90
# Terminate the process with main's exit code.
movl %eax, %edi
call exit
mov $exit, %rax
int $0x90
.size _start, .-_start
.size __start, .-__start

View file

@ -20,9 +20,9 @@
.section .init
/* gcc will nicely put the contents of crtend.o's .init section here. */
popq %rbp
ret
int $0x91
.section .fini
/* gcc will nicely put the contents of crtend.o's .fini section here. */
popq %rbp
ret
int $0x91

View file

@ -52,21 +52,24 @@ __sfork:
popq %rax
movq %rax, (17 * 8)(%rsi) # rflags
movl $MSRID_FSBASE, %edi
call rdmsr
mov $rdmsr, %rax
int $0x90
movq 0(%rsp), %rsi
movq %rax, (18 * 8)(%rsi) # fsbase
movl $MSRID_GSBASE, %edi
call rdmsr
mov $rdmsr, %rax
int $0x90
movq 0(%rsp), %rsi
movq %rax, (19 * 8)(%rsi) # gsbase
movq 8(%rsp), %rdi
call tfork
mov $tfork, %rax
int $0x90
.Lafter_fork:
# The value in %rax determines whether we are child or parent. There is no
# need to clean up the stack from the above pushes, leaveq sets %rsp to %rbp
# which does that for us.
leaveq
retq
int $0x91
.size __sfork, . - __sfork

View file

@ -50,21 +50,23 @@ sigsetjmp:
lea (9 * 8)(%rdi), %rdx # oldset
xor %esi, %esi # set
xor %edi, %edi # how (ignored because set is NULL)
call sigprocmask # assumes sigprocmask is per-thread on Sortix
mov $sigprocmask, %rax # assumes sigprocmask is per-thread on Sortix
int $0x90
pop %rsi
pop %rdi
2: mov 0(%rsp), %rax
mov %rbx, (0 * 8)(%rdi)
mov %rsp, (1 * 8)(%rdi)
2: mov %rbx, (0 * 8)(%rdi)
mov %rsp, %rdx
add $8, %rdx
mov %rdx, (1 * 8)(%rdi)
mov %rbp, (2 * 8)(%rdi)
mov %r12, (3 * 8)(%rdi)
mov %r13, (4 * 8)(%rdi)
mov %r14, (5 * 8)(%rdi)
mov %r15, (6 * 8)(%rdi)
mov %rax, (7 * 8)(%rdi)
mov %rsi, (8 * 8)(%rdi)
int $0x94
xorl %eax, %eax
ret
int $0x91
.size sigsetjmp, . - sigsetjmp
.global longjmp
@ -87,18 +89,9 @@ siglongjmp:
leaq (9 * 8)(%rdi), %rsi # set
movl $SIG_SETMASK, %edi # how
xorl %edx, %edx # oldset
call sigprocmask
mov $sigprocmask, %rax
int $0x90
popq %rsi
popq %rdi
2: movq (0 * 8)(%rdi), %rbx
movq (1 * 8)(%rdi), %rsp
movq (2 * 8)(%rdi), %rbp
movq (3 * 8)(%rdi), %r12
movq (4 * 8)(%rdi), %r13
movq (5 * 8)(%rdi), %r14
movq (6 * 8)(%rdi), %r15
movq (7 * 8)(%rdi), %rax
movq %rax, 0(%rsp)
movl %esi, %eax
ret
2: int $0x95
.size siglongjmp, . - siglongjmp

View file

@ -37,5 +37,5 @@ asm_syscall: /* syscall num in %rax. */
mov %ecx, errno@tpoff(%rsi)
1:
pop %rbp
ret
int $0x91
.size asm_syscall, .-asm_syscall

View file

@ -21,4 +21,4 @@ ENTRY(__ieee754_acos)
fxch %st(1)
fpatan
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -20,4 +20,4 @@ ENTRY(__ieee754_asin)
fsqrt /* sqrt (1 - x^2) */
fpatan
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -15,4 +15,4 @@ ENTRY(__ieee754_atan2)
fldl ARG_DOUBLE_TWO
fpatan
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -15,4 +15,4 @@ ENTRY(__ieee754_atan2f)
flds ARG_FLOAT_TWO
fpatan
XMM_FLOAT_EPILOGUE
ret
int $0x91

View file

@ -88,7 +88,7 @@ ENTRY(__ieee754_exp)
fldcw CWSTORE_SAV
1:
XMM_DOUBLE_EPILOGUE
ret
int $0x91
x_Inf_or_NaN:
/*
* Return 0 if x is -Inf. Otherwise just return x, although the
@ -100,9 +100,9 @@ x_Inf_or_NaN:
jne x_not_minus_Inf
fldz
XMM_DOUBLE_EPILOGUE
ret
int $0x91
x_not_minus_Inf:
fldl ARG_DOUBLE_ONE
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -35,7 +35,7 @@ ENTRY(__ieee754_expf)
fscale /* e^x */
fstp %st(1)
XMM_FLOAT_EPILOGUE
ret
int $0x91
x_Inf_or_NaN:
/*
@ -47,9 +47,9 @@ x_Inf_or_NaN:
jne x_not_minus_Inf
fldz
XMM_FLOAT_EPILOGUE
ret
int $0x91
x_not_minus_Inf:
flds ARG_FLOAT_ONE
XMM_FLOAT_EPILOGUE
ret
int $0x91

View file

@ -20,4 +20,4 @@ ENTRY(__ieee754_fmod)
jc 1b
fstp %st(1)
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -15,4 +15,4 @@ ENTRY(__ieee754_log)
fldl ARG_DOUBLE_ONE
fyl2x
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -15,4 +15,4 @@ ENTRY(__ieee754_log10)
fldl ARG_DOUBLE_ONE
fyl2x
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -15,4 +15,4 @@ ENTRY(__ieee754_log10f)
flds ARG_FLOAT_ONE
fyl2x
XMM_FLOAT_EPILOGUE
ret
int $0x91

View file

@ -15,4 +15,4 @@ ENTRY(__ieee754_log2)
fldl ARG_DOUBLE_ONE
fyl2x
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -15,4 +15,4 @@ ENTRY(__ieee754_log2f)
flds ARG_FLOAT_ONE
fyl2x
XMM_FLOAT_EPILOGUE
ret
int $0x91

View file

@ -15,4 +15,4 @@ ENTRY(__ieee754_logf)
flds ARG_FLOAT_ONE
fyl2x
XMM_FLOAT_EPILOGUE
ret
int $0x91

View file

@ -19,4 +19,4 @@ ENTRY(__ieee754_remainder)
jc 1b
fstp %st(1)
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -19,4 +19,4 @@ ENTRY(__ieee754_remainderf)
jc 1b
fstp %st(1)
XMM_FLOAT_EPILOGUE
ret
int $0x91

View file

@ -16,4 +16,4 @@ ENTRY(__ieee754_scalb)
fscale
fstp %st(1) /* bug fix for fp stack overflow */
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -15,4 +15,4 @@ ENTRY(__ieee754_scalbf)
flds ARG_FLOAT_ONE
fscale
XMM_FLOAT_EPILOGUE
ret
int $0x91

View file

@ -14,4 +14,4 @@ ENTRY(__ieee754_sqrt)
#else
sqrtsd %xmm0,%xmm0
#endif
ret
int $0x91

View file

@ -14,4 +14,4 @@ ENTRY(__ieee754_sqrtf)
#else
sqrtss %xmm0,%xmm0
#endif
ret
int $0x91

View file

@ -16,8 +16,8 @@ ENTRY(lrint)
fistpl (%esp)
movl (%esp),%eax
leave
ret
int $0x91
#else
cvtsd2siq %xmm0,%rax
ret
int $0x91
#endif

View file

@ -15,4 +15,4 @@ ENTRY(atan)
fld1
fpatan
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -15,4 +15,4 @@ ENTRY(atanf)
fld1
fpatan
XMM_FLOAT_EPILOGUE
ret
int $0x91

View file

@ -42,4 +42,4 @@ ENTRY(ceil)
fstpl -8(%rsp)
movsd -8(%rsp),%xmm0
#endif
ret
int $0x91

View file

@ -40,4 +40,4 @@ ENTRY(ceilf)
fstps -4(%rsp)
movss -4(%rsp),%xmm0
#endif
ret
int $0x91

View file

@ -35,4 +35,4 @@ ENTRY(copysign)
pand %xmm3,%xmm0
por %xmm1,%xmm0
#endif
ret
int $0x91

View file

@ -34,4 +34,4 @@ ENTRY(copysignf)
pand %xmm3,%xmm0
por %xmm1,%xmm0
#endif
ret
int $0x91

View file

@ -17,7 +17,7 @@ ENTRY(cos)
andw $0x400,%ax
jnz 1f
XMM_DOUBLE_EPILOGUE
ret
int $0x91
1: fldpi
fadd %st(0)
fxch %st(1)
@ -28,4 +28,4 @@ ENTRY(cos)
fstp %st(1)
fcos
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -15,4 +15,4 @@ ENTRY(cosf)
flds ARG_FLOAT_ONE
fcos
XMM_FLOAT_EPILOGUE
ret
int $0x91

View file

@ -23,4 +23,4 @@ ENTRY(finite)
cmpq %rdi,%rsi
setne %al
#endif
ret
int $0x91

View file

@ -22,4 +22,4 @@ ENTRY(finitef)
cmpl $0x7ff00000,%esi
setne %al
#endif
ret
int $0x91

View file

@ -40,4 +40,4 @@ ENTRY(floor)
fstpl -8(%rsp)
movsd -8(%rsp),%xmm0
#endif
ret
int $0x91

View file

@ -40,4 +40,4 @@ ENTRY(floorf)
fstps -4(%rsp)
movss -4(%rsp),%xmm0
#endif
ret
int $0x91

View file

@ -29,4 +29,4 @@ ENTRY(ilogb)
fistpl -8(%rsp)
movl -8(%rsp),%eax
#endif
ret
int $0x91

View file

@ -29,4 +29,4 @@ ENTRY(ilogbf)
fistpl -4(%rsp)
movl -4(%rsp),%eax
#endif
ret
int $0x91

View file

@ -21,4 +21,4 @@ ENTRY(ilogbl)
fistpl -4(%rsp)
movl -4(%rsp), %eax
#endif
ret
int $0x91

View file

@ -65,7 +65,7 @@ use_fyl2x:
faddp
fyl2x
XMM_DOUBLE_EPILOGUE
ret
int $0x91
.align 4
use_fyl2xp1:
@ -73,4 +73,4 @@ use_fyl2xp1:
fldl ARG_DOUBLE_ONE
fyl2xp1
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -65,7 +65,7 @@ use_fyl2x:
faddp
fyl2x
XMM_FLOAT_EPILOGUE
ret
int $0x91
.align 4
use_fyl2xp1:
@ -73,4 +73,4 @@ use_fyl2xp1:
flds ARG_FLOAT_ONE
fyl2xp1
XMM_FLOAT_EPILOGUE
ret
int $0x91

View file

@ -15,4 +15,4 @@ ENTRY(logb)
fxtract
fstp %st
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -15,4 +15,4 @@ ENTRY(logbf)
fxtract
fstp %st
XMM_FLOAT_EPILOGUE
ret
int $0x91

View file

@ -13,4 +13,4 @@ ENTRY(logbl)
fldt ARG_LONG_DOUBLE_ONE
fxtract
fstp %st
ret
int $0x91

View file

@ -103,4 +103,4 @@ ENTRY(modf)
L1:
#endif
leave
ret
int $0x91

View file

@ -14,4 +14,4 @@ ENTRY(rint)
fldl ARG_DOUBLE_ONE
frndint
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -14,4 +14,4 @@ ENTRY(rintf)
flds ARG_FLOAT_ONE
frndint
XMM_FLOAT_EPILOGUE
ret
int $0x91

View file

@ -27,4 +27,4 @@ ENTRY(_scalbn)
fscale
fstp %st(1) /* clean up stack */
#endif
ret
int $0x91

View file

@ -27,4 +27,4 @@ ENTRY(_scalbnf)
fscale
fstp %st(1) /* clean up stack */
#endif
ret
int $0x91

View file

@ -24,4 +24,4 @@ ENTRY(_scalbnl)
fscale
fstp %st(1) /* clean up stack */
#endif
ret
int $0x91

View file

@ -15,4 +15,4 @@ ENTRY(significand)
fxtract
fstp %st(1)
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -15,4 +15,4 @@ ENTRY(significandf)
fxtract
fstp %st(1)
XMM_FLOAT_EPILOGUE
ret
int $0x91

View file

@ -17,7 +17,7 @@ ENTRY(sin)
andw $0x400,%ax
jnz 1f
XMM_DOUBLE_EPILOGUE
ret
int $0x91
1: fldpi
fadd %st(0)
fxch %st(1)
@ -28,4 +28,4 @@ ENTRY(sin)
fstp %st(1)
fsin
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -15,4 +15,4 @@ ENTRY(sinf)
flds ARG_FLOAT_ONE
fsin
XMM_FLOAT_EPILOGUE
ret
int $0x91

View file

@ -18,7 +18,7 @@ ENTRY(tan)
jnz 1f
fstp %st(0)
XMM_DOUBLE_EPILOGUE
ret
int $0x91
1: fldpi
fadd %st(0)
fxch %st(1)
@ -30,4 +30,4 @@ ENTRY(tan)
fptan
fstp %st(0)
XMM_DOUBLE_EPILOGUE
ret
int $0x91

View file

@ -16,4 +16,4 @@ ENTRY(tanf)
fptan
fstp %st(0)
XMM_FLOAT_EPILOGUE
ret
int $0x91

View file

@ -14,4 +14,4 @@ __signmask:
ENTRY(fabs)
movsd __signmask(%rip),%xmm1
andpd %xmm1,%xmm0
ret
int $0x91

View file

@ -18,4 +18,4 @@ ENTRY(__flt_rounds)
movl $0x2d, %eax /* 0x2d = 00.10.11.01 */
sarl %cl, %eax /* 0,1,2,3 -> 1,3,2,0 */
andl $3, %eax
ret
int $0x91

View file

@ -23,4 +23,4 @@ ENTRY(fpgetmask)
movl -4(%rsp),%eax
notl %eax
andl $63,%eax
ret
int $0x91

View file

@ -22,4 +22,4 @@ ENTRY(fpgetprec)
movl -4(%rsp),%eax
rorl $8,%eax
andl $3,%eax
ret
int $0x91

View file

@ -21,4 +21,4 @@ ENTRY(fpgetround)
fnstcw -4(%rsp)
movl -4(%rsp), %eax
andl $0x00000c00, %eax
ret
int $0x91

View file

@ -24,4 +24,4 @@ ENTRY(fpgetsticky)
movl -4(%rsp),%eax
orl -8(%rsp),%eax
andl $63,%eax
ret
int $0x91

View file

@ -41,4 +41,4 @@ ENTRY(fpsetmask)
notl %eax
andl $0x0000003f, %eax
ret
int $0x91

View file

@ -36,4 +36,4 @@ ENTRY(fpsetprec)
movl %edx,-4(%rsp)
fldcw -4(%rsp)
ret
int $0x91

View file

@ -39,4 +39,4 @@ ENTRY(fpsetround)
movl %edx,-4(%rsp)
ldmxcsr -4(%rsp)
ret
int $0x91

View file

@ -42,4 +42,4 @@ ENTRY(fpsetsticky)
ldmxcsr -32(%rsp)
fldenv -28(%rsp)
ret
int $0x91

View file

@ -4,7 +4,7 @@ include ../build-aux/compiler.mak
include ../build-aux/version.mak
include ../build-aux/dirs.mak
OPTLEVEL?=-g -O2
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
CXXFLAGS?=$(OPTLEVEL)
CXXFLAGS:=$(CXXFLAGS) -std=gnu++0x -Wall -Wextra -fno-exceptions -fno-rtti -msse -msse2

4
utils/.gitignore vendored
View file

@ -1,6 +1,10 @@
*.o
basename
cat
cfg
cfi-test
cfi-test-2
cfi-test-3
chkblayout
chmod
chroot

View file

@ -13,6 +13,10 @@ CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\"
BINARIES_EXCEPT_INSTALL:=\
basename \
cat \
cfg \
cfi-test \
cfi-test-2 \
cfi-test-3 \
chkblayout \
chmod \
chroot \
@ -79,6 +83,9 @@ install: all
mkdir -p $(DESTDIR)$(BINDIR)
install $(BINARIES_EXCEPT_INSTALL) $(DESTDIR)$(BINDIR)
install xinstall $(DESTDIR)$(BINDIR)/install
mkdir -p $(DESTDIR)$(SBINDIR)
install cfion $(DESTDIR)$(SBINDIR)/cfion
install cfioff $(DESTDIR)$(SBINDIR)/cfioff
%: %.c
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $< -o $@

76
utils/cfg.c Normal file
View file

@ -0,0 +1,76 @@
#include <sys/stat.h>
#include <err.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
struct control_flow
{
uintptr_t from;
uintptr_t to;
};
struct control_flow* cfg = NULL;
size_t cfg_length = 0;
static int cfcmp(const void* a_ptr, const void* b_ptr)
{
const struct control_flow* a = (const struct control_flow*) a_ptr;
const struct control_flow* b = (const struct control_flow*) b_ptr;
if ( b->from < a->from )
return 1;
if ( b->from > a->from )
return -1;
if ( b->to < a->to )
return 1;
if ( b->to > a->to )
return -1;
return 0;
}
int main(int argc, char* argv[])
{
for ( int i = 1; i < argc; i++ )
{
FILE* fp = fopen(argv[i], "r");
if ( !fp )
err(1, "%s", argv[i]);
struct stat st;
if ( fstat(fileno(fp), &st) < 0 )
err(1, "stat: %s", argv[i]);
size_t entries = st.st_size / sizeof(struct control_flow);
size_t new_length = cfg_length + entries;
size_t new_size = new_length * sizeof(struct control_flow);
cfg = (struct control_flow*) realloc(cfg, new_size);
if ( !cfg )
err(1, "malloc");
if ( fread(cfg + cfg_length, sizeof(struct control_flow), entries, fp) != entries )
err(1, "%s", argv[i]);
cfg_length = new_length;
fclose(fp);
}
#if 0
for ( size_t i = 0; i < cfg_length; i++ )
fprintf(stderr, "[%zu] 0x%lX -> 0x%lX\n", i, cfg[i].from, cfg[i].to);
#endif
qsort(cfg, cfg_length, sizeof(struct control_flow), cfcmp);
size_t new_length = 0;
for ( size_t i = 0; i < cfg_length; i++ )
{
if ( i && cfg[i-1].from == cfg[i].from && cfg[i-1].to == cfg[i].to )
continue;
cfg[new_length++] = cfg[i];
}
cfg_length = new_length;
fflush(stdout);
if ( fwrite(cfg, sizeof(struct control_flow), cfg_length, stdout) != cfg_length )
err(1, "stdout");
if ( ferror(stdout) || fflush(stdout) == EOF )
err(1, "stdout");
#if 1
for ( size_t i = 0; i < cfg_length; i++ )
fprintf(stderr, "[%zu] 0x%lX -> 0x%lX\n", i, cfg[i].from, cfg[i].to);
#endif
return 0;
}

19
utils/cfi-test-2.c Normal file
View file

@ -0,0 +1,19 @@
#include <stdio.h>
#include <string.h>
#pragma GCC optimize 0
void gpf(void)
{
char buffer[16];
memset(buffer, 0x11, 128); /* buffer overrun */
}
int main(void)
{
printf("begun\n");
void (*gpf_ptr)(void) = gpf;
gpf_ptr();
printf("done\n");
return 42;
}

36
utils/cfi-test-3.c Normal file
View file

@ -0,0 +1,36 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#pragma GCC optimize 0
void yes_function(int param)
{
printf("yes: %i\n", param);
}
void no_function(int param)
{
if ( param )
system("undesirable-command");
printf("no: %i\n", param);
}
void adversary(void)
{
uintptr_t array[1];
for ( size_t i = 0; i < 16; i++ )
array[i] = (uintptr_t) no_function; /* buffer overrun */
(void) array;
}
int main(int argc, char* argv[])
{
int param = 2 <= argc ? atoi(argv[1]) : 1;
void (*ptr)(int) = no_function;
if ( param )
ptr = yes_function;
adversary();
ptr(param);
return 0;
}

18
utils/cfi-test.c Normal file
View file

@ -0,0 +1,18 @@
#include <stdio.h>
#include <string.h>
#pragma GCC optimize 0
void gpf(void)
{
char buffer[16];
memset(buffer, 0x11, 128); /* buffer overrun */
}
int main(void)
{
printf("begun\n");
gpf();
printf("done\n");
return 42;
}

9
utils/cfioff Executable file
View file

@ -0,0 +1,9 @@
#!/bin/sh
for program in find grep rm; do
rm -f "/bin/$program.cfg"
done
find / | \
grep -E '\.cfg$' | \
while read file; do
rm -v "$file"
done

14
utils/cfion Executable file
View file

@ -0,0 +1,14 @@
#!/bin/sh
find / | \
grep -E '\.[[:digit:]]+\.cfi$' | \
sed -E 's/\.[[:digit:]]+\.cfi//g' |
sort -u |
while read program; do
echo "$program"
cfg "$program".*.cfi > "$program.cfg.new" 2>/dev/null
rm "$program".*.cfi
mv "$program.cfg.new" "$program.0.cfi"
if [ "$program" != "/bin/rm" ] && [ "$program" != "/bin/dash" ]; then
cp "$program.0.cfi" "$program.cfg"
fi
done