]> git.ipfire.org Git - pakfire.git/commitdiff
jail: Implement configuring timeouts for commands
authorMichael Tremer <michael.tremer@ipfire.org>
Fri, 6 Jan 2023 10:27:56 +0000 (10:27 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Fri, 6 Jan 2023 10:27:56 +0000 (10:27 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/include/pakfire/jail.h
src/libpakfire/jail.c
src/libpakfire/libpakfire.sym
tests/libpakfire/jail.c

index 46d1d4024822c9fc1f07e11fe7075d063cde24f9..03500bb8269ac933c510a8571f546459c2227ce7 100644 (file)
@@ -41,6 +41,9 @@ int pakfire_jail_bind(struct pakfire_jail* jail,
 // Resource Limits
 int pakfire_jail_nice(struct pakfire_jail* jail, int nice);
 
+// Timeout
+int pakfire_jail_set_timeout(struct pakfire_jail* jail, unsigned int timeout);
+
 // Environment
 const char* pakfire_jail_get_env(struct pakfire_jail* jail, const char* key);
 int pakfire_jail_set_env(struct pakfire_jail* jail, const char* key, const char* value);
index 5c7a844b995ca944813f74f4881f7af7d51784c4..9cb347335ef2c0a3f117ef674de970e382d388f2 100644 (file)
@@ -35,6 +35,7 @@
 #include <sys/personality.h>
 #include <sys/prctl.h>
 #include <sys/resource.h>
+#include <sys/timerfd.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 
@@ -95,6 +96,9 @@ struct pakfire_jail {
        // Resource Limits
        int nice;
 
+       // Timeout
+       struct itimerspec timeout;
+
        // CGroup
        struct pakfire_cgroup* cgroup;
 
@@ -166,6 +170,10 @@ static int clone3(struct clone_args* args, size_t size) {
        return syscall(__NR_clone3, args, size);
 }
 
+static int pidfd_send_signal(int pidfd, int sig, siginfo_t* info, unsigned int flags) {
+       return syscall(SYS_pidfd_send_signal, pidfd, sig, info, flags);
+}
+
 static int pakfire_jail_exec_has_flag(
                const struct pakfire_jail_exec* ctx, const enum pakfire_jail_exec_flags flag) {
        return ctx->flags & flag;
@@ -439,6 +447,51 @@ PAKFIRE_EXPORT int pakfire_jail_import_env(struct pakfire_jail* jail, const char
        return 0;
 }
 
+// Timeout
+
+PAKFIRE_EXPORT int pakfire_jail_set_timeout(
+               struct pakfire_jail* jail, unsigned int timeout) {
+       // Store value
+       jail->timeout.it_value.tv_sec = timeout;
+
+       if (timeout > 0)
+               DEBUG(jail->pakfire, "Timeout set to %d second(s)\n", timeout);
+       else
+               DEBUG(jail->pakfire, "Timeout disabled\n");
+
+       return 0;
+}
+
+static int pakfire_jail_create_timer(struct pakfire_jail* jail) {
+       int r;
+
+       // Nothing to do if no timeout has been set
+       if (!jail->timeout.it_value.tv_sec)
+               return -1;
+
+       // Create a new timer
+       const int fd = timerfd_create(CLOCK_MONOTONIC, 0);
+       if (fd < 0) {
+               ERROR(jail->pakfire, "Could not create timer: %m\n");
+               goto ERROR;
+       }
+
+       // Arm timer
+       r = timerfd_settime(fd, 0, &jail->timeout, NULL);
+       if (r) {
+               ERROR(jail->pakfire, "Could not arm timer: %m\n");
+               goto ERROR;
+       }
+
+       return fd;
+
+ERROR:
+       if (fd > 0)
+               close(fd);
+
+       return -1;
+}
+
 /*
        This function replaces any logging in the child process.
 
@@ -640,6 +693,7 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec
        int epollfd = -1;
        struct epoll_event ev;
        struct epoll_event events[EPOLL_MAX_EVENTS];
+       char garbage[8];
        int r = 0;
 
        // Fetch file descriptors from context
@@ -648,6 +702,9 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec
        const int stderr = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.stderr);
        const int pidfd  = ctx->pidfd;
 
+       // Timer
+       const int timerfd = pakfire_jail_create_timer(jail);
+
        // Logging
        const int log_INFO  = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_INFO);
        const int log_ERROR = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_ERROR);
@@ -655,7 +712,7 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec
 
        // Make a list of all file descriptors we are interested in
        int fds[] = {
-               stdin, stdout, stderr, pidfd, log_INFO, log_ERROR, log_DEBUG,
+               stdin, stdout, stderr, pidfd, timerfd, log_INFO, log_ERROR, log_DEBUG,
        };
 
        // Setup epoll
@@ -742,6 +799,33 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec
                                        ended = 1;
                                        continue;
 
+                               // Handle timer events
+                               } else if (fd == timerfd) {
+                                       DEBUG(jail->pakfire, "Timer event received\n");
+
+                                       // Disarm the timer
+                                       r = read(timerfd, garbage, sizeof(garbage));
+                                       if (r < 1) {
+                                               ERROR(jail->pakfire, "Could not disarm timer: %m\n");
+                                               r = 1;
+                                               goto ERROR;
+                                       }
+
+                                       // Terminate the process if it hasn't already ended
+                                       if (!ended) {
+                                               DEBUG(jail->pakfire, "Terminating process...\n");
+
+                                               // Send SIGTERM to the process
+                                               r = pidfd_send_signal(pidfd, SIGKILL, NULL, 0);
+                                               if (r) {
+                                                       ERROR(jail->pakfire, "Could not kill process: %m\n");
+                                                       goto ERROR;
+                                               }
+                                       }
+
+                                       // There is nothing else to do
+                                       continue;
+
                                // Handle logging messages
                                } else if (fd == log_INFO) {
                                        buffer = &ctx->buffers.log_INFO;
@@ -820,6 +904,8 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec
 ERROR:
        if (epollfd > 0)
                close(epollfd);
+       if (timerfd > 0)
+               close(timerfd);
 
        return r;
 }
index 71f1608a3b557c1c17a44108ed7e3cf19f07b4b2..b6d13d407be8e9aefdd2480afb93e19bb65897e6 100644 (file)
@@ -157,6 +157,7 @@ global:
        pakfire_jail_nice;
        pakfire_jail_ref;
        pakfire_jail_set_env;
+       pakfire_jail_set_timeout;
        pakfire_jail_unref;
 
        # log
index 4b115dd583902fa1c05d14897e4f5f5ab354c355..94f362d9015bfce401abd3577793c620eb9b5b08 100644 (file)
@@ -425,6 +425,33 @@ FAIL:
        return r;
 }
 
+static int test_timeout(const struct test* t) {
+       struct pakfire_jail* jail = NULL;
+       int r = EXIT_FAILURE;
+
+       const char* argv[] = {
+               "/command", "sleep", "5", NULL,
+       };
+
+       // Create a new jail
+       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0));
+
+       // Set a timeout of one second
+       ASSERT_SUCCESS(pakfire_jail_set_timeout(jail, 1));
+
+       // Check if we receive the correct exit code
+       ASSERT(pakfire_jail_exec(jail, argv, NULL, NULL, NULL) == 139);
+
+       // Success
+       r = EXIT_SUCCESS;
+
+FAIL:
+       if (jail)
+               pakfire_jail_unref(jail);
+
+       return r;
+}
+
 int main(int argc, const char* argv[]) {
        testsuite_add_test(test_create);
        testsuite_add_test(test_exit_code);
@@ -439,6 +466,7 @@ int main(int argc, const char* argv[]) {
        testsuite_add_test(test_bind);
        testsuite_add_test(test_communicate);
        testsuite_add_test(test_send_signal);
+       testsuite_add_test(test_timeout);
 
        return testsuite_run(argc, argv);
 }