From 243ed3a8b83ed1419a6c4093ad323b1a38827a68 Mon Sep 17 00:00:00 2001 From: Snaipe Date: Thu, 26 Mar 2015 22:18:43 +0100 Subject: [PATCH] Trying a safer approach for a fork() alternative --- include/criterion/types.h | 12 +++--- src/posix-compat.c | 91 +++++++++++++++------------------------ src/posix-compat.h | 13 +++++- src/process.c | 34 +++++++++------ src/process.h | 2 + src/runner.c | 4 ++ 6 files changed, 81 insertions(+), 75 deletions(-) diff --git a/include/criterion/types.h b/include/criterion/types.h index 3a4672f..6e7dae2 100644 --- a/include/criterion/types.h +++ b/include/criterion/types.h @@ -29,9 +29,9 @@ struct criterion_test_extra_data { int sentinel_; - const char *const identifier_; - const char *const file_; - const unsigned line_; + const char *identifier_; + const char *file_; + unsigned line_; void (*init)(void); void (*fini)(void); int signal; @@ -44,12 +44,14 @@ struct criterion_test { const char *name; const char *category; void (*test)(void); - struct criterion_test_extra_data *const data; + struct criterion_test_extra_data *data; }; struct criterion_suite { const char *name; - struct criterion_test_extra_data *const data; + struct criterion_test_extra_data *data; }; +typedef void (*f_worker_func)(struct criterion_test *, struct criterion_suite *); + #endif /* !CRITERION_TYPES_H_ */ diff --git a/src/posix-compat.c b/src/posix-compat.c index 7a5d809..01e4527 100644 --- a/src/posix-compat.c +++ b/src/posix-compat.c @@ -1,4 +1,5 @@ #include "posix-compat.h" +#include "process.h" #ifdef _WIN32 # define VC_EXTRALEAN @@ -7,7 +8,6 @@ # include # include # include -# include # include # define CREATE_SUSPENDED_(Filename, CmdLine, StartupInfo, Info) \ @@ -22,11 +22,7 @@ &(StartupInfo), \ &(Info)) -# ifdef _WIN64 -# define Register(Reg, Context) ((Context).R ## Reg) -# else -# define Register(Reg, Context) ((Context).E ## Reg) -# endif +# define CONTEXT_INIT { .ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT } #else # include # include @@ -52,40 +48,24 @@ struct pipe_handle { #endif }; +struct worker_context g_worker_context = {.test = NULL}; + #ifdef _WIN32 -# define CONTEXT_INIT { .ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT } -struct region_info { - char *ptr; - char *base; - size_t size; -}; - -static inline void get_memory_info(struct region_info *stack, struct region_info *heap) { - CONTEXT context = CONTEXT_INIT; - MEMORY_BASIC_INFORMATION mbi; - - GetThreadContext(GetCurrentThread(), &context); - stack->ptr = (char *) Register(sp, context); - - VirtualQuery(stack->ptr, &mbi, sizeof (mbi)); - stack->base = mbi.BaseAddress; - stack->size = mbi.RegionSize; - - void *ptr = malloc(1); - VirtualQuery(ptr, &mbi, sizeof (mbi)); - heap->ptr = NULL; - heap->base = mbi.BaseAddress; - heap->size = mbi.RegionSize; - free(ptr); -} - -static jmp_buf g_jmp; - -static void resume_child(void) { - longjmp(g_jmp, 1); -} +static struct criterion_test child_test; +static struct criterion_test_extra_data child_test_data; +static struct criterion_suite child_suite; +static struct criterion_test_extra_data child_suite_data; +static struct pipe_handle child_pipe; #endif +int resume_child(void) { + if (g_worker_context.test) { + run_worker(&g_worker_context); + return 1; + } + return 0; +} + s_proc_handle *fork_process() { #ifdef _WIN32 PROCESS_INFORMATION info; @@ -95,35 +75,32 @@ s_proc_handle *fork_process() { CONTEXT context = CONTEXT_INIT; - // Initialize longjmp buffer - if (setjmp(g_jmp)) - return NULL; // we are the child, return - // Create the suspended child process wchar_t filename[MAX_PATH]; GetModuleFileNameW(NULL, filename, MAX_PATH); if (!CREATE_SUSPENDED_(filename, GetCommandLineW(), si, info)) - return NULL; + return (void *) -1; - // Set child's instruction pointer to resume_child - GetThreadContext(info.hThread, &context); - Register(ip, context) = (intptr_t) resume_child; - SetThreadContext(info.hThread, &context); + // Copy context over + f_worker_func child_func = g_worker_context.func; - // Copy stack, heap - struct region_info stack; - struct region_info heap; - get_memory_info(&stack, &heap); + child_test = *g_worker_context.test; + child_test_data = *g_worker_context.test->data; + child_suite = *g_worker_context.suite; + child_suite_data = *g_worker_context.suite->data; + child_pipe = *g_worker_context.pipe; - VirtualAllocEx(info.hProcess, stack.base, stack.size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); - WriteProcessMemory(info.hProcess, stack.base, stack.base, stack.size, NULL); + g_worker_context = (struct worker_context) { &child_test, &child_suite, child_func, &child_pipe }; + child_test.data = &child_test_data; + child_suite.data = &child_suite_data; - VirtualAllocEx(info.hProcess, heap.base, heap.size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); - WriteProcessMemory(info.hProcess, heap.base, heap.base, heap.size, NULL); - - // Copy jmp_buf - WriteProcessMemory(info.hProcess, &g_jmp, &g_jmp, sizeof (jmp_buf), NULL); + WriteProcessMemory(info.hProcess, &child_test, &child_test, sizeof (child_test), NULL); + WriteProcessMemory(info.hProcess, &child_test_data, &child_test_data, sizeof (child_test_data), NULL); + WriteProcessMemory(info.hProcess, &child_suite, &child_suite, sizeof (child_suite), NULL); + WriteProcessMemory(info.hProcess, &child_suite_data, &child_suite_data, sizeof (child_suite_data), NULL); + WriteProcessMemory(info.hProcess, &child_pipe, &child_pipe, sizeof (child_pipe), NULL); + WriteProcessMemory(info.hProcess, &g_worker_context, &g_worker_context, sizeof (struct worker_context), NULL); ResumeThread(info.hThread); CloseHandle(info.hThread); diff --git a/src/posix-compat.h b/src/posix-compat.h index 8f53627..3c32e9c 100644 --- a/src/posix-compat.h +++ b/src/posix-compat.h @@ -20,7 +20,7 @@ # include # endif -#include +#include struct proc_handle; typedef struct proc_handle s_proc_handle; @@ -28,6 +28,17 @@ typedef struct proc_handle s_proc_handle; struct pipe_handle; typedef struct pipe_handle s_pipe_handle; +struct worker_context { + struct criterion_test *test; + struct criterion_suite *suite; + f_worker_func func; + struct pipe_handle *pipe; +}; + +extern struct worker_context g_worker_context; + +int resume_child(void); + s_pipe_handle *stdpipe(); FILE *pipe_in(s_pipe_handle *p); FILE *pipe_out(s_pipe_handle *p); diff --git a/src/process.c b/src/process.c index c0ebd76..83afc73 100644 --- a/src/process.c +++ b/src/process.c @@ -60,28 +60,38 @@ struct event *worker_read_event(struct process *proc) { return read_event(proc->in); } +void run_worker(struct worker_context *ctx) { + fclose(stdin); + g_event_pipe = pipe_out(ctx->pipe); + + ctx->func(ctx->test, ctx->suite); + fclose(g_event_pipe); + + fflush(NULL); // flush all opened streams + if (criterion_options.no_early_exit) + return; + _Exit(0); +} + struct process *spawn_test_worker(struct criterion_test *test, struct criterion_suite *suite, - void (*func)(struct criterion_test *, struct criterion_suite *)) { + f_worker_func func) { smart s_pipe_handle *pipe = stdpipe(); if (pipe == NULL) abort(); + g_worker_context = (struct worker_context) { + .test = test, + .suite = suite, + .func = func, + .pipe = pipe + }; s_proc_handle *proc = fork_process(); if (proc == (void *) -1) { return NULL; } else if (proc == NULL) { - fclose(stdin); - g_event_pipe = pipe_out(pipe); - - func(test, suite); - fclose(g_event_pipe); - - fflush(NULL); // flush all opened streams - if (criterion_options.no_early_exit) - return NULL; - else - _Exit(0); + run_worker(&g_worker_context); + return NULL; } return unique_ptr(struct process, { .proc = proc, .in = pipe_in(pipe) }, close_process); diff --git a/src/process.h b/src/process.h index 679c17f..5e0a6c3 100644 --- a/src/process.h +++ b/src/process.h @@ -25,6 +25,7 @@ # define PROCESS_H_ # include +# include "posix-compat.h" struct process; @@ -39,6 +40,7 @@ struct process_status { int status; }; +void run_worker(struct worker_context *ctx); void set_runner_process(void); void unset_runner_process(void); bool is_runner(void); diff --git a/src/runner.c b/src/runner.c index 8f07b22..8c84b1e 100644 --- a/src/runner.c +++ b/src/runner.c @@ -33,6 +33,7 @@ #include "event.h" #include "process.h" #include "timer.h" +#include "posix-compat.h" IMPL_SECTION_LIMITS(struct criterion_test, criterion_tests); IMPL_SECTION_LIMITS(struct criterion_suite, crit_suites); @@ -199,6 +200,9 @@ static void run_test(struct criterion_global_stats *stats, } static int criterion_run_all_tests_impl(void) { + if (resume_child()) // (windows only) resume from the fork + return -1; + smart struct criterion_test_set *set = criterion_init(); report(PRE_ALL, set);