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
, 0));
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
, 0));
70 // Check if we receive the correct exit code
71 ASSERT(pakfire_jail_exec(jail
, argv
, NULL
, NULL
, NULL
) == 123);
78 pakfire_jail_unref(jail
);
83 static int test_env(const struct test
* t
) {
84 struct pakfire_jail
* jail
= NULL
;
87 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
, 0));
89 // Check if the default variables are set
90 ASSERT(pakfire_jail_get_env(jail
, "LANG"));
91 ASSERT(pakfire_jail_get_env(jail
, "TERM"));
93 // Fetch a non-existing environment variable
94 ASSERT_NULL(pakfire_jail_get_env(jail
, "VARIABLE"));
97 ASSERT_SUCCESS(pakfire_jail_set_env(jail
, "VARIABLE", "VALUE"));
99 // Read the value back
100 ASSERT_STRING_EQUALS(pakfire_jail_get_env(jail
, "VARIABLE"), "VALUE");
103 ASSERT_NULL(pakfire_jail_unref(jail
));
111 static int test_exec(const struct test
* t
) {
112 struct pakfire_jail
* jail
= NULL
;
115 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
, 0));
117 // Try to execute something
118 ASSERT_SUCCESS(pakfire_jail_exec(jail
, cmd_hello_world
, NULL
, NULL
, NULL
));
121 ASSERT_NULL(pakfire_jail_unref(jail
));
129 static int test_launch_into_cgroup(const struct test
* t
) {
130 struct pakfire_cgroup
* cgroup
= NULL
;
131 struct pakfire_jail
* jail
= NULL
;
132 int r
= EXIT_FAILURE
;
134 // Create a new cgroup
135 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup
, t
->pakfire
, "pakfire-test", 0));
138 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
, 0));
140 // Connect jail to the cgroup
141 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail
, cgroup
));
144 ASSERT(pakfire_jail_exec(jail
, cmd_hello_world
, NULL
, NULL
, NULL
) == 0);
150 pakfire_cgroup_destroy(cgroup
);
151 pakfire_cgroup_unref(cgroup
);
154 pakfire_jail_unref(jail
);
159 static int test_nice(const struct test
* t
) {
160 struct pakfire_jail
* jail
= NULL
;
162 int r
= EXIT_FAILURE
;
164 const char* argv
[] = {
165 "/command", "print-nice", NULL
,
169 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
, 0));
171 // Set invalid nice levels
172 ASSERT_ERRNO(pakfire_jail_nice(jail
, 100), EINVAL
);
173 ASSERT_ERRNO(pakfire_jail_nice(jail
, -100), EINVAL
);
175 // Set something sane
176 ASSERT_SUCCESS(pakfire_jail_nice(jail
, 5));
178 // Check if the nice level has been set
179 ASSERT_SUCCESS(pakfire_jail_exec(jail
, argv
,
180 NULL
, pakfire_jail_capture_stdout
, &output
));
181 ASSERT_STRING_EQUALS(output
, "5\n");
188 pakfire_jail_unref(jail
);
195 static int test_memory_limit(const struct test
* t
) {
196 struct pakfire_cgroup
* cgroup
= NULL
;
197 struct pakfire_jail
* jail
= NULL
;
198 int r
= EXIT_FAILURE
;
202 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup
, t
->pakfire
, "pakfire-test", 0));
205 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
, 0));
207 // Connect jail to the cgroup
208 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail
, cgroup
));
210 // Set a memory limit of 100 MiB
211 ASSERT_SUCCESS(pakfire_cgroup_set_memory_limit(cgroup
, 100 * 1024 * 1024));
213 // Try to exhaust all memory
214 ASSERT_FAILURE(pakfire_jail_exec(jail
, cmd_exhaust_memory
, NULL
, NULL
, NULL
));
216 // A fork bomb should also exhaust all memory
217 ASSERT_FAILURE(pakfire_jail_exec(jail
, cmd_fork_bomb
, NULL
, NULL
, NULL
));
224 pakfire_jail_unref(jail
);
226 pakfire_cgroup_destroy(cgroup
);
227 pakfire_cgroup_unref(cgroup
);
233 static int test_pid_limit(const struct test
* t
) {
234 struct pakfire_cgroup
* cgroup
= NULL
;
235 struct pakfire_jail
* jail
= NULL
;
236 int r
= EXIT_FAILURE
;
239 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup
, t
->pakfire
, "pakfire-test", 0));
242 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
, 0));
244 // Connect jail to the cgroup
245 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail
, cgroup
));
247 // Set a PID limit of 100 processes
248 ASSERT_SUCCESS(pakfire_cgroup_set_pid_limit(cgroup
, 100));
250 // Try to fork as many processes as possible
251 ASSERT_FAILURE(pakfire_jail_exec(jail
, cmd_fork_bomb
, NULL
, NULL
, NULL
));
258 pakfire_jail_unref(jail
);
260 pakfire_cgroup_destroy(cgroup
);
261 pakfire_cgroup_unref(cgroup
);
267 static int test_file_ownership(const struct test
* t
) {
268 int r
= EXIT_FAILURE
;
271 // Execute a simple command
272 ASSERT_SUCCESS(pakfire_jail_run(t
->pakfire
, cmd_stat_ownership
, 0, &output
));
274 // Check if the file has been mapped to root/root
275 ASSERT_STRING_EQUALS(output
, "uid=0 gid=0\n");
287 static int test_bind(const struct test
* t
) {
288 struct pakfire_jail
* jail
= NULL
;
290 int r
= EXIT_FAILURE
;
292 const char* source
= "/";
293 const char* target
= "/oldroot";
295 const char* argv
[] = {
296 "/command", "check-mountpoint", target
, NULL
,
300 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
, 0));
302 // Bind-mount nonsense
303 ASSERT_ERRNO(pakfire_jail_bind(jail
, NULL
, target
, 0), EINVAL
);
304 ASSERT_ERRNO(pakfire_jail_bind(jail
, source
, NULL
, 0), EINVAL
);
306 // Bind-mount something
307 ASSERT_SUCCESS(pakfire_jail_bind(jail
, source
, target
, MS_RDONLY
));
309 // Check if the mount actually works
310 ASSERT_SUCCESS(pakfire_jail_exec(jail
, argv
, NULL
, NULL
, NULL
));
317 pakfire_jail_unref(jail
);
322 static int callback_stdin(struct pakfire
* pakfire
, void* data
, int fd
) {
323 const char* lines
[] = { "a", "b", "c", NULL
};
326 for (const char** line
= lines
; *line
; line
++) {
327 r
= dprintf(fd
, "%s\n", *line
);
329 LOG_ERROR("Could not write line (%s) to stdin: %m\n", *line
);
338 static int test_communicate(const struct test
* t
) {
339 struct pakfire_jail
* jail
= NULL
;
340 int r
= EXIT_FAILURE
;
342 const char* argv
[] = {
343 "/command", "pipe", NULL
,
347 ASSERT_SUCCESS(pakfire_jail_create(&jail
, t
->pakfire
, 0));
349 // Check if the mount actually works
350 ASSERT_SUCCESS(pakfire_jail_exec(jail
, argv
, callback_stdin
, NULL
, NULL
));
357 pakfire_jail_unref(jail
);
362 int main(int argc
, const char* argv
[]) {
363 testsuite_add_test(test_create
);
364 testsuite_add_test(test_exit_code
);
365 testsuite_add_test(test_env
);
366 testsuite_add_test(test_exec
);
367 testsuite_add_test(test_launch_into_cgroup
);
368 testsuite_add_test(test_nice
);
369 testsuite_add_test(test_memory_limit
);
370 testsuite_add_test(test_pid_limit
);
371 testsuite_add_test(test_file_ownership
);
372 testsuite_add_test(test_bind
);
373 testsuite_add_test(test_communicate
);
375 return testsuite_run(argc
, argv
);