1 /*#############################################################################
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2022 Pakfire development team #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
19 #############################################################################*/
21 #include <sys/mount.h>
23 #include <pakfire/cgroup.h>
24 #include <pakfire/jail.h>
26 #include "../testsuite.h"
28 static const char* cmd_hello_world
[] = {
29 "/command", "echo", "Hello World!", NULL
,
32 static const char* cmd_exhaust_memory
[] = {
33 "/command", "exhaust-memory", NULL
,
36 static const char* cmd_fork_bomb
[] = {
37 "/command", "fork-bomb", NULL
,
40 static const char* cmd_stat_ownership
[] = {
41 "/command", "stat-ownership", NULL
,
44 static int test_create(const struct test
* t
) {
45 struct pakfire_jail
* jail
= NULL
;
48 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
51 ASSERT_NULL(pakfire_jail_unref(jail
));
59 static int test_exit_code(const struct test
* t
) {
60 struct pakfire_jail
* jail
= NULL
;
63 const char* argv
[] = {
64 "/command", "exit-with-code", "123", NULL
,
68 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
70 // Check if we receive the correct exit code
71 ASSERT(pakfire_jail_exec(jail
, argv
, 0) == 123);
78 pakfire_jail_unref(jail
);
83 static int test_segv(const struct test
* t
) {
84 struct pakfire_jail
* jail
= NULL
;
87 const char* argv
[] = {
88 "/command", "segv", NULL
,
92 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
94 // Check if we receive the correct exit code
95 ASSERT(pakfire_jail_exec(jail
, argv
, 0) == 139);
102 pakfire_jail_unref(jail
);
107 static int test_env(const struct test
* t
) {
108 struct pakfire_jail
* jail
= NULL
;
111 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
113 // Check if the default variables are set
114 ASSERT(pakfire_jail_get_env(jail
, "LANG"));
115 ASSERT(pakfire_jail_get_env(jail
, "TERM"));
117 // Fetch a non-existing environment variable
118 ASSERT_NULL(pakfire_jail_get_env(jail
, "VARIABLE"));
121 ASSERT_SUCCESS(pakfire_jail_set_env(jail
, "VARIABLE", "VALUE"));
123 // Read the value back
124 ASSERT_STRING_EQUALS(pakfire_jail_get_env(jail
, "VARIABLE"), "VALUE");
127 ASSERT_NULL(pakfire_jail_unref(jail
));
135 static int test_exec(const struct test
* t
) {
136 struct pakfire_jail
* jail
= NULL
;
139 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
141 // Try to execute something
142 ASSERT_SUCCESS(pakfire_jail_exec(jail
, cmd_hello_world
, 0));
145 ASSERT_NULL(pakfire_jail_unref(jail
));
153 static int test_launch_into_cgroup(const struct test
* t
) {
154 struct pakfire_cgroup
* cgroup
= NULL
;
155 struct pakfire_jail
* jail
= NULL
;
156 int r
= EXIT_FAILURE
;
158 // Create a new cgroup
159 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup
, t
->ctx
, "pakfire-test", 0));
162 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
164 // Connect jail to the cgroup
165 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail
, cgroup
));
168 ASSERT(pakfire_jail_exec(jail
, cmd_hello_world
, 0) == 0);
174 pakfire_cgroup_destroy(cgroup
);
175 pakfire_cgroup_unref(cgroup
);
178 pakfire_jail_unref(jail
);
183 static int test_nice(const struct test
* t
) {
184 struct pakfire_jail
* jail
= NULL
;
186 int r
= EXIT_FAILURE
;
188 const char* argv
[] = {
189 "/command", "print-nice", NULL
,
193 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
195 // Set invalid nice levels
196 ASSERT_ERRNO(pakfire_jail_nice(jail
, 100), EINVAL
);
197 ASSERT_ERRNO(pakfire_jail_nice(jail
, -100), EINVAL
);
199 // Set something sane
200 ASSERT_SUCCESS(pakfire_jail_nice(jail
, 5));
202 // Capture the output of the next execution
203 pakfire_jail_set_stdout_callback(jail
, pakfire_jail_capture_stdout
, &output
);
205 // Check if the nice level has been set
206 ASSERT_SUCCESS(pakfire_jail_exec(jail
, argv
, 0));
207 ASSERT_STRING_EQUALS(output
, "5\n");
214 pakfire_jail_unref(jail
);
221 static int test_memory_limit(const struct test
* t
) {
222 struct pakfire_cgroup
* cgroup
= NULL
;
223 struct pakfire_jail
* jail
= NULL
;
224 int r
= EXIT_FAILURE
;
228 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup
, t
->ctx
, "pakfire-test", 0));
231 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
233 // Connect jail to the cgroup
234 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail
, cgroup
));
236 // Set a memory limit of 100 MiB
237 ASSERT_SUCCESS(pakfire_cgroup_set_memory_limit(cgroup
, 100 * 1024 * 1024));
239 // Try to exhaust all memory
240 ASSERT_FAILURE(pakfire_jail_exec(jail
, cmd_exhaust_memory
, 0));
242 // A fork bomb should also exhaust all memory
243 ASSERT_FAILURE(pakfire_jail_exec(jail
, cmd_fork_bomb
, 0));
250 pakfire_jail_unref(jail
);
252 pakfire_cgroup_destroy(cgroup
);
253 pakfire_cgroup_unref(cgroup
);
259 static int test_pid_limit(const struct test
* t
) {
260 struct pakfire_cgroup
* cgroup
= NULL
;
261 struct pakfire_jail
* jail
= NULL
;
262 int r
= EXIT_FAILURE
;
265 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup
, t
->ctx
, "pakfire-test", 0));
268 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
270 // Connect jail to the cgroup
271 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail
, cgroup
));
273 // Set a PID limit of 100 processes
274 ASSERT_SUCCESS(pakfire_cgroup_set_pid_limit(cgroup
, 100));
276 // Try to fork as many processes as possible
277 ASSERT_FAILURE(pakfire_jail_exec(jail
, cmd_fork_bomb
, 0));
284 pakfire_jail_unref(jail
);
286 pakfire_cgroup_destroy(cgroup
);
287 pakfire_cgroup_unref(cgroup
);
293 static int test_file_ownership(const struct test
* t
) {
294 int r
= EXIT_FAILURE
;
297 // Execute a simple command
298 ASSERT_SUCCESS(pakfire_jail_run(t
->pakfire
, cmd_stat_ownership
, 0, &output
));
300 // Check if the file has been mapped to root/root
301 ASSERT_STRING_EQUALS(output
, "uid=0 gid=0\n");
313 static int test_bind(const struct test
* t
) {
314 struct pakfire_jail
* jail
= NULL
;
316 int r
= EXIT_FAILURE
;
318 const char* source
= "/";
319 const char* target
= "/oldroot";
321 const char* argv
[] = {
322 "/command", "check-mountpoint", target
, NULL
,
326 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
328 // Bind-mount nonsense
329 ASSERT_ERRNO(pakfire_jail_bind(jail
, NULL
, target
, 0), EINVAL
);
330 ASSERT_ERRNO(pakfire_jail_bind(jail
, source
, NULL
, 0), EINVAL
);
332 // Bind-mount something
333 ASSERT_SUCCESS(pakfire_jail_bind(jail
, source
, target
, MS_RDONLY
));
335 // Check if the mount actually works
336 ASSERT_SUCCESS(pakfire_jail_exec(jail
, argv
, 0));
343 pakfire_jail_unref(jail
);
348 static int callback_stdin(struct pakfire_ctx
* ctx
, struct pakfire_jail
* pakfire
, void* data
, int fd
) {
349 int* lines
= (int*)data
;
353 r
= dprintf(fd
, "LINE %d\n", *lines
);
360 LOG_ERROR("Could not write line (%u) to stdin: %m\n", *lines
);
365 // Decrement the lines counter
372 static int test_communicate(const struct test
* t
) {
373 struct pakfire_jail
* jail
= NULL
;
374 int r
= EXIT_FAILURE
;
376 // How many lines to send?
379 const char* argv
[] = {
380 "/command", "pipe", NULL
,
384 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
386 pakfire_jail_set_stdin_callback(jail
, callback_stdin
, &lines
);
388 // Check if the mount actually works
389 ASSERT_SUCCESS(pakfire_jail_exec(jail
, argv
, 0));
396 pakfire_jail_unref(jail
);
401 static int test_send_one_signal(const struct test
* t
,
402 struct pakfire_jail
* jail
, const char* signal
) {
403 const char* argv
[] = {
404 "/command", "send-signal", signal
, NULL
,
407 // Perform the command
408 return pakfire_jail_exec(jail
, argv
, 0);
411 static int test_send_signal(const struct test
* t
) {
412 struct pakfire_jail
* jail
= NULL
;
413 int r
= EXIT_FAILURE
;
416 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
418 // Sending SIGTERM to ourselves
419 ASSERT(test_send_one_signal(t
, jail
, "15") == 0);
421 // Sending SIGKILL to ourselves
422 ASSERT(test_send_one_signal(t
, jail
, "9") == 0);
424 // Sending SIGSTOP to ourselves (this should be ignored by the jail)
425 ASSERT(test_send_one_signal(t
, jail
, "23") == 0);
432 pakfire_jail_unref(jail
);
437 static int test_timeout(const struct test
* t
) {
438 struct pakfire_jail
* jail
= NULL
;
439 int r
= EXIT_FAILURE
;
441 const char* argv
[] = {
442 "/command", "sleep", "5", NULL
,
446 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
448 // Set a timeout of one second
449 ASSERT_SUCCESS(pakfire_jail_set_timeout(jail
, 1));
451 // Check if we receive the correct exit code
452 ASSERT(pakfire_jail_exec(jail
, argv
, 0) == 139);
459 pakfire_jail_unref(jail
);
464 int main(int argc
, const char* argv
[]) {
465 testsuite_add_test(test_create
, TEST_WANTS_PAKFIRE
);
466 testsuite_add_test(test_exit_code
, TEST_WANTS_PAKFIRE
);
467 testsuite_add_test(test_segv
, TEST_WANTS_PAKFIRE
);
468 testsuite_add_test(test_env
, TEST_WANTS_PAKFIRE
);
469 testsuite_add_test(test_exec
, TEST_WANTS_PAKFIRE
);
470 testsuite_add_test(test_launch_into_cgroup
, TEST_WANTS_PAKFIRE
);
471 testsuite_add_test(test_nice
, TEST_WANTS_PAKFIRE
);
472 testsuite_add_test(test_memory_limit
, TEST_WANTS_PAKFIRE
);
473 testsuite_add_test(test_pid_limit
, TEST_WANTS_PAKFIRE
);
475 testsuite_add_test(test_file_ownership
, TEST_WANTS_PAKFIRE
);
477 testsuite_add_test(test_bind
, TEST_WANTS_PAKFIRE
);
478 testsuite_add_test(test_communicate
, TEST_WANTS_PAKFIRE
);
479 testsuite_add_test(test_send_signal
, TEST_WANTS_PAKFIRE
);
480 testsuite_add_test(test_timeout
, TEST_WANTS_PAKFIRE
);
482 return testsuite_run(argc
, argv
);