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
, NULL
, NULL
, NULL
, 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
, NULL
, NULL
, NULL
, 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
, NULL
, NULL
, NULL
, 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
->pakfire
, "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
, NULL
, NULL
, NULL
, 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 // Check if the nice level has been set
203 ASSERT_SUCCESS(pakfire_jail_exec(jail
, argv
,
204 NULL
, pakfire_jail_capture_stdout
, &output
, 0));
205 ASSERT_STRING_EQUALS(output
, "5\n");
212 pakfire_jail_unref(jail
);
219 static int test_memory_limit(const struct test
* t
) {
220 struct pakfire_cgroup
* cgroup
= NULL
;
221 struct pakfire_jail
* jail
= NULL
;
222 int r
= EXIT_FAILURE
;
226 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup
, t
->pakfire
, "pakfire-test", 0));
229 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
231 // Connect jail to the cgroup
232 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail
, cgroup
));
234 // Set a memory limit of 100 MiB
235 ASSERT_SUCCESS(pakfire_cgroup_set_memory_limit(cgroup
, 100 * 1024 * 1024));
237 // Try to exhaust all memory
238 ASSERT_FAILURE(pakfire_jail_exec(jail
, cmd_exhaust_memory
, NULL
, NULL
, NULL
, 0));
240 // A fork bomb should also exhaust all memory
241 ASSERT_FAILURE(pakfire_jail_exec(jail
, cmd_fork_bomb
, NULL
, NULL
, NULL
, 0));
248 pakfire_jail_unref(jail
);
250 pakfire_cgroup_destroy(cgroup
);
251 pakfire_cgroup_unref(cgroup
);
257 static int test_pid_limit(const struct test
* t
) {
258 struct pakfire_cgroup
* cgroup
= NULL
;
259 struct pakfire_jail
* jail
= NULL
;
260 int r
= EXIT_FAILURE
;
263 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup
, t
->pakfire
, "pakfire-test", 0));
266 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
268 // Connect jail to the cgroup
269 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail
, cgroup
));
271 // Set a PID limit of 100 processes
272 ASSERT_SUCCESS(pakfire_cgroup_set_pid_limit(cgroup
, 100));
274 // Try to fork as many processes as possible
275 ASSERT_FAILURE(pakfire_jail_exec(jail
, cmd_fork_bomb
, NULL
, NULL
, NULL
, 0));
282 pakfire_jail_unref(jail
);
284 pakfire_cgroup_destroy(cgroup
);
285 pakfire_cgroup_unref(cgroup
);
291 static int test_file_ownership(const struct test
* t
) {
292 int r
= EXIT_FAILURE
;
295 // Execute a simple command
296 ASSERT_SUCCESS(pakfire_jail_run(t
->pakfire
, cmd_stat_ownership
, 0, &output
));
298 // Check if the file has been mapped to root/root
299 ASSERT_STRING_EQUALS(output
, "uid=0 gid=0\n");
311 static int test_bind(const struct test
* t
) {
312 struct pakfire_jail
* jail
= NULL
;
314 int r
= EXIT_FAILURE
;
316 const char* source
= "/";
317 const char* target
= "/oldroot";
319 const char* argv
[] = {
320 "/command", "check-mountpoint", target
, NULL
,
324 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
326 // Bind-mount nonsense
327 ASSERT_ERRNO(pakfire_jail_bind(jail
, NULL
, target
, 0), EINVAL
);
328 ASSERT_ERRNO(pakfire_jail_bind(jail
, source
, NULL
, 0), EINVAL
);
330 // Bind-mount something
331 ASSERT_SUCCESS(pakfire_jail_bind(jail
, source
, target
, MS_RDONLY
));
333 // Check if the mount actually works
334 ASSERT_SUCCESS(pakfire_jail_exec(jail
, argv
, NULL
, NULL
, NULL
, 0));
341 pakfire_jail_unref(jail
);
346 static int callback_stdin(struct pakfire
* pakfire
, void* data
, int fd
) {
347 int* lines
= (int*)data
;
351 r
= dprintf(fd
, "LINE %d\n", *lines
);
353 LOG_ERROR("Could not write line (%u) to stdin: %m\n", *lines
);
358 // Decrement the lines counter
365 static int test_communicate(const struct test
* t
) {
366 struct pakfire_jail
* jail
= NULL
;
367 int r
= EXIT_FAILURE
;
369 // How many lines to send?
372 const char* argv
[] = {
373 "/command", "pipe", NULL
,
377 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
379 // Check if the mount actually works
380 ASSERT_SUCCESS(pakfire_jail_exec(jail
, argv
, callback_stdin
, NULL
, &lines
, 0));
387 pakfire_jail_unref(jail
);
392 static int test_send_one_signal(const struct test
* t
,
393 struct pakfire_jail
* jail
, const char* signal
) {
394 const char* argv
[] = {
395 "/command", "send-signal", signal
, NULL
,
398 // Perform the command
399 return pakfire_jail_exec(jail
, argv
, NULL
, NULL
, NULL
, 0);
402 static int test_send_signal(const struct test
* t
) {
403 struct pakfire_jail
* jail
= NULL
;
404 int r
= EXIT_FAILURE
;
407 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
409 // Sending SIGTERM to ourselves
410 ASSERT(test_send_one_signal(t
, jail
, "15") == 0);
412 // Sending SIGKILL to ourselves
413 ASSERT(test_send_one_signal(t
, jail
, "9") == 0);
415 // Sending SIGSTOP to ourselves (this should be ignored by the jail)
416 ASSERT(test_send_one_signal(t
, jail
, "23") == 0);
423 pakfire_jail_unref(jail
);
428 static int test_timeout(const struct test
* t
) {
429 struct pakfire_jail
* jail
= NULL
;
430 int r
= EXIT_FAILURE
;
432 const char* argv
[] = {
433 "/command", "sleep", "5", NULL
,
437 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
));
439 // Set a timeout of one second
440 ASSERT_SUCCESS(pakfire_jail_set_timeout(jail
, 1));
442 // Check if we receive the correct exit code
443 ASSERT(pakfire_jail_exec(jail
, argv
, NULL
, NULL
, NULL
, 0) == 139);
450 pakfire_jail_unref(jail
);
455 int main(int argc
, const char* argv
[]) {
456 testsuite_add_test(test_create
, TEST_WANTS_PAKFIRE
);
457 testsuite_add_test(test_exit_code
, TEST_WANTS_PAKFIRE
);
458 testsuite_add_test(test_segv
, TEST_WANTS_PAKFIRE
);
459 testsuite_add_test(test_env
, TEST_WANTS_PAKFIRE
);
460 testsuite_add_test(test_exec
, TEST_WANTS_PAKFIRE
);
461 testsuite_add_test(test_launch_into_cgroup
, TEST_WANTS_PAKFIRE
);
462 testsuite_add_test(test_nice
, TEST_WANTS_PAKFIRE
);
463 testsuite_add_test(test_memory_limit
, TEST_WANTS_PAKFIRE
);
464 testsuite_add_test(test_pid_limit
, TEST_WANTS_PAKFIRE
);
465 testsuite_add_test(test_file_ownership
, TEST_WANTS_PAKFIRE
);
466 testsuite_add_test(test_bind
, TEST_WANTS_PAKFIRE
);
467 testsuite_add_test(test_communicate
, TEST_WANTS_PAKFIRE
);
468 testsuite_add_test(test_send_signal
, TEST_WANTS_PAKFIRE
);
469 testsuite_add_test(test_timeout
, TEST_WANTS_PAKFIRE
);
471 return testsuite_run(argc
, argv
);