diff --git a/src/main.c b/src/main.c index c6c905b4..54135f4b 100644 --- a/src/main.c +++ b/src/main.c @@ -320,6 +320,7 @@ typedef struct tf_run_args_t { int count; const char* args; const char* zip; + bool one_proc; bool help; } tf_run_args_t; @@ -341,6 +342,7 @@ static int _tf_run_task(const tf_run_args_t* args, int index) tf_task_set_http_port(task, args->http_port ? args->http_port + index : 0); tf_task_set_https_port(task, args->https_port ? args->https_port + index : 0); tf_task_set_args(task, args->args); + tf_task_set_one_proc(task, args->one_proc); const char* db_path = args->db_path; char db_path_buffer[256]; if (index) @@ -385,6 +387,7 @@ static int _tf_command_run(const char* file, int argc, char* argv[]) { "db-path", 'd', offsetof(tf_run_args_t, db_path), NULL, XOPT_TYPE_STRING, NULL, "Sqlite database path (default: db.sqlite)." }, { "count", 'n', offsetof(tf_run_args_t, count), NULL, XOPT_TYPE_INT, NULL, "Number of instances to run." }, { "args", 'a', offsetof(tf_run_args_t, args), NULL, XOPT_TYPE_STRING, NULL, "Arguments of the form key=value,foo=bar,verbose=true." }, + { "one-proc", 'o', offsetof(tf_run_args_t, one_proc), NULL, XOPT_TYPE_BOOL, NULL, "Run everything in one process (unsafely!)." }, { "zip", 'z', offsetof(tf_run_args_t, zip), NULL, XOPT_TYPE_STRING, NULL, "Zip archive from which to load files." }, { "help", 'h', offsetof(tf_run_args_t, help), NULL, XOPT_TYPE_BOOL, NULL, "Shows this help message." }, XOPT_NULLOPTION, @@ -485,7 +488,7 @@ static int _tf_command_sandbox(const char* file, int argc, char* argv[]) prctl(PR_SET_PDEATHSIG, SIGHUP); #endif tf_task_t* task = tf_task_create(); - tf_task_configure_from_stdin(task); + tf_task_configure_from_fd(task, STDIN_FILENO); shedPrivileges(); /* The caller will trigger tf_task_activate with a message. */ tf_task_run(task); diff --git a/src/packetstream.c b/src/packetstream.c index fbb417cb..0b1bca5a 100644 --- a/src/packetstream.c +++ b/src/packetstream.c @@ -1,5 +1,6 @@ #include "packetstream.h" +#include "log.h" #include "mem.h" #include "uv.h" @@ -116,7 +117,11 @@ static void _packetstream_on_read(uv_stream_t* handle, ssize_t count, const uv_b void tf_packetstream_start(tf_packetstream_t* stream) { stream->stream.data = stream; - uv_read_start((uv_stream_t*)&stream->stream, _packetstream_allocate, _packetstream_on_read); + int result = uv_read_start((uv_stream_t*)&stream->stream, _packetstream_allocate, _packetstream_on_read); + if (result) + { + tf_printf("uv_read_start: %s\n", uv_strerror(result)); + } } static void _packetstream_on_write(uv_write_t* request, int status) diff --git a/src/task.c b/src/task.c index d51a8c02..ffee9df9 100644 --- a/src/task.c +++ b/src/task.c @@ -94,6 +94,7 @@ typedef struct _tf_task_t bool _activated; bool _trusted; + bool _one_proc; bool _killed; int32_t _exitCode; char _scriptName[256]; @@ -1620,9 +1621,9 @@ tf_task_t* tf_task_create() return task; } -void tf_task_configure_from_stdin(tf_task_t* task) +void tf_task_configure_from_fd(tf_task_t* task, int fd) { - task->_parent = tf_taskstub_create_parent(task, STDIN_FILENO); + task->_parent = tf_taskstub_create_parent(task, fd); } static void _tf_task_trace_to_parent(tf_trace_t* trace, const char* buffer, size_t size, void* user_data) @@ -1754,6 +1755,16 @@ void tf_task_run(tf_task_t* task) } while (_tf_task_run_jobs(task)); } +bool tf_task_get_one_proc(tf_task_t* task) +{ + return task->_one_proc; +} + +void tf_task_set_one_proc(tf_task_t* task, bool one_proc) +{ + task->_one_proc = one_proc; +} + void tf_task_set_trusted(tf_task_t* task, bool trusted) { task->_trusted = trusted; diff --git a/src/task.h b/src/task.h index 021fe097..bd99afde 100644 --- a/src/task.h +++ b/src/task.h @@ -35,7 +35,7 @@ typedef enum _tf_task_message_t { } tf_task_message_t; tf_task_t* tf_task_create(); -void tf_task_configure_from_stdin(tf_task_t* task); +void tf_task_configure_from_fd(tf_task_t* task, int fd); void tf_task_set_ssb_port(tf_task_t* task, int port); void tf_task_set_http_port(tf_task_t* task, int port); void tf_task_set_https_port(tf_task_t* task, int port); @@ -43,6 +43,8 @@ void tf_task_set_db_path(tf_task_t* task, const char* path); void tf_task_set_zip_path(tf_task_t* task, const char* path); const char* tf_task_get_zip_path(tf_task_t* task); void tf_task_set_args(tf_task_t* task, const char* args); +bool tf_task_get_one_proc(tf_task_t* task); +void tf_task_set_one_proc(tf_task_t* task, bool one_proc); void tf_task_activate(tf_task_t* task); void tf_task_run(tf_task_t* task); int tf_task_execute(tf_task_t* task, const char* file); diff --git a/src/taskstub.js.c b/src/taskstub.js.c index a7379b18..56d61b6b 100644 --- a/src/taskstub.js.c +++ b/src/taskstub.js.c @@ -63,6 +63,16 @@ static JSValue _taskstub_loadFile(JSContext* context, JSValueConst this_val, int static void _taskstub_on_process_exit(uv_process_t* process, int64_t status, int terminationSignal); static void _taskstub_finalizer(JSRuntime *runtime, JSValue value); +static void _tf_taskstub_run_sandbox_thread(void* data) +{ + uv_file fd = (uv_file)(intptr_t)data; + tf_task_t* task = tf_task_create(); + tf_task_configure_from_fd(task, fd); + /* The caller will trigger tf_task_activate with a message. */ + tf_task_run(task); + tf_task_destroy(task); +} + static JSValue _taskstub_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) { tf_task_t* parent = tf_task_get(context); @@ -125,41 +135,73 @@ static JSValue _taskstub_create(JSContext* context, JSValueConst this_val, int a char arg1[] = "sandbox"; char* command_argv[] = { _executable, arg1, 0 }; - uv_pipe_t* pipe = tf_packetstream_get_pipe(stub->_stream); - memset(pipe, 0, sizeof(*pipe)); - if (uv_pipe_init(tf_task_get_loop(parent), pipe, 1) != 0) - { - tf_printf("uv_pipe_init failed\n"); - } - - uv_stdio_container_t io[3]; - io[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE; - io[0].data.stream = (uv_stream_t*)pipe; - io[1].flags = UV_INHERIT_FD; - io[1].data.fd = STDOUT_FILENO; - io[2].flags = UV_INHERIT_FD; - io[2].data.fd = STDERR_FILENO; - - uv_process_options_t options = {0}; - options.args = command_argv; - options.exit_cb = _taskstub_on_process_exit; - options.stdio = io; - options.stdio_count = sizeof(io) / sizeof(*io); - options.file = command_argv[0]; - JSValue result = JS_NULL; - stub->_process.data = stub; - int spawn_result = uv_spawn(tf_task_get_loop(parent), &stub->_process, &options); - if (spawn_result == 0) + if (tf_task_get_one_proc(parent)) { + uv_os_sock_t fds[2]; + int pipe_result = uv_socketpair(SOCK_STREAM, AF_UNIX, fds, 0, 0); + if (pipe_result) + { + tf_printf("uv_socketpair failed: %s\n", uv_strerror(pipe_result)); + } + + uv_pipe_t* pipe = tf_packetstream_get_pipe(stub->_stream); + memset(pipe, 0, sizeof(*pipe)); + pipe_result = uv_pipe_init(tf_task_get_loop(parent), pipe, 1); + if (pipe_result != 0) + { + tf_printf("uv_pipe_init failed: %s\n", uv_strerror(pipe_result)); + } + pipe_result = uv_pipe_open(pipe, fds[0]); + if (pipe_result != 0) + { + tf_printf("uv_pipe_open failed: %s\n", uv_strerror(pipe_result)); + } + + uv_thread_t* thread = tf_malloc(sizeof(uv_thread_t)); + uv_thread_create(thread, _tf_taskstub_run_sandbox_thread, (void*)(intptr_t)fds[1]); + tf_packetstream_set_on_receive(stub->_stream, tf_task_on_receive_packet, stub); tf_packetstream_start(stub->_stream); result = taskObject; } else { - tf_printf("uv_spawn failed: %s\n", uv_strerror(spawn_result)); - JS_FreeValue(context, taskObject); + uv_pipe_t* pipe = tf_packetstream_get_pipe(stub->_stream); + memset(pipe, 0, sizeof(*pipe)); + if (uv_pipe_init(tf_task_get_loop(parent), pipe, 1) != 0) + { + tf_printf("uv_pipe_init failed\n"); + } + + uv_stdio_container_t io[3]; + io[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE | UV_WRITABLE_PIPE; + io[0].data.stream = (uv_stream_t*)pipe; + io[1].flags = UV_INHERIT_FD; + io[1].data.fd = STDOUT_FILENO; + io[2].flags = UV_INHERIT_FD; + io[2].data.fd = STDERR_FILENO; + + uv_process_options_t options = {0}; + options.args = command_argv; + options.exit_cb = _taskstub_on_process_exit; + options.stdio = io; + options.stdio_count = sizeof(io) / sizeof(*io); + options.file = command_argv[0]; + + stub->_process.data = stub; + int spawn_result = uv_spawn(tf_task_get_loop(parent), &stub->_process, &options); + if (spawn_result == 0) + { + tf_packetstream_set_on_receive(stub->_stream, tf_task_on_receive_packet, stub); + tf_packetstream_start(stub->_stream); + result = taskObject; + } + else + { + tf_printf("uv_spawn failed: %s\n", uv_strerror(spawn_result)); + JS_FreeValue(context, taskObject); + } } return result; } @@ -230,9 +272,10 @@ tf_taskstub_t* tf_taskstub_create_parent(tf_task_t* task, uv_file file) tf_printf("uv_pipe_init failed\n"); } tf_packetstream_set_on_receive(parentStub->_stream, tf_task_on_receive_packet, parentStub); - if (uv_pipe_open(tf_packetstream_get_pipe(parentStub->_stream), file) != 0) + int result = uv_pipe_open(tf_packetstream_get_pipe(parentStub->_stream), file); + if (result != 0) { - tf_printf("uv_pipe_open failed\n"); + tf_printf("uv_pipe_open failed: %s\n", uv_strerror(result)); } tf_packetstream_start(parentStub->_stream);