]> git.ipfire.org Git - people/ms/pakfire.git/blobdiff - tests/libpakfire/jail.c
jail: Implement sending data into standard input
[people/ms/pakfire.git] / tests / libpakfire / jail.c
index 6cd15fe1261222de6ce1a064d4d578e4a56ec3d6..d49795a94680604107d074265390b3f2ca5d5095 100644 (file)
@@ -18,6 +18,8 @@
 #                                                                             #
 #############################################################################*/
 
+#include <sys/mount.h>
+
 #include <pakfire/cgroup.h>
 #include <pakfire/jail.h>
 
@@ -27,32 +29,86 @@ static const char* cmd_hello_world[] = {
        "/command", "echo", "Hello World!", NULL,
 };
 
+static const char* cmd_exhaust_memory[] = {
+       "/command", "exhaust-memory", NULL,
+};
+
+static const char* cmd_fork_bomb[] = {
+       "/command", "fork-bomb", NULL,
+};
+
+static const char* cmd_stat_ownership[] = {
+       "/command", "stat-ownership", NULL,
+};
+
 static int test_create(const struct test* t) {
        struct pakfire_jail* jail = NULL;
 
        // Create a new jail
-       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0));
+       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
 
        // Destroy it
        ASSERT_NULL(pakfire_jail_unref(jail));
 
-       // Create an interactive jail
-       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, PAKFIRE_JAIL_INTERACTIVE));
-
-       // Destroy it again
-       ASSERT_NULL(pakfire_jail_unref(jail));
-
        return EXIT_SUCCESS;
 
 FAIL:
        return EXIT_FAILURE;
 }
 
+static int test_exit_code(const struct test* t) {
+       struct pakfire_jail* jail = NULL;
+       int r = EXIT_FAILURE;
+
+       const char* argv[] = {
+               "/command", "exit-with-code", "123", NULL,
+       };
+
+       // Create a new jail
+       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
+
+       // Check if we receive the correct exit code
+       ASSERT(pakfire_jail_exec(jail, argv, NULL, NULL, NULL, 0) == 123);
+
+       // Success
+       r = EXIT_SUCCESS;
+
+FAIL:
+       if (jail)
+               pakfire_jail_unref(jail);
+
+       return r;
+}
+
+static int test_segv(const struct test* t) {
+       struct pakfire_jail* jail = NULL;
+       int r = EXIT_FAILURE;
+
+       const char* argv[] = {
+               "/command", "segv", NULL,
+       };
+
+       // Create a new jail
+       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
+
+       // Check if we receive the correct exit code
+       ASSERT(pakfire_jail_exec(jail, argv, NULL, NULL, NULL, 0) == 139);
+
+       // Success
+       r = EXIT_SUCCESS;
+
+FAIL:
+       if (jail)
+               pakfire_jail_unref(jail);
+
+       return r;
+}
+
 static int test_env(const struct test* t) {
        struct pakfire_jail* jail = NULL;
 
        // Create a new jail
-       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0));
+       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
 
        // Check if the default variables are set
        ASSERT(pakfire_jail_get_env(jail, "LANG"));
@@ -80,10 +136,10 @@ static int test_exec(const struct test* t) {
        struct pakfire_jail* jail = NULL;
 
        // Create a new jail
-       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0));
+       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
 
        // Try to execute something
-       ASSERT_SUCCESS(pakfire_jail_exec(jail, cmd_hello_world, NULL));
+       ASSERT_SUCCESS(pakfire_jail_exec(jail, cmd_hello_world, NULL, NULL, NULL, 0));
 
        // Destroy it
        ASSERT_NULL(pakfire_jail_unref(jail));
@@ -100,16 +156,16 @@ static int test_launch_into_cgroup(const struct test* t) {
        int r = EXIT_FAILURE;
 
        // Create a new cgroup
-       ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup, t->pakfire, "pakfire-test", 0));
+       ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup, t->ctx, "pakfire-test", 0));
 
        // Create a new jail
-       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0));
+       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
 
        // Connect jail to the cgroup
        ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail, cgroup));
 
        // Run command
-       ASSERT(pakfire_jail_exec(jail, cmd_hello_world, NULL) == 0);
+       ASSERT(pakfire_jail_exec(jail, cmd_hello_world, NULL, NULL, NULL, 0) == 0);
 
        r = EXIT_SUCCESS;
 
@@ -134,7 +190,7 @@ static int test_nice(const struct test* t) {
        };
 
        // Create a new jail
-       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0));
+       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
 
        // Set invalid nice levels
        ASSERT_ERRNO(pakfire_jail_nice(jail,  100), EINVAL);
@@ -144,7 +200,8 @@ static int test_nice(const struct test* t) {
        ASSERT_SUCCESS(pakfire_jail_nice(jail, 5));
 
        // Check if the nice level has been set
-       ASSERT_SUCCESS(pakfire_jail_exec(jail, argv, &output));
+       ASSERT_SUCCESS(pakfire_jail_exec(jail, argv,
+               NULL, pakfire_jail_capture_stdout, &output, 0));
        ASSERT_STRING_EQUALS(output, "5\n");
 
        // Success
@@ -159,12 +216,262 @@ FAIL:
        return r;
 }
 
+static int test_memory_limit(const struct test* t) {
+       struct pakfire_cgroup* cgroup = NULL;
+       struct pakfire_jail* jail = NULL;
+       int r = EXIT_FAILURE;
+
+
+       // Create cgroup
+       ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup, t->ctx, "pakfire-test", 0));
+
+       // Create jail
+       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
+
+       // Connect jail to the cgroup
+       ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail, cgroup));
+
+       // Set a memory limit of 100 MiB
+       ASSERT_SUCCESS(pakfire_cgroup_set_memory_limit(cgroup, 100 * 1024 * 1024));
+
+       // Try to exhaust all memory
+       ASSERT_FAILURE(pakfire_jail_exec(jail, cmd_exhaust_memory, NULL, NULL, NULL, 0));
+
+       // A fork bomb should also exhaust all memory
+       ASSERT_FAILURE(pakfire_jail_exec(jail, cmd_fork_bomb, NULL, NULL, NULL, 0));
+
+       // Success
+       r = EXIT_SUCCESS;
+
+FAIL:
+       if (jail)
+               pakfire_jail_unref(jail);
+       if (cgroup) {
+               pakfire_cgroup_destroy(cgroup);
+               pakfire_cgroup_unref(cgroup);
+       }
+
+       return r;
+}
+
+static int test_pid_limit(const struct test* t) {
+       struct pakfire_cgroup* cgroup = NULL;
+       struct pakfire_jail* jail = NULL;
+       int r = EXIT_FAILURE;
+
+       // Create cgroup
+       ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup, t->ctx, "pakfire-test", 0));
+
+       // Create jail
+       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
+
+       // Connect jail to the cgroup
+       ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail, cgroup));
+
+       // Set a PID limit of 100 processes
+       ASSERT_SUCCESS(pakfire_cgroup_set_pid_limit(cgroup, 100));
+
+       // Try to fork as many processes as possible
+       ASSERT_FAILURE(pakfire_jail_exec(jail, cmd_fork_bomb, NULL, NULL, NULL, 0));
+
+       // Success
+       r = EXIT_SUCCESS;
+
+FAIL:
+       if (jail)
+               pakfire_jail_unref(jail);
+       if (cgroup) {
+               pakfire_cgroup_destroy(cgroup);
+               pakfire_cgroup_unref(cgroup);
+       }
+
+       return r;
+}
+
+static int test_file_ownership(const struct test* t) {
+       int r = EXIT_FAILURE;
+       char* output = NULL;
+
+       // Execute a simple command
+       ASSERT_SUCCESS(pakfire_jail_run(t->pakfire, cmd_stat_ownership, 0, &output));
+
+       // Check if the file has been mapped to root/root
+       ASSERT_STRING_EQUALS(output, "uid=0 gid=0\n");
+
+       // Success
+       r = EXIT_SUCCESS;
+
+FAIL:
+       if (output)
+               free(output);
+
+       return r;
+}
+
+static int test_bind(const struct test* t) {
+       struct pakfire_jail* jail = NULL;
+       char* output = NULL;
+       int r = EXIT_FAILURE;
+
+       const char* source = "/";
+       const char* target = "/oldroot";
+
+       const char* argv[] = {
+               "/command", "check-mountpoint", target, NULL,
+       };
+
+       // Create a new jail
+       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
+
+       // Bind-mount nonsense
+       ASSERT_ERRNO(pakfire_jail_bind(jail, NULL, target, 0), EINVAL);
+       ASSERT_ERRNO(pakfire_jail_bind(jail, source, NULL, 0), EINVAL);
+
+       // Bind-mount something
+       ASSERT_SUCCESS(pakfire_jail_bind(jail, source, target, MS_RDONLY));
+
+       // Check if the mount actually works
+       ASSERT_SUCCESS(pakfire_jail_exec(jail, argv, NULL, NULL, NULL, 0));
+
+       // Success
+       r = EXIT_SUCCESS;
+
+FAIL:
+       if (jail)
+               pakfire_jail_unref(jail);
+
+       return r;
+}
+
+static int callback_stdin(struct pakfire* pakfire, void* data, int fd) {
+       int* lines = (int*)data;
+       int r;
+
+       while (*lines > 0) {
+               r = dprintf(fd, "LINE %d\n", *lines);
+               if (r < 0) {
+                       switch (errno) {
+                               case EAGAIN:
+                                       return 0;
+
+                               default:
+                                       LOG_ERROR("Could not write line (%u) to stdin: %m\n", *lines);
+                                       return -errno;
+                       }
+               }
+
+               // Decrement the lines counter
+               (*lines)--;
+       }
+
+       return EOF;
+}
+
+static int test_communicate(const struct test* t) {
+       struct pakfire_jail* jail = NULL;
+       int r = EXIT_FAILURE;
+
+       // How many lines to send?
+       int lines = 65535;
+
+       const char* argv[] = {
+               "/command", "pipe", NULL,
+       };
+
+       // Create a new jail
+       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
+
+       // Check if the mount actually works
+       ASSERT_SUCCESS(pakfire_jail_exec(jail, argv, callback_stdin, NULL, &lines, 0));
+
+       // Success
+       r = EXIT_SUCCESS;
+
+FAIL:
+       if (jail)
+               pakfire_jail_unref(jail);
+
+       return r;
+}
+
+static int test_send_one_signal(const struct test* t,
+               struct pakfire_jail* jail, const char* signal) {
+       const char* argv[] = {
+               "/command", "send-signal", signal, NULL,
+       };
+
+       // Perform the command
+       return pakfire_jail_exec(jail, argv, NULL, NULL, NULL, 0);
+}
+
+static int test_send_signal(const struct test* t) {
+       struct pakfire_jail* jail = NULL;
+       int r = EXIT_FAILURE;
+
+       // Create a new jail
+       ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
+
+       // Sending SIGTERM to ourselves
+       ASSERT(test_send_one_signal(t, jail, "15") == 0);
+
+       // Sending SIGKILL to ourselves
+       ASSERT(test_send_one_signal(t, jail, "9") == 0);
+
+       // Sending SIGSTOP to ourselves (this should be ignored by the jail)
+       ASSERT(test_send_one_signal(t, jail, "23") == 0);
+
+       // Success
+       r = EXIT_SUCCESS;
+
+FAIL:
+       if (jail)
+               pakfire_jail_unref(jail);
+
+       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));
+
+       // 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, 0) == 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_env);
-       testsuite_add_test(test_exec);
-       testsuite_add_test(test_launch_into_cgroup);
-       testsuite_add_test(test_nice);
+       testsuite_add_test(test_create, TEST_WANTS_PAKFIRE);
+       testsuite_add_test(test_exit_code, TEST_WANTS_PAKFIRE);
+       testsuite_add_test(test_segv, TEST_WANTS_PAKFIRE);
+       testsuite_add_test(test_env, TEST_WANTS_PAKFIRE);
+       testsuite_add_test(test_exec, TEST_WANTS_PAKFIRE);
+       testsuite_add_test(test_launch_into_cgroup, TEST_WANTS_PAKFIRE);
+       testsuite_add_test(test_nice, TEST_WANTS_PAKFIRE);
+       testsuite_add_test(test_memory_limit, TEST_WANTS_PAKFIRE);
+       testsuite_add_test(test_pid_limit, TEST_WANTS_PAKFIRE);
+       testsuite_add_test(test_file_ownership, TEST_WANTS_PAKFIRE);
+       testsuite_add_test(test_bind, TEST_WANTS_PAKFIRE);
+       testsuite_add_test(test_communicate, TEST_WANTS_PAKFIRE);
+       testsuite_add_test(test_send_signal, TEST_WANTS_PAKFIRE);
+       testsuite_add_test(test_timeout, TEST_WANTS_PAKFIRE);
 
        return testsuite_run(argc, argv);
 }