# #
#############################################################################*/
+#include <sys/mount.h>
+
#include <pakfire/cgroup.h>
#include <pakfire/jail.h>
#include "../testsuite.h"
-static const char* cmd[2] = {
- "/usr/bin/does-not-exist",
+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, 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, 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"));
static int test_exec(const struct test* t) {
struct pakfire_jail* jail = NULL;
- const char* argv[] = {
- "/does-not-exist",
- 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(pakfire_jail_exec(jail, argv, NULL) == 127);
+ ASSERT_SUCCESS(pakfire_jail_exec(jail, cmd_hello_world, 0));
// Destroy it
ASSERT_NULL(pakfire_jail_unref(jail));
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, NULL) == 0);
+ ASSERT(pakfire_jail_exec(jail, cmd_hello_world, 0) == 0);
r = EXIT_SUCCESS;
return r;
}
+static int test_nice(const struct test* t) {
+ struct pakfire_jail* jail = NULL;
+ char* output = NULL;
+ int r = EXIT_FAILURE;
+
+ const char* argv[] = {
+ "/command", "print-nice", NULL,
+ };
+
+ // Create a new jail
+ ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
+
+ // Set invalid nice levels
+ ASSERT_ERRNO(pakfire_jail_nice(jail, 100), EINVAL);
+ ASSERT_ERRNO(pakfire_jail_nice(jail, -100), EINVAL);
+
+ // Set something sane
+ ASSERT_SUCCESS(pakfire_jail_nice(jail, 5));
+
+ // Capture the output of the next execution
+ pakfire_jail_set_stdout_callback(jail, pakfire_jail_capture_stdout, &output);
+
+ // Check if the nice level has been set
+ ASSERT_SUCCESS(pakfire_jail_exec(jail, argv, 0));
+ ASSERT_STRING_EQUALS(output, "5\n");
+
+ // Success
+ r = EXIT_SUCCESS;
+
+FAIL:
+ if (jail)
+ pakfire_jail_unref(jail);
+ if (output)
+ free(output);
+
+ 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, 0));
+
+ // A fork bomb should also exhaust all memory
+ ASSERT_FAILURE(pakfire_jail_exec(jail, cmd_fork_bomb, 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, 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, 0));
+
+ // Success
+ r = EXIT_SUCCESS;
+
+FAIL:
+ if (jail)
+ pakfire_jail_unref(jail);
+
+ return r;
+}
+
+static int callback_stdin(struct pakfire_ctx* ctx, struct pakfire_jail* 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));
+
+ pakfire_jail_set_stdin_callback(jail, callback_stdin, &lines);
+
+ // Check if the mount actually works
+ ASSERT_SUCCESS(pakfire_jail_exec(jail, argv, 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, 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, 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_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);
+#if 0
+ testsuite_add_test(test_file_ownership, TEST_WANTS_PAKFIRE);
+#endif
+ 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);
}