Reorganize the codebase

This commit is contained in:
Jianfeng Jiang
2023-04-09 23:12:42 -04:00
committed by Tate, Hongliang Tian
parent 888853a6de
commit 271a16d492
416 changed files with 67 additions and 53 deletions

View File

@ -0,0 +1,4 @@
.PHONY: all
all:
ln -sf /busybox/busybox sh

1
regression/apps/bin/sh Symbolic link
View File

@ -0,0 +1 @@
/busybox/busybox

View File

@ -0,0 +1,7 @@
### How to compile this busybox
We don't include the source code of busybox here since the source code is really large. The busybox can be compiled with following commands.
After download the source code of busybox 1.35.0 and unzip, then cd to the directory of busybox
1. `make defconfig`. We set all config as default.
2. Set static link option in .config: `CONFIG_STATIC=y`. We need a static-linked busybox binary since we does not support dynamic linking now.
3. Set standalone shell option in .config: `CONFIG_FEATURE_SH_STANDALONE=y`. The standalone ash will try to call busybox applets instead of search binaries in host system. e.g., when running ls, standalone ash will invoke `busybox ls`.

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6db28e1ed8bdac06ac595b2126cefc10ecf16156bf4c1005950f561aedc0531a
size 2701792

View File

@ -0,0 +1,12 @@
.PHONY: build clean run
build: hello.c execve.c
@gcc -static hello.c -o hello
@gcc -static execve.c -o execve
clean:
@rm hello
@rm execve
run: build
@./execve

3
regression/apps/execve/execve Executable file
View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c7cc90df87ade7ff2cb494e13678aceaf93542883558fda947bd2fc01e2d73a6
size 871952

View File

@ -0,0 +1,15 @@
#include <stdio.h>
#include <unistd.h>
int main() {
char* argv[] = { "argv1", "argv2", NULL };
char* envp[] = { "home=/", "version=1.1", NULL };
// The hello will be put at /execve/hello in InitRamfs
printf("Execve a new file /execve/hello:\n");
// flush the stdout content to ensure the content print to console
fflush(stdout);
execve("/execve/hello", argv, envp);
printf("Should not print\n");
fflush(stdout);
return 0;
}

3
regression/apps/execve/hello Executable file
View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d0152798ba393fb04603ead02083e74de560d9cf51584f5cdca762d0706ebd2d
size 871960

View File

@ -0,0 +1,16 @@
#include <stdio.h>
int main(int argc, char *argv[], char *envp[]) {
printf("Hello world from hello.c(execved in execve.c)!\n");
printf("argc = %d\n", argc);
for(int i = 0; i < argc; i++) {
printf("%s\n", argv[i]);
}
for(int i = 0 ;; i++) {
if (envp[i] == NULL) {
break;
}
printf("%s\n", envp[i]);
}
return 0;
}

View File

@ -0,0 +1,10 @@
.PHONY: build clean run
build: fork.s
@gcc -static -nostdlib fork.s -o fork
clean:
@rm fork
run: build
@./fork

3
regression/apps/fork/fork Executable file
View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9bc1642390b9dc38ecc058240e529401c38aa8bb9a86bad3615e4bdad505fa8c
size 9592

View File

@ -0,0 +1,69 @@
.global _start
.section .text
_start:
call print_hello_world
mov $57, %rax # syscall number of fork
syscall
cmp $0, %rax
je _child # child process
jmp _parent # parent process
_parent:
call wait_child
call get_pid
call print_parent_message
call exit
_child:
call get_pid
call print_child_message
call exit
wait_child:
mov %rax, %rdi # child process id
_loop:
mov $61, %rax # syscall number of wait4
mov $0, %rsi # exit status address
mov $1, %rdx # WNOHANG
syscall
cmp %rdi, %rax # The return value is the pid of child
jne _loop
ret
exit:
mov $60, %rax # syscall number of exit
mov $0, %rdi # exit code
syscall
get_pid:
mov $39, %rax
syscall
ret
print_hello_world:
mov $message, %rsi # address of message
mov $message_end, %rdx
sub %rsi, %rdx # calculate message len
jmp _print_message
print_parent_message:
mov $message_parent, %rsi # address of message
mov $message_parent_end, %rdx
sub %rsi, %rdx # calculate message len
jmp _print_message
print_child_message:
mov $message_child, %rsi # address of message
mov $message_child_end, %rdx
sub %rsi, %rdx # calculate message len
jmp _print_message
# never directly call _print_message
_print_message:
mov $1, %rax # syscall number of write
mov $1, %rdi # stdout
syscall
ret
.section .rodata
message:
.ascii "Hello, world in fork\n"
message_end:
message_parent:
.ascii "Hello world from parent\n"
message_parent_end:
message_child:
.ascii "Hello world from child\n"
message_child_end:

View File

@ -0,0 +1,10 @@
.PHONY: build clean run
build: fork.c
@gcc -static fork.c -o fork
clean:
@rm fork
run: build
@./fork

3
regression/apps/fork_c/fork Executable file
View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ca7a77a72da160f11670a04a16b623944295b9059399da45f57668f121b00d11
size 877152

View File

@ -0,0 +1,14 @@
#include <stdio.h>
#include <unistd.h>
int main() {
printf("before fork\n");
fflush(stdout);
if(fork() == 0) {
printf("after fork: Hello from child\n");
} else {
printf("after fork: Hello from parent\n");
}
fflush(stdout);
return 0;
}

View File

@ -0,0 +1,10 @@
.PHONY: build clean run
build: hello.c
@gcc -static -mno-sse hello.c -o hello
clean:
@rm hello
run: build
@./hello

3
regression/apps/hello_c/hello Executable file
View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dda5a7d6081cc2252056375d0550731ef2fd24789aa5f17da189a36bf78c588d
size 871896

View File

@ -0,0 +1,6 @@
#include <stdio.h>
int main() {
printf("hello world from hello_c!\n");
return 0;
}

View File

@ -0,0 +1,10 @@
.PHONY: build clean run
build: hello.c
@gcc hello.c -o hello
clean:
@rm hello
run: build
@./hello

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:64b67cf4ad247d668833888447ec7f12d37f5f816abd549d5c2c08fecfa4dd09
size 16696

View File

@ -0,0 +1,6 @@
#include <stdio.h>
int main() {
printf("hello world from hello_pie!\n");
return 0;
}

View File

@ -0,0 +1,10 @@
.PHONY: build clean run
build: hello_world.s
@gcc -static -nostdlib hello_world.s -o hello_world
clean:
@rm hello_world
run: build
@./hello_world

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f56fb5cf05f234578b13c8d73f2d29568f6f513602f1ea0b71a0dbf0cf8f60f8
size 9104

View File

@ -0,0 +1,26 @@
.global _start
.section .text
_start:
call print_message
call print_message
call print_message
mov $60, %rax # syscall number of exit
mov $0, %rdi # exit code
syscall
get_pid:
mov $39, %rax
syscall
ret
print_message:
mov $1, %rax # syscall number of write
mov $1, %rdi # stdout
mov $message, %rsi # address of message
mov $message_end, %rdx
sub %rsi, %rdx # calculate message len
syscall
ret
.section .rodata
message:
.ascii "Hello, world\n"
message_end:

View File

@ -0,0 +1,10 @@
.PHONY: build clean run
build: pthread_test.c
@gcc -static pthread_test.c -lpthread -o pthread_test
clean:
@rm pthread_test
run: build
@./pthread_test

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d40bfc16dbf95aba3afa1934fa4efc0147f0a0a12533362a8d95e105d4d2af21
size 1693840

View File

@ -0,0 +1,280 @@
// This test file is from occlum pthread test.
#include <sys/types.h>
#include <pthread.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#ifndef SYS_gettid
#error "SYS_gettid unavailable on this system"
#endif
#define gettid() ((pid_t)syscall(SYS_gettid))
// ============================================================================
// Helper functions
// ============================================================================
#define THROW_ERROR(fmt, ...) do { \
printf("\t\tERROR:" fmt " in func %s at line %d of file %s with errno %d: %s\n", \
##__VA_ARGS__, __func__, __LINE__, __FILE__, errno, strerror(errno)); \
return -1; \
} while (0)
// ============================================================================
// Helper macros
// ============================================================================
#define NTHREADS (3)
#define STACK_SIZE (8 * 1024)
// ============================================================================
// The test case of concurrent counter
// ============================================================================
#define LOCAL_COUNT (1000UL)
#define EXPECTED_GLOBAL_COUNT (LOCAL_COUNT * NTHREADS)
struct thread_arg {
int ti;
long local_count;
volatile unsigned long *global_count;
pthread_mutex_t *mutex;
};
static void *thread_func(void *_arg) {
struct thread_arg *arg = _arg;
for (long i = 0; i < arg->local_count; i++) {
pthread_mutex_lock(arg->mutex);
(*arg->global_count)++;
pthread_mutex_unlock(arg->mutex);
}
return NULL;
}
static int test_mutex_with_concurrent_counter(void) {
/*
* Multiple threads are to increase a global counter concurrently
*/
volatile unsigned long global_count = 0;
pthread_t threads[NTHREADS];
struct thread_arg thread_args[NTHREADS];
/*
* Protect the counter with a mutex
*/
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
/*
* Start the threads
*/
for (int ti = 0; ti < NTHREADS; ti++) {
struct thread_arg *thread_arg = &thread_args[ti];
thread_arg->ti = ti;
thread_arg->local_count = LOCAL_COUNT;
thread_arg->global_count = &global_count;
thread_arg->mutex = &mutex;
if (pthread_create(&threads[ti], NULL, thread_func, thread_arg) < 0) {
printf("ERROR: pthread_create failed (ti = %d)\n", ti);
return -1;
}
}
/*
* Wait for the threads to finish
*/
for (int ti = 0; ti < NTHREADS; ti++) {
if (pthread_join(threads[ti], NULL) < 0) {
printf("ERROR: pthread_join failed (ti = %d)\n", ti);
return -1;
}
}
/*
* Check the correctness of the concurrent counter
*/
if (global_count != EXPECTED_GLOBAL_COUNT) {
printf("ERROR: incorrect global_count (actual = %ld, expected = %ld)\n",
global_count, EXPECTED_GLOBAL_COUNT);
return -1;
}
pthread_mutex_destroy(&mutex);
return 0;
}
// ============================================================================
// The test case of robust mutex
// ============================================================================
struct thread_robust_arg {
int ti;
volatile int *global_count;
pthread_mutex_t *mutex;
};
int ret_err = -1;
static void *thread_worker(void *_arg) {
struct thread_robust_arg *arg = _arg;
int err = pthread_mutex_lock(arg->mutex);
if (err == EOWNERDEAD) {
// The mutex is locked by the thread here, but the state is marked as
// inconsistent, the thread should call 'pthread_mutex_consistent' to
// make the mutex consistent again.
if (pthread_mutex_consistent(arg->mutex) != 0) {
printf("ERROR: failed to recover the mutex\n");
return &ret_err;
}
} else if (err != 0) {
printf("ERROR: failed to lock the mutex with error: %d\n", err);
return &ret_err;
}
// Mutex is locked
(*arg->global_count)++;
// Wait for other threads to acquire the lock
sleep(1);
// Exit without unlocking the mutex, this will makes the mutex in an
// inconsistent state.
return NULL;
}
static int test_robust_mutex_with_concurrent_counter(void) {
volatile int global_count = 0;
pthread_t threads[NTHREADS];
struct thread_robust_arg thread_args[NTHREADS];
// Init robust mutex
pthread_mutex_t mutex;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
pthread_mutex_init(&mutex, &attr);
// Start the threads
for (int ti = 0; ti < NTHREADS; ti++) {
struct thread_robust_arg *thread_arg = &thread_args[ti];
thread_arg->ti = ti;
thread_arg->global_count = &global_count;
thread_arg->mutex = &mutex;
if (pthread_create(&threads[ti], NULL, thread_worker, thread_arg) < 0) {
THROW_ERROR("pthread_create failed (ti = %d)", ti);
}
}
// Wait for the threads to finish
for (int ti = 0; ti < NTHREADS; ti++) {
int *ret_val;
if (pthread_join(threads[ti], (void **)&ret_val) < 0) {
THROW_ERROR("pthread_join failed (ti = %d)", ti);
}
// printf("Thread %d joined\n", ti);
// fflush(stdout);
if (ret_val && *ret_val != 0) {
THROW_ERROR("run thread failed (ti = %d) with return val: %d", ti, *ret_val);
}
}
// printf("Thread all exited.\n");
// fflush(stdout);
// Check the result
if (global_count != NTHREADS) {
THROW_ERROR("incorrect global_count (actual = %d, expected = %d)", global_count,
NTHREADS);
}
pthread_mutex_destroy(&mutex);
return 0;
}
// ============================================================================
// The test case of waiting condition variable
// ============================================================================
#define WAIT_ROUND (10)
struct thread_cond_arg {
int ti;
volatile unsigned int *val;
volatile int *exit_thread_count;
pthread_cond_t *cond_val;
pthread_mutex_t *mutex;
};
static void *thread_cond_wait(void *_arg) {
struct thread_cond_arg *arg = _arg;
printf("Thread #%d: start to wait on condition variable.\n", arg->ti);
fflush(stdout);
for (unsigned int i = 0; i < WAIT_ROUND; ++i) {
int tid = gettid();
printf("WAIT ROUND: %d, tid = %d\n", i, tid);
fflush(stdout);
pthread_mutex_lock(arg->mutex);
printf("pthread mutex lock: tid = %d\n", tid);
fflush(stdout);
while (*(arg->val) == 0) {
pthread_cond_wait(arg->cond_val, arg->mutex);
printf("pthread cond wait: tid = %d\n", tid);
fflush(stdout);
}
pthread_mutex_unlock(arg->mutex);
}
(*arg->exit_thread_count)++;
printf("Thread #%d: exited.\n", arg->ti);
fflush(stdout);
return NULL;
}
static int test_mutex_with_cond_wait(void) {
volatile unsigned int val = 0;
volatile int exit_thread_count = 0;
pthread_t threads[NTHREADS];
struct thread_cond_arg thread_args[NTHREADS];
pthread_cond_t cond_val = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
/*
* Start the threads waiting on the condition variable
*/
for (int ti = 0; ti < NTHREADS; ti++) {
struct thread_cond_arg *thread_arg = &thread_args[ti];
thread_arg->ti = ti;
thread_arg->val = &val;
thread_arg->exit_thread_count = &exit_thread_count;
thread_arg->cond_val = &cond_val;
thread_arg->mutex = &mutex;
if (pthread_create(&threads[ti], NULL, thread_cond_wait, thread_arg) < 0) {
printf("ERROR: pthread_create failed (ti = %d)\n", ti);
return -1;
}
}
/*
* Unblock all threads currently waiting on the condition variable
*/
while (exit_thread_count < NTHREADS) {
pthread_mutex_lock(&mutex);
val = 1;
pthread_cond_broadcast(&cond_val);
pthread_mutex_unlock(&mutex);
pthread_mutex_lock(&mutex);
val = 0;
pthread_mutex_unlock(&mutex);
}
/*
* Wait for the threads to finish
*/
for (int ti = 0; ti < NTHREADS; ti++) {
if (pthread_join(threads[ti], NULL) < 0) {
printf("ERROR: pthread_join failed (ti = %d)\n", ti);
return -1;
}
}
return 0;
}
int main() {
test_mutex_with_concurrent_counter();
test_robust_mutex_with_concurrent_counter();
// test_mutex_with_cond_wait();
return 0;
}

View File

@ -0,0 +1,16 @@
#!/bin/sh
set -e
SCRIPT_DIR=/scripts
cd ${SCRIPT_DIR}/..
echo "Running tests......"
tests="hello_world/hello_world fork/fork execve/execve fork_c/fork signal_c/signal_test pthread/pthread_test hello_pie/hello"
for testcase in ${tests}
do
echo "Running test ${testcase}......"
${testcase}
done
echo "All tests passed"

View File

@ -0,0 +1,37 @@
#!/bin/sh
set -e
set -x
SCRIPT_DIR=/scripts
cd ${SCRIPT_DIR}
touch hello.txt
mv hello.txt hello_world.txt
rm hello_world.txt
awk '{print $2}' test_cmd.sh
cp test_cmd.sh test_cmd_backup.sh
cat test_cmd_backup.sh
rm test_cmd_backup.sh
ln -s test_cmd.sh tesk_cmd_soft_link
readlink -f tesk_cmd_soft_link
tail -n 1 tesk_cmd_soft_link
rm tesk_cmd_soft_link
ln test_cmd.sh tesk_cmd_hard_link
tail -n 1 tesk_cmd_hard_link
unlink tesk_cmd_hard_link
sed 3q test_cmd.sh
find . -name "*test_cmd*"
mkdir foo
rmdir foo
echo "Hello world from jinux" > hello.txt
rm hello.txt
cd ..

View File

@ -0,0 +1,7 @@
.PHONY: build clean run
build: signal_test.c
@gcc -static signal_test.c -o signal_test
clean:
@rm signal_test
run: build
@./signal_test

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:25894b343f222eea5074a83e16a7140868a992a66e7bc8e363fabf2c23e19451
size 882520

View File

@ -0,0 +1,284 @@
// This test file is from occlum, to test whether we implement signal correctly.
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
#include <ucontext.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <spawn.h>
#include <assert.h>
#include <fcntl.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>
#include <time.h>
// ============================================================================
// Helper functions
// ============================================================================
#define THROW_ERROR(fmt, ...) do { \
printf("\t\tERROR:" fmt " in func %s at line %d of file %s with errno %d: %s\n", \
##__VA_ARGS__, __func__, __LINE__, __FILE__, errno, strerror(errno)); \
return -1; \
} while (0)
// ============================================================================
// Test sigprocmask
// ============================================================================
#define sigcmpset(a, b) memcmp((a), (b), 8)
int test_sigprocmask() {
int ret;
sigset_t new, old;
sigset_t expected_old;
// Check sigmask == []
if ((ret = sigprocmask(0, NULL, &old)) < 0) {
THROW_ERROR("sigprocmask failed unexpectedly");
}
sigemptyset(&expected_old);
if (sigcmpset(&old, &expected_old) != 0) {
THROW_ERROR("unexpected old sigset");
}
// SIG_BLOCK: [] --> [SIGSEGV]
sigemptyset(&new);
sigaddset(&new, SIGSEGV);
if ((ret = sigprocmask(SIG_BLOCK, &new, &old)) < 0) {
THROW_ERROR("sigprocmask failed unexpectedly");
}
sigemptyset(&expected_old);
if (sigcmpset(&old, &expected_old) != 0) {
THROW_ERROR("unexpected old sigset");
}
// SIG_SETMASK: [SIGSEGV] --> [SIGIO]
sigemptyset(&new);
sigaddset(&new, SIGIO);
if ((ret = sigprocmask(SIG_SETMASK, &new, &old)) < 0) {
THROW_ERROR("sigprocmask failed unexpectedly");
}
sigemptyset(&expected_old);
sigaddset(&expected_old, SIGSEGV);
if (sigcmpset(&old, &expected_old) != 0) {
THROW_ERROR("unexpected old sigset");
}
// SIG_UNBLOCK: [SIGIO] -> []
if ((ret = sigprocmask(SIG_UNBLOCK, &new, &old)) < 0) {
THROW_ERROR("sigprocmask failed unexpectedly");
}
sigemptyset(&expected_old);
sigaddset(&expected_old, SIGIO);
if (sigcmpset(&old, &expected_old) != 0) {
THROW_ERROR("unexpected old sigset");
}
// Check sigmask == []
if ((ret = sigprocmask(0, NULL, &old)) < 0) {
THROW_ERROR("sigprocmask failed unexpectedly");
}
sigemptyset(&expected_old);
if (sigcmpset(&old, &expected_old) != 0) {
THROW_ERROR("unexpected old sigset");
}
return 0;
}
// ============================================================================
// Test raise syscall and user-registered signal handlers
// ============================================================================
#define MAX_RECURSION_LEVEL 3
static void handle_sigio(int num, siginfo_t *info, void *context) {
static volatile int recursion_level = 0;
printf("Hello from SIGIO signal handler (recursion_level = %d)!\n", recursion_level);
fflush(stdout);
recursion_level++;
if (recursion_level <= MAX_RECURSION_LEVEL) {
raise(SIGIO);
}
recursion_level--;
}
int test_raise() {
struct sigaction new_action, old_action;
memset(&new_action, 0, sizeof(struct sigaction));
memset(&old_action, 0, sizeof(struct sigaction));
new_action.sa_sigaction = handle_sigio;
new_action.sa_flags = SA_SIGINFO | SA_NODEFER;
if (sigaction(SIGIO, &new_action, &old_action) < 0) {
THROW_ERROR("registering new signal handler failed");
}
if (old_action.sa_handler != SIG_DFL) {
THROW_ERROR("unexpected old sig handler");
}
raise(SIGIO);
if (sigaction(SIGIO, &old_action, NULL) < 0) {
THROW_ERROR("restoring old signal handler failed");
}
return 0;
}
// ============================================================================
// Test catching and handling hardware exception
// ============================================================================
static void handle_sigfpe(int num, siginfo_t *info, void *_context) {
printf("SIGFPE Caught\n");
fflush(stdout);
assert(num == SIGFPE);
assert(info->si_signo == SIGFPE);
ucontext_t *ucontext = _context;
mcontext_t *mcontext = &ucontext->uc_mcontext;
// The faulty instruction should be `idiv %esi` (f7 fe)
mcontext->gregs[REG_RIP] += 2;
return;
}
// Note: this function is fragile in the sense that compiler may not always
// emit the instruction pattern that triggers divide-by-zero as we expect.
// TODO: rewrite this in assembly
int div_maybe_zero(int x, int y) {
return x / y;
}
#define fxsave(addr) __asm __volatile("fxsave %0" : "=m" (*(addr)))
int test_handle_sigfpe() {
// Set up a signal handler that handles divide-by-zero exception
struct sigaction new_action, old_action;
memset(&new_action, 0, sizeof(struct sigaction));
memset(&old_action, 0, sizeof(struct sigaction));
new_action.sa_sigaction = handle_sigfpe;
new_action.sa_flags = SA_SIGINFO;
if (sigaction(SIGFPE, &new_action, &old_action) < 0) {
THROW_ERROR("registering new signal handler failed");
}
if (old_action.sa_handler != SIG_DFL) {
THROW_ERROR("unexpected old sig handler");
}
char x[512] __attribute__((aligned(16))) = {};
char y[512] __attribute__((aligned(16))) = {};
// Trigger divide-by-zero exception
int a = 1;
int b = 0;
// Use volatile to prevent compiler optimization
volatile int c;
fxsave(x);
c = div_maybe_zero(a, b);
fxsave(y);
// jinux does not save and restore fpregs now, so we emit this check.
// if (memcmp(x, y, 512) != 0) {
// THROW_ERROR("floating point registers are modified");
// }
printf("Signal handler successfully jumped over the divide-by-zero instruction\n");
fflush(stdout);
if (sigaction(SIGFPE, &old_action, NULL) < 0) {
THROW_ERROR("restoring old signal handler failed");
}
return 0;
}
// TODO: rewrite this in assembly
int read_maybe_null(int *p) {
return *p;
}
static void handle_sigsegv(int num, siginfo_t *info, void *_context) {
printf("SIGSEGV Caught\n");
fflush(stdout);
assert(num == SIGSEGV);
assert(info->si_signo == SIGSEGV);
ucontext_t *ucontext = _context;
mcontext_t *mcontext = &ucontext->uc_mcontext;
// TODO: how long is the instruction?
// The faulty instruction should be `idiv %esi` (f7 fe)
mcontext->gregs[REG_RIP] += 2;
return;
}
int test_handle_sigsegv() {
// Set up a signal handler that handles divide-by-zero exception
struct sigaction new_action, old_action;
memset(&new_action, 0, sizeof(struct sigaction));
memset(&old_action, 0, sizeof(struct sigaction));
new_action.sa_sigaction = handle_sigsegv;
new_action.sa_flags = SA_SIGINFO;
if (sigaction(SIGSEGV, &new_action, &old_action) < 0) {
THROW_ERROR("registering new signal handler failed");
}
if (old_action.sa_handler != SIG_DFL) {
THROW_ERROR("unexpected old sig handler");
}
int *addr = NULL;
volatile int val = read_maybe_null(addr);
(void)val; // to suppress "unused variables" warning
printf("Signal handler successfully jumped over a null-dereferencing instruction\n");
fflush(stdout);
if (sigaction(SIGSEGV, &old_action, NULL) < 0) {
THROW_ERROR("restoring old signal handler failed");
}
return 0;
}
// ============================================================================
// Test SIGCHLD signal
// ============================================================================
int sigchld = 0;
void proc_exit() {
sigchld = 1;
}
int test_sigchld() {
signal(SIGCHLD, proc_exit);
printf("Run a parent process has pid = %d\n", getpid());
fflush(stdout);
int pid = fork();
if(pid == 0) {
// child process
printf("create a new proces successfully (pid = %d)\n", getpid());
fflush(stdout);
} else {
// parent process
wait(NULL);
printf("sigchld = %d\n", sigchld);
fflush(stdout);
}
return 0;
}
int main() {
test_sigprocmask();
test_raise();
test_handle_sigfpe();
test_handle_sigsegv();
test_sigchld();
return 0;
}