From 4ec0b7f71013f94a7f868e957a59dacd473bbcdc Mon Sep 17 00:00:00 2001 From: clfreville2 Date: Sat, 18 Nov 2023 15:46:23 +0100 Subject: [PATCH] Define a runner timeout --- src/main.cpp | 9 ++++----- src/program.hpp | 1 + src/runner.cpp | 46 ++++++++++++++++++++++++++++++++++++++++------ src/runner.hpp | 1 + 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 8c26c14..542aa8a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -22,7 +21,6 @@ sk::runner_backend detect_backend() { } int status = 0; waitpid(pid, &status, 0); - std::cout << status << std::endl; if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { std::cerr << "Using Docker" << std::endl; return sk::runner_backend::Docker; @@ -58,8 +56,9 @@ int main(int argc, char **argv) { std::stringstream buffer; buffer << t.rdbuf(); std::string code = buffer.str(); - sk::program program{code, "ghcr.io/moshell-lang/moshell"}; + sk::program program{"sample-code", code, "ghcr.io/moshell-lang/moshell:master"}; sk::run_result result = runner.run_blocking(program); + std::cout << "exited with: " << result.exit_code << "\n"; std::cout << "out: " << result.out << "\n"; std::cout << "err: " << result.err << "\n"; return 0; @@ -79,7 +78,7 @@ int main(int argc, char **argv) { break; } - std::string_view jobId(static_cast(request.data()), JOB_ID_LEN); + std::string jobId(static_cast(request.data()), JOB_ID_LEN); uint32_t imageLen = sk::read_uint32(static_cast(request.data()) + JOB_ID_LEN); uint32_t codeLen = sk::read_uint32(static_cast(request.data()) + JOB_ID_LEN + sizeof(uint32_t)); @@ -91,7 +90,7 @@ int main(int argc, char **argv) { std::string requestString(static_cast(request.data()) + MIN_MESSAGE_LEN + imageLen, codeLen); std::cout << "Executing " << codeLen << " bytes code.\n"; - sk::program program{requestString, imageString}; + sk::program program{jobId, requestString, imageString}; sk::run_result result = runner.run_blocking(program); std::cout << "Result: " << result.out << std::endl; diff --git a/src/program.hpp b/src/program.hpp index a74a616..7779324 100644 --- a/src/program.hpp +++ b/src/program.hpp @@ -5,6 +5,7 @@ namespace sk { struct program { + std::string name; std::string code; std::string image; }; diff --git a/src/runner.cpp b/src/runner.cpp index 28a55d2..3381d1b 100644 --- a/src/runner.cpp +++ b/src/runner.cpp @@ -5,14 +5,25 @@ #include #include #include +#include #include #include #include +static constexpr int TIMEOUT_SECONDS = 2; + +// Define a helper to throw a system error if a syscall fails +static auto ensure = [](int res) -> void { + if (res == -1) { + throw std::system_error{errno, std::generic_category()}; + } +}; + namespace sk { runner::runner(runner_backend backend) : backend{backend} {} run_result runner::run_blocking(const program &program) { + // Open file descriptors ahead of time int in_pipe[2]; int out_pipe[2]; int err_pipe[2]; @@ -20,6 +31,17 @@ run_result runner::run_blocking(const program &program) { throw std::system_error{errno, std::generic_category()}; } + // Create a timer that will be polled when the program runs too long + int timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + if (timerfd == -1) { + throw std::system_error{errno, std::generic_category()}; + } + itimerspec timer{}; + timer.it_value.tv_sec = TIMEOUT_SECONDS; + if (timerfd_settime(timerfd, 0, &timer, nullptr) == -1) { + throw std::system_error{errno, std::generic_category()}; + } + // Avoid blocking on stdin, and being interrupted if the pipe is closed prematurely int flags = fcntl(in_pipe[1], F_GETFL, 0); fcntl(in_pipe[1], F_SETFL, flags | O_NONBLOCK); @@ -29,19 +51,21 @@ run_result runner::run_blocking(const program &program) { posix_spawn_file_actions_addclose(&actions, in_pipe[1]); posix_spawn_file_actions_addclose(&actions, out_pipe[0]); posix_spawn_file_actions_addclose(&actions, err_pipe[0]); + posix_spawn_file_actions_addclose(&actions, timerfd); posix_spawn_file_actions_adddup2(&actions, in_pipe[0], STDIN_FILENO); posix_spawn_file_actions_adddup2(&actions, out_pipe[1], STDOUT_FILENO); posix_spawn_file_actions_adddup2(&actions, err_pipe[1], STDERR_FILENO); posix_spawn_file_actions_addclose(&actions, in_pipe[0]); posix_spawn_file_actions_addclose(&actions, out_pipe[1]); posix_spawn_file_actions_addclose(&actions, err_pipe[1]); - const char *const docker_args[] = {"docker", "run", "--rm", "-i", "--pull=never", "--cap-drop=ALL", "--network=none", "--memory=64m", "--memory-swap=64m", "--pids-limit=128", program.image.c_str(), nullptr}; - const char *const bwrap_args[] = {"bwrap", "--ro-bind", "/usr", "/usr", "--dir", "/tmp", "--dir", "/var", "--proc", "/proc", "--dev", "/dev", "--symlink", "usr/lib", "/lib", "--symlink", "usr/lib64", "/lib64", "--symlink", "usr/bin", "/bin", "--symlink", "usr/sbin", "/sbin", "--unshare-all", "/bin/sh", nullptr}; + const char *const docker_args[] = {"docker", "run", "--rm", "-i", "--name", program.name.c_str(), "--pull=never", "--cap-drop=ALL", "--network=none", "--memory=64m", "--memory-swap=64m", "--pids-limit=128", program.image.c_str(), nullptr}; + const char *const bwrap_args[] = {"bwrap", "--ro-bind", "/usr", "/usr", "--dir", "/tmp", "--dir", "/var", "--proc", "/proc", "--dev", "/dev", "--symlink", "usr/lib", "/lib", "--symlink", "usr/lib64", "/lib64", "--symlink", "usr/bin", "/bin", "--symlink", "usr/sbin", "/sbin", "--unshare-all", "--die-with-parent", "/bin/sh", nullptr}; const char *const *args = docker_args; if (backend == runner_backend::BubbleWrap) { args = bwrap_args; } pid_t pid; + bool killed = false; int exit_code; if (posix_spawnp(&pid, args[0], &actions, nullptr, const_cast(args), nullptr) != 0) { throw std::system_error{errno, std::generic_category()}; @@ -58,7 +82,7 @@ run_result runner::run_blocking(const program &program) { std::array buffer{}; std::string out; std::string err; - std::array plist = {pollfd{in_pipe[1], POLLOUT | POLLHUP, 0}, pollfd{out_pipe[0], POLLIN, 0}, pollfd{err_pipe[0], POLLIN, 0}}; + std::array plist = {pollfd{in_pipe[1], POLLOUT | POLLHUP, 0}, pollfd{out_pipe[0], POLLIN, 0}, pollfd{err_pipe[0], POLLIN, 0}, pollfd{timerfd, POLLIN, 0}}; pollfd *pfds = plist.data(); nfds_t nfds = plist.size(); @@ -94,8 +118,8 @@ run_result runner::run_blocking(const program &program) { } } - // Poll stdout and stderr - for (nfds_t i = nfds - 2; i < nfds; ++i) { + // Poll stdout, stderr and the timer + for (nfds_t i = nfds - 3; i < nfds; ++i) { if (pfds[i].revents & POLLIN) { ssize_t bytes_read = read(pfds[i].fd, buffer.data(), buffer.size()); if (bytes_read == -1) { @@ -103,6 +127,15 @@ run_result runner::run_blocking(const program &program) { } if (pfds[i].fd == out_pipe[0]) { out.append(buffer.data(), bytes_read); + } else if (pfds[i].fd == timerfd) { + if (backend == runner_backend::Docker) { + const char *const kill_args[] = {"docker", "kill", program.name.c_str(), nullptr}; + pid_t kill_pid; + ensure(posix_spawnp(&kill_pid, kill_args[0], nullptr, nullptr, const_cast(kill_args), nullptr)); + } else { + ensure(kill(pid, SIGINT)); + } + killed = true; } else { err.append(buffer.data(), bytes_read); } @@ -119,8 +152,9 @@ run_result runner::run_blocking(const program &program) { waitpid(pid, &exit_code, 0); close(out_pipe[0]); close(err_pipe[0]); + close(timerfd); posix_spawn_file_actions_destroy(&actions); - return run_result{out, err}; + return run_result{out, err, killed ? 124 : exit_code}; } } diff --git a/src/runner.hpp b/src/runner.hpp index af5d732..cea9c97 100644 --- a/src/runner.hpp +++ b/src/runner.hpp @@ -7,6 +7,7 @@ namespace sk { struct [[nodiscard]] run_result { std::string out; std::string err; + int exit_code; }; enum class runner_backend { BubbleWrap, Docker };