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:
parent
5351bd0719
commit
666b84846e
87 changed files with 768 additions and 110 deletions
|
@ -41,7 +41,9 @@ SORTIX_PORTS_DIR=$(make_dir_path_absolute "$SORTIX_PORTS_DIR")
|
||||||
SORTIX_REPOSITORY_DIR=$(make_dir_path_absolute "$SORTIX_REPOSITORY_DIR")
|
SORTIX_REPOSITORY_DIR=$(make_dir_path_absolute "$SORTIX_REPOSITORY_DIR")
|
||||||
|
|
||||||
# Decide the optimization options with which the ports will be built.
|
# 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_OPTLEVEL+x}" ]; then PORTS_OPTLEVEL="$OPTLEVEL"; fi
|
||||||
if [ -z "${PORTS_CFLAGS+x}" ]; then PORTS_CFLAGS="$PORTS_OPTLEVEL"; fi
|
if [ -z "${PORTS_CFLAGS+x}" ]; then PORTS_CFLAGS="$PORTS_OPTLEVEL"; fi
|
||||||
if [ -z "${PORTS_CXXFLAGS+x}" ]; then PORTS_CXXFLAGS="$PORTS_OPTLEVEL"; fi
|
if [ -z "${PORTS_CXXFLAGS+x}" ]; then PORTS_CXXFLAGS="$PORTS_OPTLEVEL"; fi
|
||||||
|
|
|
@ -121,7 +121,9 @@ ifdef SYSROOT
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Determine default optimization level.
|
# 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)
|
DEFAULT_OPTLEVEL_FOR_BUILD:=$(DEFAULT_GENERIC_OPTLEVEL_BASE)
|
||||||
ifeq ($(BUILD_IS_SORTIX),1)
|
ifeq ($(BUILD_IS_SORTIX),1)
|
||||||
DEFAULT_OPTLEVEL_FOR_BUILD+=
|
DEFAULT_OPTLEVEL_FOR_BUILD+=
|
||||||
|
@ -130,3 +132,8 @@ DEFAULT_OPTLEVEL:=$(DEFAULT_GENERIC_OPTLEVEL_BASE)
|
||||||
ifeq ($(HOST_IS_SORTIX),1)
|
ifeq ($(HOST_IS_SORTIX),1)
|
||||||
DEFAULT_OPTLEVEL+=
|
DEFAULT_OPTLEVEL+=
|
||||||
endif
|
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
|
||||||
|
|
|
@ -207,11 +207,13 @@ vgafont.h: vgafont.f16
|
||||||
|
|
||||||
*.cpp */*.cpp */*/*.cpp: | kb/default-kblayout.h vgafont.h
|
*.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
|
%.o: %.cpp
|
||||||
$(CXX) -c $< -o $@ $(CPPFLAGS) $(CXXFLAGS)
|
CFI_ENABLE=0 $(CXX) -c $< -o $@ $(CPPFLAGS) $(CXXFLAGS)
|
||||||
|
|
||||||
%.o: %.S
|
%.o: %.S
|
||||||
$(CXX) -c $< -o $@ $(CPPFLAGS) $(CXXFLAGS)
|
CFI_ENABLE=0 $(CXX) -c $< -o $@ $(CPPFLAGS) $(CXXFLAGS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(ALLOBJS) sortix.bin
|
rm -f $(ALLOBJS) sortix.bin
|
||||||
|
|
|
@ -52,6 +52,13 @@ struct ioctx_struct;
|
||||||
typedef struct ioctx_struct ioctx_t;
|
typedef struct ioctx_struct ioctx_t;
|
||||||
struct segment;
|
struct segment;
|
||||||
|
|
||||||
|
// The control flow graph is a set of such pairs.
|
||||||
|
struct control_flow
|
||||||
|
{
|
||||||
|
uintptr_t from;
|
||||||
|
uintptr_t to;
|
||||||
|
};
|
||||||
|
|
||||||
class Process
|
class Process
|
||||||
{
|
{
|
||||||
friend void Process__OnLastThreadExit(void*);
|
friend void Process__OnLastThreadExit(void*);
|
||||||
|
@ -82,6 +89,10 @@ private:
|
||||||
Ref<MountTable> mtable;
|
Ref<MountTable> mtable;
|
||||||
Ref<DescriptorTable> dtable;
|
Ref<DescriptorTable> dtable;
|
||||||
|
|
||||||
|
// If this file is open, then the indirect control flow is recorded to it.
|
||||||
|
public:
|
||||||
|
Ref<Descriptor> cfi_dump;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Ref<ProcessTable> ptable;
|
Ref<ProcessTable> ptable;
|
||||||
|
|
||||||
|
@ -145,6 +156,13 @@ public:
|
||||||
kthread_mutex_t segment_write_lock;
|
kthread_mutex_t segment_write_lock;
|
||||||
kthread_mutex_t segment_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:
|
public:
|
||||||
kthread_mutex_t user_timers_lock;
|
kthread_mutex_t user_timers_lock;
|
||||||
UserTimer user_timers[PROCESS_TIMER_NUM_MAX];
|
UserTimer user_timers[PROCESS_TIMER_NUM_MAX];
|
||||||
|
|
|
@ -53,6 +53,28 @@ Thread* RunKernelThread(Process* process, void (*entry)(void*), void* user,
|
||||||
size_t stacksize = 0);
|
size_t stacksize = 0);
|
||||||
Thread* RunKernelThread(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
|
class Thread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -85,6 +107,16 @@ public:
|
||||||
bool signal_single;
|
bool signal_single;
|
||||||
Clock execute_clock;
|
Clock execute_clock;
|
||||||
Clock system_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:
|
public:
|
||||||
void HandleSignal(struct interrupt_context* intctx);
|
void HandleSignal(struct interrupt_context* intctx);
|
||||||
|
|
|
@ -142,6 +142,11 @@ Process::Process()
|
||||||
segment_write_lock = KTHREAD_MUTEX_INITIALIZER;
|
segment_write_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
segment_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;
|
user_timers_lock = KTHREAD_MUTEX_INITIALIZER;
|
||||||
memset(&user_timers, 0, sizeof(user_timers));
|
memset(&user_timers, 0, sizeof(user_timers));
|
||||||
// alarm_timer initialized in member constructor.
|
// alarm_timer initialized in member constructor.
|
||||||
|
@ -166,6 +171,11 @@ Process::~Process()
|
||||||
assert(!mtable);
|
assert(!mtable);
|
||||||
assert(!cwd);
|
assert(!cwd);
|
||||||
assert(!root);
|
assert(!root);
|
||||||
|
// TODO: Investigate whether cfi_dump needs to be specially handled during
|
||||||
|
// process shutdown.
|
||||||
|
|
||||||
|
// Delete the CFG.
|
||||||
|
delete[] cfg;
|
||||||
|
|
||||||
assert(ptable);
|
assert(ptable);
|
||||||
ptable->Free(pid);
|
ptable->Free(pid);
|
||||||
|
@ -680,6 +690,8 @@ Process* Process::Fork()
|
||||||
kthread_mutex_lock(&ptrlock);
|
kthread_mutex_lock(&ptrlock);
|
||||||
clone->root = root;
|
clone->root = root;
|
||||||
clone->cwd = cwd;
|
clone->cwd = cwd;
|
||||||
|
// TODO: fork cfi_dump
|
||||||
|
// TODO: fork cfg
|
||||||
kthread_mutex_unlock(&ptrlock);
|
kthread_mutex_unlock(&ptrlock);
|
||||||
|
|
||||||
kthread_mutex_lock(&idlock);
|
kthread_mutex_lock(&idlock);
|
||||||
|
@ -742,6 +754,13 @@ void Process::ResetForExecute()
|
||||||
signal_stack->ss_flags = SS_DISABLE;
|
signal_stack->ss_flags = SS_DISABLE;
|
||||||
|
|
||||||
ResetAddressSpace();
|
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,
|
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;
|
delete[] program_image_path;
|
||||||
program_image_path = programname_clone; programname_clone = NULL;
|
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;
|
uintptr_t userspace_addr;
|
||||||
size_t userspace_size;
|
size_t userspace_size;
|
||||||
Memory::GetUserVirtualArea(&userspace_addr, &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, ®s.sigmask, sizeof(sigset_t));
|
memcpy(&thread->signal_mask, ®s.sigmask, sizeof(sigset_t));
|
||||||
memcpy(&thread->signal_stack, ®s.altstack, sizeof(stack_t));
|
memcpy(&thread->signal_stack, ®s.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);
|
StartKernelThread(thread);
|
||||||
|
|
||||||
return child_process->pid;
|
return child_process->pid;
|
||||||
|
|
|
@ -713,7 +713,7 @@ retry_another_signal:
|
||||||
handler_regs.eip = (unsigned long) handler_ptr;
|
handler_regs.eip = (unsigned long) handler_ptr;
|
||||||
handler_regs.eflags &= ~FLAGS_DIRECTION;
|
handler_regs.eflags &= ~FLAGS_DIRECTION;
|
||||||
#elif defined(__x86_64__)
|
#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 -= sizeof(stack_frame);
|
||||||
stack_location &= ~(16UL-1UL); /* 16-byte align */
|
stack_location &= ~(16UL-1UL); /* 16-byte align */
|
||||||
struct stack_frame* stack = (struct stack_frame*) stack_location;
|
struct stack_frame* stack = (struct stack_frame*) stack_location;
|
||||||
|
@ -803,6 +803,25 @@ retry_another_signal:
|
||||||
if ( (signal_single = signal_count == 1) )
|
if ( (signal_single = signal_count == 1) )
|
||||||
signal_single_frame = (uintptr_t) stack;
|
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.
|
// Run the signal handler by returning to user-space.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -902,6 +921,18 @@ void Thread::HandleSigreturn(struct interrupt_context* intctx)
|
||||||
DecodeMachineContext(&stack_frame.ucontext.uc_mcontext, &resume_regs);
|
DecodeMachineContext(&stack_frame.ucontext.uc_mcontext, &resume_regs);
|
||||||
Scheduler::LoadInterruptedContext(intctx, &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 )
|
if ( signal_count != SIZE_MAX )
|
||||||
signal_count--;
|
signal_count--;
|
||||||
signal_single = false;
|
signal_single = false;
|
||||||
|
|
|
@ -100,6 +100,9 @@ Thread::Thread()
|
||||||
sigemptyset(&signal_mask);
|
sigemptyset(&signal_mask);
|
||||||
memset(&signal_stack, 0, sizeof(signal_stack));
|
memset(&signal_stack, 0, sizeof(signal_stack));
|
||||||
signal_stack.ss_flags = SS_DISABLE;
|
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.
|
// execute_clock initialized in member constructor.
|
||||||
// system_clock initialized in member constructor.
|
// system_clock initialized in member constructor.
|
||||||
Time::InitializeThreadClocks(this);
|
Time::InitializeThreadClocks(this);
|
||||||
|
|
|
@ -337,6 +337,47 @@ thread_exit_handler:
|
||||||
pushq $0 # err_code
|
pushq $0 # err_code
|
||||||
pushq $132 # int_no
|
pushq $132 # int_no
|
||||||
jmp interrupt_handler_prepare
|
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:
|
interrupt_handler_prepare:
|
||||||
movq $1, asm_is_cpu_interrupted
|
movq $1, asm_is_cpu_interrupted
|
||||||
|
|
|
@ -20,11 +20,16 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <msr.h>
|
#include <msr.h>
|
||||||
|
#include <setjmp.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <sortix/kernel/copy.h>
|
||||||
#include <sortix/kernel/cpu.h>
|
#include <sortix/kernel/cpu.h>
|
||||||
|
#include <sortix/kernel/descriptor.h>
|
||||||
#include <sortix/kernel/interrupt.h>
|
#include <sortix/kernel/interrupt.h>
|
||||||
|
#include <sortix/kernel/ioctx.h>
|
||||||
#include <sortix/kernel/kernel.h>
|
#include <sortix/kernel/kernel.h>
|
||||||
#include <sortix/kernel/process.h>
|
#include <sortix/kernel/process.h>
|
||||||
#include <sortix/kernel/scheduler.h>
|
#include <sortix/kernel/scheduler.h>
|
||||||
|
@ -91,6 +96,13 @@ extern "C" void interrupt_handler_null();
|
||||||
extern "C" void syscall_handler();
|
extern "C" void syscall_handler();
|
||||||
extern "C" void yield_cpu_handler();
|
extern "C" void yield_cpu_handler();
|
||||||
extern "C" void thread_exit_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 Sortix {
|
||||||
namespace Interrupt {
|
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__DispatchHandler_handler;
|
||||||
static struct interrupt_handler Signal__ReturnHandler_handler;
|
static struct interrupt_handler Signal__ReturnHandler_handler;
|
||||||
static struct interrupt_handler Scheduler__ThreadExitCPU_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.
|
// Temporarily to see if this is the source of the assertion failure.
|
||||||
void DispatchHandlerWrap(struct interrupt_context* intctx, void* user)
|
void DispatchHandlerWrap(struct interrupt_context* intctx, void* user)
|
||||||
|
@ -243,6 +460,13 @@ void Init()
|
||||||
RegisterRawHandler(130, isr130, true, true);
|
RegisterRawHandler(130, isr130, true, true);
|
||||||
RegisterRawHandler(131, isr131, true, true);
|
RegisterRawHandler(131, isr131, true, true);
|
||||||
RegisterRawHandler(132, thread_exit_handler, true, false);
|
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;
|
Scheduler__InterruptYieldCPU_handler.handler = Scheduler::InterruptYieldCPU;
|
||||||
RegisterHandler(129, &Scheduler__InterruptYieldCPU_handler);
|
RegisterHandler(129, &Scheduler__InterruptYieldCPU_handler);
|
||||||
|
@ -252,6 +476,19 @@ void Init()
|
||||||
RegisterHandler(131, &Signal__ReturnHandler_handler);
|
RegisterHandler(131, &Signal__ReturnHandler_handler);
|
||||||
Scheduler__ThreadExitCPU_handler.handler = Scheduler::ThreadExitCPU;
|
Scheduler__ThreadExitCPU_handler.handler = Scheduler::ThreadExitCPU;
|
||||||
RegisterHandler(132, &Scheduler__ThreadExitCPU_handler);
|
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);
|
IDT::Set(interrupt_table, NUM_INTERRUPTS);
|
||||||
|
|
||||||
|
|
|
@ -807,13 +807,13 @@ headers:
|
||||||
|
|
||||||
# libk
|
# libk
|
||||||
%.libk.o: %.c
|
%.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++
|
%.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
|
%.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:
|
clean:
|
||||||
rm -f *.o */*.o */*/*.o *.a
|
rm -f *.o */*.o */*/*.o *.a
|
||||||
|
|
|
@ -39,20 +39,24 @@ __start:
|
||||||
pushq %rsi
|
pushq %rsi
|
||||||
pushq %rdi
|
pushq %rdi
|
||||||
pushq %rcx
|
pushq %rcx
|
||||||
call initialize_standard_library
|
mov $initialize_standard_library, %rax
|
||||||
|
int $0x90
|
||||||
|
|
||||||
# Run the global constructors.
|
# Run the global constructors.
|
||||||
call _init
|
mov $_init, %rax
|
||||||
|
int $0x90
|
||||||
|
|
||||||
# Run main
|
# Run main
|
||||||
popq %rdx # Note! envp is now %rdx (previously %rcx)
|
popq %rdx # Note! envp is now %rdx (previously %rcx)
|
||||||
popq %rdi
|
popq %rdi
|
||||||
popq %rsi
|
popq %rsi
|
||||||
addq $8, %rsp
|
addq $8, %rsp
|
||||||
call main
|
mov $main, %rax
|
||||||
|
int $0x90
|
||||||
|
|
||||||
# Terminate the process with main's exit code.
|
# Terminate the process with main's exit code.
|
||||||
movl %eax, %edi
|
movl %eax, %edi
|
||||||
call exit
|
mov $exit, %rax
|
||||||
|
int $0x90
|
||||||
.size _start, .-_start
|
.size _start, .-_start
|
||||||
.size __start, .-__start
|
.size __start, .-__start
|
||||||
|
|
|
@ -20,9 +20,9 @@
|
||||||
.section .init
|
.section .init
|
||||||
/* gcc will nicely put the contents of crtend.o's .init section here. */
|
/* gcc will nicely put the contents of crtend.o's .init section here. */
|
||||||
popq %rbp
|
popq %rbp
|
||||||
ret
|
int $0x91
|
||||||
|
|
||||||
.section .fini
|
.section .fini
|
||||||
/* gcc will nicely put the contents of crtend.o's .fini section here. */
|
/* gcc will nicely put the contents of crtend.o's .fini section here. */
|
||||||
popq %rbp
|
popq %rbp
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -52,21 +52,24 @@ __sfork:
|
||||||
popq %rax
|
popq %rax
|
||||||
movq %rax, (17 * 8)(%rsi) # rflags
|
movq %rax, (17 * 8)(%rsi) # rflags
|
||||||
movl $MSRID_FSBASE, %edi
|
movl $MSRID_FSBASE, %edi
|
||||||
call rdmsr
|
mov $rdmsr, %rax
|
||||||
|
int $0x90
|
||||||
movq 0(%rsp), %rsi
|
movq 0(%rsp), %rsi
|
||||||
movq %rax, (18 * 8)(%rsi) # fsbase
|
movq %rax, (18 * 8)(%rsi) # fsbase
|
||||||
movl $MSRID_GSBASE, %edi
|
movl $MSRID_GSBASE, %edi
|
||||||
call rdmsr
|
mov $rdmsr, %rax
|
||||||
|
int $0x90
|
||||||
movq 0(%rsp), %rsi
|
movq 0(%rsp), %rsi
|
||||||
movq %rax, (19 * 8)(%rsi) # gsbase
|
movq %rax, (19 * 8)(%rsi) # gsbase
|
||||||
movq 8(%rsp), %rdi
|
movq 8(%rsp), %rdi
|
||||||
|
|
||||||
call tfork
|
mov $tfork, %rax
|
||||||
|
int $0x90
|
||||||
|
|
||||||
.Lafter_fork:
|
.Lafter_fork:
|
||||||
# The value in %rax determines whether we are child or parent. There is no
|
# 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
|
# need to clean up the stack from the above pushes, leaveq sets %rsp to %rbp
|
||||||
# which does that for us.
|
# which does that for us.
|
||||||
leaveq
|
leaveq
|
||||||
retq
|
int $0x91
|
||||||
.size __sfork, . - __sfork
|
.size __sfork, . - __sfork
|
||||||
|
|
|
@ -50,21 +50,23 @@ sigsetjmp:
|
||||||
lea (9 * 8)(%rdi), %rdx # oldset
|
lea (9 * 8)(%rdi), %rdx # oldset
|
||||||
xor %esi, %esi # set
|
xor %esi, %esi # set
|
||||||
xor %edi, %edi # how (ignored because set is NULL)
|
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 %rsi
|
||||||
pop %rdi
|
pop %rdi
|
||||||
2: mov 0(%rsp), %rax
|
2: mov %rbx, (0 * 8)(%rdi)
|
||||||
mov %rbx, (0 * 8)(%rdi)
|
mov %rsp, %rdx
|
||||||
mov %rsp, (1 * 8)(%rdi)
|
add $8, %rdx
|
||||||
|
mov %rdx, (1 * 8)(%rdi)
|
||||||
mov %rbp, (2 * 8)(%rdi)
|
mov %rbp, (2 * 8)(%rdi)
|
||||||
mov %r12, (3 * 8)(%rdi)
|
mov %r12, (3 * 8)(%rdi)
|
||||||
mov %r13, (4 * 8)(%rdi)
|
mov %r13, (4 * 8)(%rdi)
|
||||||
mov %r14, (5 * 8)(%rdi)
|
mov %r14, (5 * 8)(%rdi)
|
||||||
mov %r15, (6 * 8)(%rdi)
|
mov %r15, (6 * 8)(%rdi)
|
||||||
mov %rax, (7 * 8)(%rdi)
|
|
||||||
mov %rsi, (8 * 8)(%rdi)
|
mov %rsi, (8 * 8)(%rdi)
|
||||||
|
int $0x94
|
||||||
xorl %eax, %eax
|
xorl %eax, %eax
|
||||||
ret
|
int $0x91
|
||||||
.size sigsetjmp, . - sigsetjmp
|
.size sigsetjmp, . - sigsetjmp
|
||||||
|
|
||||||
.global longjmp
|
.global longjmp
|
||||||
|
@ -87,18 +89,9 @@ siglongjmp:
|
||||||
leaq (9 * 8)(%rdi), %rsi # set
|
leaq (9 * 8)(%rdi), %rsi # set
|
||||||
movl $SIG_SETMASK, %edi # how
|
movl $SIG_SETMASK, %edi # how
|
||||||
xorl %edx, %edx # oldset
|
xorl %edx, %edx # oldset
|
||||||
call sigprocmask
|
mov $sigprocmask, %rax
|
||||||
|
int $0x90
|
||||||
popq %rsi
|
popq %rsi
|
||||||
popq %rdi
|
popq %rdi
|
||||||
2: movq (0 * 8)(%rdi), %rbx
|
2: int $0x95
|
||||||
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
|
|
||||||
.size siglongjmp, . - siglongjmp
|
.size siglongjmp, . - siglongjmp
|
||||||
|
|
|
@ -37,5 +37,5 @@ asm_syscall: /* syscall num in %rax. */
|
||||||
mov %ecx, errno@tpoff(%rsi)
|
mov %ecx, errno@tpoff(%rsi)
|
||||||
1:
|
1:
|
||||||
pop %rbp
|
pop %rbp
|
||||||
ret
|
int $0x91
|
||||||
.size asm_syscall, .-asm_syscall
|
.size asm_syscall, .-asm_syscall
|
||||||
|
|
|
@ -21,4 +21,4 @@ ENTRY(__ieee754_acos)
|
||||||
fxch %st(1)
|
fxch %st(1)
|
||||||
fpatan
|
fpatan
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -20,4 +20,4 @@ ENTRY(__ieee754_asin)
|
||||||
fsqrt /* sqrt (1 - x^2) */
|
fsqrt /* sqrt (1 - x^2) */
|
||||||
fpatan
|
fpatan
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(__ieee754_atan2)
|
||||||
fldl ARG_DOUBLE_TWO
|
fldl ARG_DOUBLE_TWO
|
||||||
fpatan
|
fpatan
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(__ieee754_atan2f)
|
||||||
flds ARG_FLOAT_TWO
|
flds ARG_FLOAT_TWO
|
||||||
fpatan
|
fpatan
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -88,7 +88,7 @@ ENTRY(__ieee754_exp)
|
||||||
fldcw CWSTORE_SAV
|
fldcw CWSTORE_SAV
|
||||||
1:
|
1:
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
x_Inf_or_NaN:
|
x_Inf_or_NaN:
|
||||||
/*
|
/*
|
||||||
* Return 0 if x is -Inf. Otherwise just return x, although the
|
* 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
|
jne x_not_minus_Inf
|
||||||
fldz
|
fldz
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
||||||
x_not_minus_Inf:
|
x_not_minus_Inf:
|
||||||
fldl ARG_DOUBLE_ONE
|
fldl ARG_DOUBLE_ONE
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -35,7 +35,7 @@ ENTRY(__ieee754_expf)
|
||||||
fscale /* e^x */
|
fscale /* e^x */
|
||||||
fstp %st(1)
|
fstp %st(1)
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
||||||
x_Inf_or_NaN:
|
x_Inf_or_NaN:
|
||||||
/*
|
/*
|
||||||
|
@ -47,9 +47,9 @@ x_Inf_or_NaN:
|
||||||
jne x_not_minus_Inf
|
jne x_not_minus_Inf
|
||||||
fldz
|
fldz
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
||||||
x_not_minus_Inf:
|
x_not_minus_Inf:
|
||||||
flds ARG_FLOAT_ONE
|
flds ARG_FLOAT_ONE
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -20,4 +20,4 @@ ENTRY(__ieee754_fmod)
|
||||||
jc 1b
|
jc 1b
|
||||||
fstp %st(1)
|
fstp %st(1)
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(__ieee754_log)
|
||||||
fldl ARG_DOUBLE_ONE
|
fldl ARG_DOUBLE_ONE
|
||||||
fyl2x
|
fyl2x
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(__ieee754_log10)
|
||||||
fldl ARG_DOUBLE_ONE
|
fldl ARG_DOUBLE_ONE
|
||||||
fyl2x
|
fyl2x
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(__ieee754_log10f)
|
||||||
flds ARG_FLOAT_ONE
|
flds ARG_FLOAT_ONE
|
||||||
fyl2x
|
fyl2x
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(__ieee754_log2)
|
||||||
fldl ARG_DOUBLE_ONE
|
fldl ARG_DOUBLE_ONE
|
||||||
fyl2x
|
fyl2x
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(__ieee754_log2f)
|
||||||
flds ARG_FLOAT_ONE
|
flds ARG_FLOAT_ONE
|
||||||
fyl2x
|
fyl2x
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(__ieee754_logf)
|
||||||
flds ARG_FLOAT_ONE
|
flds ARG_FLOAT_ONE
|
||||||
fyl2x
|
fyl2x
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -19,4 +19,4 @@ ENTRY(__ieee754_remainder)
|
||||||
jc 1b
|
jc 1b
|
||||||
fstp %st(1)
|
fstp %st(1)
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -19,4 +19,4 @@ ENTRY(__ieee754_remainderf)
|
||||||
jc 1b
|
jc 1b
|
||||||
fstp %st(1)
|
fstp %st(1)
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -16,4 +16,4 @@ ENTRY(__ieee754_scalb)
|
||||||
fscale
|
fscale
|
||||||
fstp %st(1) /* bug fix for fp stack overflow */
|
fstp %st(1) /* bug fix for fp stack overflow */
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(__ieee754_scalbf)
|
||||||
flds ARG_FLOAT_ONE
|
flds ARG_FLOAT_ONE
|
||||||
fscale
|
fscale
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -14,4 +14,4 @@ ENTRY(__ieee754_sqrt)
|
||||||
#else
|
#else
|
||||||
sqrtsd %xmm0,%xmm0
|
sqrtsd %xmm0,%xmm0
|
||||||
#endif
|
#endif
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -14,4 +14,4 @@ ENTRY(__ieee754_sqrtf)
|
||||||
#else
|
#else
|
||||||
sqrtss %xmm0,%xmm0
|
sqrtss %xmm0,%xmm0
|
||||||
#endif
|
#endif
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -16,8 +16,8 @@ ENTRY(lrint)
|
||||||
fistpl (%esp)
|
fistpl (%esp)
|
||||||
movl (%esp),%eax
|
movl (%esp),%eax
|
||||||
leave
|
leave
|
||||||
ret
|
int $0x91
|
||||||
#else
|
#else
|
||||||
cvtsd2siq %xmm0,%rax
|
cvtsd2siq %xmm0,%rax
|
||||||
ret
|
int $0x91
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(atan)
|
||||||
fld1
|
fld1
|
||||||
fpatan
|
fpatan
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(atanf)
|
||||||
fld1
|
fld1
|
||||||
fpatan
|
fpatan
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -42,4 +42,4 @@ ENTRY(ceil)
|
||||||
fstpl -8(%rsp)
|
fstpl -8(%rsp)
|
||||||
movsd -8(%rsp),%xmm0
|
movsd -8(%rsp),%xmm0
|
||||||
#endif
|
#endif
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -40,4 +40,4 @@ ENTRY(ceilf)
|
||||||
fstps -4(%rsp)
|
fstps -4(%rsp)
|
||||||
movss -4(%rsp),%xmm0
|
movss -4(%rsp),%xmm0
|
||||||
#endif
|
#endif
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -35,4 +35,4 @@ ENTRY(copysign)
|
||||||
pand %xmm3,%xmm0
|
pand %xmm3,%xmm0
|
||||||
por %xmm1,%xmm0
|
por %xmm1,%xmm0
|
||||||
#endif
|
#endif
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -34,4 +34,4 @@ ENTRY(copysignf)
|
||||||
pand %xmm3,%xmm0
|
pand %xmm3,%xmm0
|
||||||
por %xmm1,%xmm0
|
por %xmm1,%xmm0
|
||||||
#endif
|
#endif
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -17,7 +17,7 @@ ENTRY(cos)
|
||||||
andw $0x400,%ax
|
andw $0x400,%ax
|
||||||
jnz 1f
|
jnz 1f
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
1: fldpi
|
1: fldpi
|
||||||
fadd %st(0)
|
fadd %st(0)
|
||||||
fxch %st(1)
|
fxch %st(1)
|
||||||
|
@ -28,4 +28,4 @@ ENTRY(cos)
|
||||||
fstp %st(1)
|
fstp %st(1)
|
||||||
fcos
|
fcos
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(cosf)
|
||||||
flds ARG_FLOAT_ONE
|
flds ARG_FLOAT_ONE
|
||||||
fcos
|
fcos
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -23,4 +23,4 @@ ENTRY(finite)
|
||||||
cmpq %rdi,%rsi
|
cmpq %rdi,%rsi
|
||||||
setne %al
|
setne %al
|
||||||
#endif
|
#endif
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -22,4 +22,4 @@ ENTRY(finitef)
|
||||||
cmpl $0x7ff00000,%esi
|
cmpl $0x7ff00000,%esi
|
||||||
setne %al
|
setne %al
|
||||||
#endif
|
#endif
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -40,4 +40,4 @@ ENTRY(floor)
|
||||||
fstpl -8(%rsp)
|
fstpl -8(%rsp)
|
||||||
movsd -8(%rsp),%xmm0
|
movsd -8(%rsp),%xmm0
|
||||||
#endif
|
#endif
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -40,4 +40,4 @@ ENTRY(floorf)
|
||||||
fstps -4(%rsp)
|
fstps -4(%rsp)
|
||||||
movss -4(%rsp),%xmm0
|
movss -4(%rsp),%xmm0
|
||||||
#endif
|
#endif
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -29,4 +29,4 @@ ENTRY(ilogb)
|
||||||
fistpl -8(%rsp)
|
fistpl -8(%rsp)
|
||||||
movl -8(%rsp),%eax
|
movl -8(%rsp),%eax
|
||||||
#endif
|
#endif
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -29,4 +29,4 @@ ENTRY(ilogbf)
|
||||||
fistpl -4(%rsp)
|
fistpl -4(%rsp)
|
||||||
movl -4(%rsp),%eax
|
movl -4(%rsp),%eax
|
||||||
#endif
|
#endif
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -21,4 +21,4 @@ ENTRY(ilogbl)
|
||||||
fistpl -4(%rsp)
|
fistpl -4(%rsp)
|
||||||
movl -4(%rsp), %eax
|
movl -4(%rsp), %eax
|
||||||
#endif
|
#endif
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -65,7 +65,7 @@ use_fyl2x:
|
||||||
faddp
|
faddp
|
||||||
fyl2x
|
fyl2x
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
||||||
.align 4
|
.align 4
|
||||||
use_fyl2xp1:
|
use_fyl2xp1:
|
||||||
|
@ -73,4 +73,4 @@ use_fyl2xp1:
|
||||||
fldl ARG_DOUBLE_ONE
|
fldl ARG_DOUBLE_ONE
|
||||||
fyl2xp1
|
fyl2xp1
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -65,7 +65,7 @@ use_fyl2x:
|
||||||
faddp
|
faddp
|
||||||
fyl2x
|
fyl2x
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
||||||
.align 4
|
.align 4
|
||||||
use_fyl2xp1:
|
use_fyl2xp1:
|
||||||
|
@ -73,4 +73,4 @@ use_fyl2xp1:
|
||||||
flds ARG_FLOAT_ONE
|
flds ARG_FLOAT_ONE
|
||||||
fyl2xp1
|
fyl2xp1
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(logb)
|
||||||
fxtract
|
fxtract
|
||||||
fstp %st
|
fstp %st
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(logbf)
|
||||||
fxtract
|
fxtract
|
||||||
fstp %st
|
fstp %st
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -13,4 +13,4 @@ ENTRY(logbl)
|
||||||
fldt ARG_LONG_DOUBLE_ONE
|
fldt ARG_LONG_DOUBLE_ONE
|
||||||
fxtract
|
fxtract
|
||||||
fstp %st
|
fstp %st
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -103,4 +103,4 @@ ENTRY(modf)
|
||||||
L1:
|
L1:
|
||||||
#endif
|
#endif
|
||||||
leave
|
leave
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -14,4 +14,4 @@ ENTRY(rint)
|
||||||
fldl ARG_DOUBLE_ONE
|
fldl ARG_DOUBLE_ONE
|
||||||
frndint
|
frndint
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -14,4 +14,4 @@ ENTRY(rintf)
|
||||||
flds ARG_FLOAT_ONE
|
flds ARG_FLOAT_ONE
|
||||||
frndint
|
frndint
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -27,4 +27,4 @@ ENTRY(_scalbn)
|
||||||
fscale
|
fscale
|
||||||
fstp %st(1) /* clean up stack */
|
fstp %st(1) /* clean up stack */
|
||||||
#endif
|
#endif
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -27,4 +27,4 @@ ENTRY(_scalbnf)
|
||||||
fscale
|
fscale
|
||||||
fstp %st(1) /* clean up stack */
|
fstp %st(1) /* clean up stack */
|
||||||
#endif
|
#endif
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -24,4 +24,4 @@ ENTRY(_scalbnl)
|
||||||
fscale
|
fscale
|
||||||
fstp %st(1) /* clean up stack */
|
fstp %st(1) /* clean up stack */
|
||||||
#endif
|
#endif
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(significand)
|
||||||
fxtract
|
fxtract
|
||||||
fstp %st(1)
|
fstp %st(1)
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(significandf)
|
||||||
fxtract
|
fxtract
|
||||||
fstp %st(1)
|
fstp %st(1)
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -17,7 +17,7 @@ ENTRY(sin)
|
||||||
andw $0x400,%ax
|
andw $0x400,%ax
|
||||||
jnz 1f
|
jnz 1f
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
1: fldpi
|
1: fldpi
|
||||||
fadd %st(0)
|
fadd %st(0)
|
||||||
fxch %st(1)
|
fxch %st(1)
|
||||||
|
@ -28,4 +28,4 @@ ENTRY(sin)
|
||||||
fstp %st(1)
|
fstp %st(1)
|
||||||
fsin
|
fsin
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -15,4 +15,4 @@ ENTRY(sinf)
|
||||||
flds ARG_FLOAT_ONE
|
flds ARG_FLOAT_ONE
|
||||||
fsin
|
fsin
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -18,7 +18,7 @@ ENTRY(tan)
|
||||||
jnz 1f
|
jnz 1f
|
||||||
fstp %st(0)
|
fstp %st(0)
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
1: fldpi
|
1: fldpi
|
||||||
fadd %st(0)
|
fadd %st(0)
|
||||||
fxch %st(1)
|
fxch %st(1)
|
||||||
|
@ -30,4 +30,4 @@ ENTRY(tan)
|
||||||
fptan
|
fptan
|
||||||
fstp %st(0)
|
fstp %st(0)
|
||||||
XMM_DOUBLE_EPILOGUE
|
XMM_DOUBLE_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -16,4 +16,4 @@ ENTRY(tanf)
|
||||||
fptan
|
fptan
|
||||||
fstp %st(0)
|
fstp %st(0)
|
||||||
XMM_FLOAT_EPILOGUE
|
XMM_FLOAT_EPILOGUE
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -14,4 +14,4 @@ __signmask:
|
||||||
ENTRY(fabs)
|
ENTRY(fabs)
|
||||||
movsd __signmask(%rip),%xmm1
|
movsd __signmask(%rip),%xmm1
|
||||||
andpd %xmm1,%xmm0
|
andpd %xmm1,%xmm0
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -18,4 +18,4 @@ ENTRY(__flt_rounds)
|
||||||
movl $0x2d, %eax /* 0x2d = 00.10.11.01 */
|
movl $0x2d, %eax /* 0x2d = 00.10.11.01 */
|
||||||
sarl %cl, %eax /* 0,1,2,3 -> 1,3,2,0 */
|
sarl %cl, %eax /* 0,1,2,3 -> 1,3,2,0 */
|
||||||
andl $3, %eax
|
andl $3, %eax
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -23,4 +23,4 @@ ENTRY(fpgetmask)
|
||||||
movl -4(%rsp),%eax
|
movl -4(%rsp),%eax
|
||||||
notl %eax
|
notl %eax
|
||||||
andl $63,%eax
|
andl $63,%eax
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -22,4 +22,4 @@ ENTRY(fpgetprec)
|
||||||
movl -4(%rsp),%eax
|
movl -4(%rsp),%eax
|
||||||
rorl $8,%eax
|
rorl $8,%eax
|
||||||
andl $3,%eax
|
andl $3,%eax
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -21,4 +21,4 @@ ENTRY(fpgetround)
|
||||||
fnstcw -4(%rsp)
|
fnstcw -4(%rsp)
|
||||||
movl -4(%rsp), %eax
|
movl -4(%rsp), %eax
|
||||||
andl $0x00000c00, %eax
|
andl $0x00000c00, %eax
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -24,4 +24,4 @@ ENTRY(fpgetsticky)
|
||||||
movl -4(%rsp),%eax
|
movl -4(%rsp),%eax
|
||||||
orl -8(%rsp),%eax
|
orl -8(%rsp),%eax
|
||||||
andl $63,%eax
|
andl $63,%eax
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -41,4 +41,4 @@ ENTRY(fpsetmask)
|
||||||
|
|
||||||
notl %eax
|
notl %eax
|
||||||
andl $0x0000003f, %eax
|
andl $0x0000003f, %eax
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -36,4 +36,4 @@ ENTRY(fpsetprec)
|
||||||
movl %edx,-4(%rsp)
|
movl %edx,-4(%rsp)
|
||||||
|
|
||||||
fldcw -4(%rsp)
|
fldcw -4(%rsp)
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -39,4 +39,4 @@ ENTRY(fpsetround)
|
||||||
movl %edx,-4(%rsp)
|
movl %edx,-4(%rsp)
|
||||||
ldmxcsr -4(%rsp)
|
ldmxcsr -4(%rsp)
|
||||||
|
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -42,4 +42,4 @@ ENTRY(fpsetsticky)
|
||||||
|
|
||||||
ldmxcsr -32(%rsp)
|
ldmxcsr -32(%rsp)
|
||||||
fldenv -28(%rsp)
|
fldenv -28(%rsp)
|
||||||
ret
|
int $0x91
|
||||||
|
|
|
@ -4,7 +4,7 @@ include ../build-aux/compiler.mak
|
||||||
include ../build-aux/version.mak
|
include ../build-aux/version.mak
|
||||||
include ../build-aux/dirs.mak
|
include ../build-aux/dirs.mak
|
||||||
|
|
||||||
OPTLEVEL?=-g -O2
|
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
|
||||||
CXXFLAGS?=$(OPTLEVEL)
|
CXXFLAGS?=$(OPTLEVEL)
|
||||||
|
|
||||||
CXXFLAGS:=$(CXXFLAGS) -std=gnu++0x -Wall -Wextra -fno-exceptions -fno-rtti -msse -msse2
|
CXXFLAGS:=$(CXXFLAGS) -std=gnu++0x -Wall -Wextra -fno-exceptions -fno-rtti -msse -msse2
|
||||||
|
|
4
utils/.gitignore
vendored
4
utils/.gitignore
vendored
|
@ -1,6 +1,10 @@
|
||||||
*.o
|
*.o
|
||||||
basename
|
basename
|
||||||
cat
|
cat
|
||||||
|
cfg
|
||||||
|
cfi-test
|
||||||
|
cfi-test-2
|
||||||
|
cfi-test-3
|
||||||
chkblayout
|
chkblayout
|
||||||
chmod
|
chmod
|
||||||
chroot
|
chroot
|
||||||
|
|
|
@ -13,6 +13,10 @@ CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\"
|
||||||
BINARIES_EXCEPT_INSTALL:=\
|
BINARIES_EXCEPT_INSTALL:=\
|
||||||
basename \
|
basename \
|
||||||
cat \
|
cat \
|
||||||
|
cfg \
|
||||||
|
cfi-test \
|
||||||
|
cfi-test-2 \
|
||||||
|
cfi-test-3 \
|
||||||
chkblayout \
|
chkblayout \
|
||||||
chmod \
|
chmod \
|
||||||
chroot \
|
chroot \
|
||||||
|
@ -79,6 +83,9 @@ install: all
|
||||||
mkdir -p $(DESTDIR)$(BINDIR)
|
mkdir -p $(DESTDIR)$(BINDIR)
|
||||||
install $(BINARIES_EXCEPT_INSTALL) $(DESTDIR)$(BINDIR)
|
install $(BINARIES_EXCEPT_INSTALL) $(DESTDIR)$(BINDIR)
|
||||||
install xinstall $(DESTDIR)$(BINDIR)/install
|
install xinstall $(DESTDIR)$(BINDIR)/install
|
||||||
|
mkdir -p $(DESTDIR)$(SBINDIR)
|
||||||
|
install cfion $(DESTDIR)$(SBINDIR)/cfion
|
||||||
|
install cfioff $(DESTDIR)$(SBINDIR)/cfioff
|
||||||
|
|
||||||
%: %.c
|
%: %.c
|
||||||
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $< -o $@
|
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $< -o $@
|
||||||
|
|
76
utils/cfg.c
Normal file
76
utils/cfg.c
Normal 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
19
utils/cfi-test-2.c
Normal 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
36
utils/cfi-test-3.c
Normal 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
18
utils/cfi-test.c
Normal 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
9
utils/cfioff
Executable 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
14
utils/cfion
Executable 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
|
Loading…
Add table
Add a link
Reference in a new issue