]> git.ipfire.org Git - people/ms/pakfire.git/blob - tests/libpakfire/jail.c
tests: jail: Check for correct exit code
[people/ms/pakfire.git] / tests / libpakfire / jail.c
1 /*#############################################################################
2 # #
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2022 Pakfire development team #
5 # #
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. #
10 # #
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. #
15 # #
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/>. #
18 # #
19 #############################################################################*/
20
21 #include <sys/mount.h>
22
23 #include <pakfire/cgroup.h>
24 #include <pakfire/jail.h>
25
26 #include "../testsuite.h"
27
28 static const char* cmd_hello_world[] = {
29 "/command", "echo", "Hello World!", NULL,
30 };
31
32 static const char* cmd_exhaust_memory[] = {
33 "/command", "exhaust-memory", NULL,
34 };
35
36 static const char* cmd_fork_bomb[] = {
37 "/command", "fork-bomb", NULL,
38 };
39
40 static const char* cmd_stat_ownership[] = {
41 "/command", "stat-ownership", NULL,
42 };
43
44 static int test_create(const struct test* t) {
45 struct pakfire_jail* jail = NULL;
46
47 // Create a new jail
48 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0));
49
50 // Destroy it
51 ASSERT_NULL(pakfire_jail_unref(jail));
52
53 return EXIT_SUCCESS;
54
55 FAIL:
56 return EXIT_FAILURE;
57 }
58
59 static int test_exit_code(const struct test* t) {
60 struct pakfire_jail* jail = NULL;
61 int r = EXIT_FAILURE;
62
63 const char* argv[] = {
64 "/command", "exit-with-code", "123", NULL,
65 };
66
67 // Create a new jail
68 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0));
69
70 // Check if we receive the correct exit code
71 ASSERT(pakfire_jail_exec(jail, argv, NULL, NULL, NULL) == 123);
72
73 // Success
74 r = EXIT_SUCCESS;
75
76 FAIL:
77 if (jail)
78 pakfire_jail_unref(jail);
79
80 return r;
81 }
82
83 static int test_env(const struct test* t) {
84 struct pakfire_jail* jail = NULL;
85
86 // Create a new jail
87 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0));
88
89 // Check if the default variables are set
90 ASSERT(pakfire_jail_get_env(jail, "LANG"));
91 ASSERT(pakfire_jail_get_env(jail, "TERM"));
92
93 // Fetch a non-existing environment variable
94 ASSERT_NULL(pakfire_jail_get_env(jail, "VARIABLE"));
95
96 // Set a variable
97 ASSERT_SUCCESS(pakfire_jail_set_env(jail, "VARIABLE", "VALUE"));
98
99 // Read the value back
100 ASSERT_STRING_EQUALS(pakfire_jail_get_env(jail, "VARIABLE"), "VALUE");
101
102 // Destroy it
103 ASSERT_NULL(pakfire_jail_unref(jail));
104
105 return EXIT_SUCCESS;
106
107 FAIL:
108 return EXIT_FAILURE;
109 }
110
111 static int test_exec(const struct test* t) {
112 struct pakfire_jail* jail = NULL;
113
114 // Create a new jail
115 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0));
116
117 // Try to execute something
118 ASSERT_SUCCESS(pakfire_jail_exec(jail, cmd_hello_world, NULL, NULL, NULL));
119
120 // Destroy it
121 ASSERT_NULL(pakfire_jail_unref(jail));
122
123 return EXIT_SUCCESS;
124
125 FAIL:
126 return EXIT_FAILURE;
127 }
128
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;
133
134 // Create a new cgroup
135 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup, t->pakfire, "pakfire-test", 0));
136
137 // Create a new jail
138 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0));
139
140 // Connect jail to the cgroup
141 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail, cgroup));
142
143 // Run command
144 ASSERT(pakfire_jail_exec(jail, cmd_hello_world, NULL, NULL, NULL) == 0);
145
146 r = EXIT_SUCCESS;
147
148 FAIL:
149 if (cgroup) {
150 pakfire_cgroup_destroy(cgroup);
151 pakfire_cgroup_unref(cgroup);
152 }
153 if (jail)
154 pakfire_jail_unref(jail);
155
156 return r;
157 }
158
159 static int test_nice(const struct test* t) {
160 struct pakfire_jail* jail = NULL;
161 char* output = NULL;
162 int r = EXIT_FAILURE;
163
164 const char* argv[] = {
165 "/command", "print-nice", NULL,
166 };
167
168 // Create a new jail
169 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0));
170
171 // Set invalid nice levels
172 ASSERT_ERRNO(pakfire_jail_nice(jail, 100), EINVAL);
173 ASSERT_ERRNO(pakfire_jail_nice(jail, -100), EINVAL);
174
175 // Set something sane
176 ASSERT_SUCCESS(pakfire_jail_nice(jail, 5));
177
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");
182
183 // Success
184 r = EXIT_SUCCESS;
185
186 FAIL:
187 if (jail)
188 pakfire_jail_unref(jail);
189 if (output)
190 free(output);
191
192 return r;
193 }
194
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;
199
200
201 // Create cgroup
202 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup, t->pakfire, "pakfire-test", 0));
203
204 // Create jail
205 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0));
206
207 // Connect jail to the cgroup
208 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail, cgroup));
209
210 // Set a memory limit of 100 MiB
211 ASSERT_SUCCESS(pakfire_cgroup_set_memory_limit(cgroup, 100 * 1024 * 1024));
212
213 // Try to exhaust all memory
214 ASSERT_FAILURE(pakfire_jail_exec(jail, cmd_exhaust_memory, NULL, NULL, NULL));
215
216 // A fork bomb should also exhaust all memory
217 ASSERT_FAILURE(pakfire_jail_exec(jail, cmd_fork_bomb, NULL, NULL, NULL));
218
219 // Success
220 r = EXIT_SUCCESS;
221
222 FAIL:
223 if (jail)
224 pakfire_jail_unref(jail);
225 if (cgroup) {
226 pakfire_cgroup_destroy(cgroup);
227 pakfire_cgroup_unref(cgroup);
228 }
229
230 return r;
231 }
232
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;
237
238 // Create cgroup
239 ASSERT_SUCCESS(pakfire_cgroup_open(&cgroup, t->pakfire, "pakfire-test", 0));
240
241 // Create jail
242 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0));
243
244 // Connect jail to the cgroup
245 ASSERT_SUCCESS(pakfire_jail_set_cgroup(jail, cgroup));
246
247 // Set a PID limit of 100 processes
248 ASSERT_SUCCESS(pakfire_cgroup_set_pid_limit(cgroup, 100));
249
250 // Try to fork as many processes as possible
251 ASSERT_FAILURE(pakfire_jail_exec(jail, cmd_fork_bomb, NULL, NULL, NULL));
252
253 // Success
254 r = EXIT_SUCCESS;
255
256 FAIL:
257 if (jail)
258 pakfire_jail_unref(jail);
259 if (cgroup) {
260 pakfire_cgroup_destroy(cgroup);
261 pakfire_cgroup_unref(cgroup);
262 }
263
264 return r;
265 }
266
267 static int test_file_ownership(const struct test* t) {
268 int r = EXIT_FAILURE;
269 char* output = NULL;
270
271 // Execute a simple command
272 ASSERT_SUCCESS(pakfire_jail_run(t->pakfire, cmd_stat_ownership, 0, &output));
273
274 // Check if the file has been mapped to root/root
275 ASSERT_STRING_EQUALS(output, "uid=0 gid=0\n");
276
277 // Success
278 r = EXIT_SUCCESS;
279
280 FAIL:
281 if (output)
282 free(output);
283
284 return r;
285 }
286
287 static int test_bind(const struct test* t) {
288 struct pakfire_jail* jail = NULL;
289 char* output = NULL;
290 int r = EXIT_FAILURE;
291
292 const char* source = "/";
293 const char* target = "/oldroot";
294
295 const char* argv[] = {
296 "/command", "check-mountpoint", target, NULL,
297 };
298
299 // Create a new jail
300 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0));
301
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);
305
306 // Bind-mount something
307 ASSERT_SUCCESS(pakfire_jail_bind(jail, source, target, MS_RDONLY));
308
309 // Check if the mount actually works
310 ASSERT_SUCCESS(pakfire_jail_exec(jail, argv, NULL, NULL, NULL));
311
312 // Success
313 r = EXIT_SUCCESS;
314
315 FAIL:
316 if (jail)
317 pakfire_jail_unref(jail);
318
319 return r;
320 }
321
322 static int callback_stdin(struct pakfire* pakfire, void* data, int fd) {
323 const char* lines[] = { "a", "b", "c", NULL };
324 int r;
325
326 for (const char** line = lines; *line; line++) {
327 r = dprintf(fd, "%s\n", *line);
328 if (r < 0) {
329 LOG_ERROR("Could not write line (%s) to stdin: %m\n", *line);
330
331 return 1;
332 }
333 }
334
335 return 0;
336 }
337
338 static int test_communicate(const struct test* t) {
339 struct pakfire_jail* jail = NULL;
340 int r = EXIT_FAILURE;
341
342 const char* argv[] = {
343 "/command", "pipe", NULL,
344 };
345
346 // Create a new jail
347 ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire, 0));
348
349 // Check if the mount actually works
350 ASSERT_SUCCESS(pakfire_jail_exec(jail, argv, callback_stdin, NULL, NULL));
351
352 // Success
353 r = EXIT_SUCCESS;
354
355 FAIL:
356 if (jail)
357 pakfire_jail_unref(jail);
358
359 return r;
360 }
361
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);
374
375 return testsuite_run(argc, argv);
376 }