• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 26546993077

27 May 2026 08:34PM UTC coverage: 72.995% (+0.3%) from 72.667%
26546993077

push

github

bluca
test-pressure: set timeout to make not wait forever

If this runs on a slow or busy machine, then we may not get enough
pressure to trigger the event sources. In such case, the test does not
finish. It is problematic when the test is _not_ run with 'meson test',
e.g. debian/ubuntu CIs.

Let's introduce a timeout for each event loop, and skip test cases
gracefully.

8 of 12 new or added lines in 1 file covered. (66.67%)

19671 existing lines in 226 files now uncovered.

337119 of 461841 relevant lines covered (72.99%)

1326365.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

1.53
/src/test/test-execute.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fnmatch.h>
4
#include <gnu/libc-version.h>
5
#include <stdio.h>
6
#include <stdlib.h>
7
#include <sys/mount.h>
8
#include <sys/prctl.h>
9
#include <unistd.h>
10

11
#include "sd-event.h"
12

13
#include "argv-util.h"
14
#include "build-path.h"
15
#include "capability-util.h"
16
#include "copy.h"
17
#include "cpu-set-util.h"
18
#include "dropin.h"
19
#include "errno-list.h"
20
#include "event-util.h"
21
#include "extract-word.h"
22
#include "fd-util.h"
23
#include "fileio.h"
24
#include "fs-util.h"
25
#include "libmount-util.h"
26
#include "manager.h"
27
#include "mkdir.h"
28
#include "mount-util.h"
29
#include "path-util.h"
30
#include "process-util.h"
31
#include "rm-rf.h"
32
#include "seccomp-util.h"
33
#include "service.h"
34
#include "signal-util.h"
35
#include "stat-util.h"
36
#include "static-destruct.h"
37
#include "strv.h"
38
#include "sysctl-util.h"
39
#include "tests.h"
40
#include "unit.h"
41
#include "user-util.h"
42
#include "virt.h"
43

44
#define PRIVATE_UNIT_DIR "/run/test-execute-unit-dir"
45

46
static char *user_runtime_unit_dir = NULL;
47
static bool can_unshare;
48
static bool have_net_dummy;
49
static bool have_netns;
50
static unsigned n_ran_tests = 0;
51

52
STATIC_DESTRUCTOR_REGISTER(user_runtime_unit_dir, freep);
1✔
53

54
typedef void (*test_function_t)(Manager *m);
55

56
static int cld_dumped_to_killed(int code) {
×
57
        /* Depending on the system, seccomp version, … some signals might result in dumping, others in plain
58
         * killing. Let's ignore the difference here, and map both cases to CLD_KILLED */
59
        return code == CLD_DUMPED ? CLD_KILLED : code;
×
60
}
61

62
_noreturn_
63
static int time_handler(sd_event_source *s, uint64_t usec, void *userdata) {
×
64
        Unit *unit = ASSERT_PTR(userdata);
×
65
        int r;
×
66

67
        log_error("Test timeout when testing %s", unit->id);
×
68
        r = unit_kill(unit, KILL_ALL, /* subgroup= */ NULL, SIGKILL, SI_USER, /* value= */ 0, /* ret_error= */ NULL);
×
69
        if (r < 0)
×
70
                log_error_errno(r, "Failed to kill %s, ignoring: %m", unit->id);
×
71

72
        abort();
×
73
}
74

75
static void wait_for_service_finish(Manager *m, Unit *unit) {
×
76
        Service *service = SERVICE(ASSERT_PTR(unit));
×
77
        usec_t timeout = 2 * USEC_PER_MINUTE;
×
78

79
        ASSERT_NOT_NULL(m);
×
80

81
        /* Bump the timeout when running in plain QEMU, as some more involved tests might start hitting the
82
         * default 2m timeout (like exec-dynamicuser-statedir.service) */
83
        if (detect_virtualization() == VIRTUALIZATION_QEMU)
×
84
                timeout *= 2;
×
85

86
        printf("%s\n", unit->id);
×
87
        exec_context_dump(&service->exec_context, stdout, "\t");
×
88

89
        _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL;
×
90
        ASSERT_OK(sd_event_add_time_relative(m->event, &s, CLOCK_MONOTONIC, timeout, 0, time_handler, unit));
×
91

92
        /* Here, sd_event_loop() cannot be used, as the sd_event object will be reused in the next test case. */
93
        while (!IN_SET(service->state, SERVICE_DEAD, SERVICE_FAILED))
×
94
                ASSERT_OK(sd_event_run(m->event, 100 * USEC_PER_MSEC));
×
95
}
×
96

97
static void check_main_result(const char *file, unsigned line, const char *func,
×
98
                              Manager *m, Unit *unit, int status_expected, int code_expected) {
99
        Service *service = NULL;
×
100

101
        ASSERT_NOT_NULL(m);
×
102
        ASSERT_NOT_NULL(unit);
×
103

104
        wait_for_service_finish(m, unit);
×
105

106
        service = SERVICE(unit);
×
107
        exec_status_dump(&service->main_exec_status, stdout, "\t");
×
108

109
        if (cld_dumped_to_killed(service->main_exec_status.code) != cld_dumped_to_killed(code_expected)) {
×
110
                log_error("%s:%u:%s %s: can_unshare=%s: exit code %d, expected %d",
×
111
                          file, line, func, unit->id, yes_no(can_unshare),
112
                          service->main_exec_status.code, code_expected);
113
                abort();
×
114
        }
115

116
        if (service->main_exec_status.status != status_expected) {
×
117
                log_error("%s:%u:%s: %s: can_unshare=%s: exit status %d, expected %d",
×
118
                          file, line, func, unit->id, yes_no(can_unshare),
119
                          service->main_exec_status.status, status_expected);
120
                abort();
×
121
        }
122
}
×
123

124
static void check_service_result(const char *file, unsigned line, const char *func,
×
125
                                 Manager *m, Unit *unit, ServiceResult result_expected) {
126
        Service *service = NULL;
×
127

128
        ASSERT_NOT_NULL(m);
×
129
        ASSERT_NOT_NULL(unit);
×
130

131
        wait_for_service_finish(m, unit);
×
132

133
        service = SERVICE(unit);
×
134

135
        if (service->result != result_expected) {
×
136
                log_error("%s:%u:%s: %s: can_unshare=%s: service end result %s, expected %s",
×
137
                          file, line, func, unit->id, yes_no(can_unshare),
138
                          service_result_to_string(service->result),
139
                          service_result_to_string(result_expected));
140
                abort();
×
141
        }
142
}
×
143

144
static bool check_nobody_user_and_group(void) {
×
145
        static int cache = -1;
×
146
        struct passwd *p;
×
147
        struct group *g;
×
148

149
        if (cache >= 0)
×
150
                return !!cache;
×
151

152
        if (!synthesize_nobody())
×
153
                goto invalid;
×
154

155
        p = getpwnam(NOBODY_USER_NAME);
×
156
        if (!p ||
×
157
            !streq(p->pw_name, NOBODY_USER_NAME) ||
×
158
            p->pw_uid != UID_NOBODY ||
×
159
            p->pw_gid != GID_NOBODY)
×
160
                goto invalid;
×
161

162
        p = getpwuid(UID_NOBODY);
×
163
        if (!p ||
×
164
            !streq(p->pw_name, NOBODY_USER_NAME) ||
×
165
            p->pw_uid != UID_NOBODY ||
×
166
            p->pw_gid != GID_NOBODY)
×
167
                goto invalid;
×
168

169
        g = getgrnam(NOBODY_GROUP_NAME);
×
170
        if (!g ||
×
171
            !streq(g->gr_name, NOBODY_GROUP_NAME) ||
×
172
            g->gr_gid != GID_NOBODY)
×
173
                goto invalid;
×
174

175
        g = getgrgid(GID_NOBODY);
×
176
        if (!g ||
×
177
            !streq(g->gr_name, NOBODY_GROUP_NAME) ||
×
178
            g->gr_gid != GID_NOBODY)
×
179
                goto invalid;
×
180

181
        cache = 1;
×
182
        return true;
×
183

184
invalid:
×
185
        cache = 0;
×
186
        return false;
×
187
}
188

189
static bool check_user_has_group_with_same_name(const char *name) {
×
190
        struct passwd *p;
×
191
        struct group *g;
×
192

193
        ASSERT_NOT_NULL(name);
×
194

195
        p = getpwnam(name);
×
196
        if (!p ||
×
197
            !streq(p->pw_name, name))
×
198
                return false;
199

200
        g = getgrgid(p->pw_gid);
×
201
        if (!g ||
×
202
            !streq(g->gr_name, name))
×
203
                return false;
×
204

205
        return true;
206
}
207

208
static bool is_inaccessible_available(void) {
×
209
        FOREACH_STRING(p,
×
210
                       "/run/systemd/inaccessible/reg",
211
                       "/run/systemd/inaccessible/dir",
212
                       "/run/systemd/inaccessible/chr",
213
                       "/run/systemd/inaccessible/blk",
214
                       "/run/systemd/inaccessible/fifo",
215
                       "/run/systemd/inaccessible/sock")
216
                if (access(p, F_OK) < 0)
×
217
                        return false;
×
218

219
        return true;
×
220
}
221

222
static void start_parent_slices(Unit *unit) {
×
223
        Unit *slice;
×
224

225
        slice = UNIT_GET_SLICE(unit);
×
226
        if (slice) {
×
227
                start_parent_slices(slice);
×
228
                ASSERT_OK_OR(unit_start(slice, NULL), -EALREADY);
×
229
        }
230
}
×
231

232
static bool apparmor_restrict_unprivileged_userns(void) {
×
233
        _cleanup_free_ char *v = NULL;
×
234
        int r;
×
235

236
        /* If kernel.apparmor_restrict_unprivileged_userns=1, then we cannot
237
         * use unprivileged user namespaces. */
238
        r = sysctl_read("kernel/apparmor_restrict_unprivileged_userns", &v);
×
239
        if (r < 0) {
×
240
                if (r != -ENOENT)
×
241
                        log_debug_errno(r, "Failed to read kernel.apparmor_restrict_unprivileged_userns sysctl, ignoring: %m");
×
242

243
                return false;
244
        }
245

246
        return streq(v, "1");
×
247
}
248

249
static bool have_userns_privileges(void) {
×
250
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
×
251
        int r;
×
252

253
        if (apparmor_restrict_unprivileged_userns())
×
254
                return false;
255

256
        r = ASSERT_OK(pidref_safe_fork(
×
257
                        "(sd-test-check-userns)",
258
                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL,
259
                        &pidref));
260

261
        if (r == 0) {
×
262
                /* Keep CAP_SYS_ADMIN if we have it to ensure we give an
263
                 * accurate result to the caller. Some kernels have a
264
                 * kernel.unprivileged_userns_clone sysctl which can be
265
                 * configured to make CLONE_NEWUSER require CAP_SYS_ADMIN.
266
                 * Additionally, AppArmor may restrict unprivileged user
267
                 * namespace creation. */
268
                r = capability_bounding_set_drop(UINT64_C(1) << CAP_SYS_ADMIN, /* right_now= */ true);
×
269
                if (r < 0) {
×
270
                        log_debug_errno(r, "Failed to drop capabilities: %m");
×
271
                        _exit(2);
×
272
                }
273

274
                r = RET_NERRNO(unshare(CLONE_NEWUSER));
×
275
                if (r < 0 && !ERRNO_IS_NEG_PRIVILEGE(r))
×
276
                        log_debug_errno(r, "Failed to create user namespace: %m");
×
277

278
                _exit(r >= 0 ? EXIT_SUCCESS : ERRNO_IS_NEG_PRIVILEGE(r) ? EXIT_FAILURE : 2);
×
279
        }
280

281
        /* The exit code records the result of the check:
282
         *  EXIT_SUCCESS => we can use user namespaces
283
         *  EXIT_FAILURE => we can NOT use user namespaces
284
         *  2            => some other error occurred */
285
        r = pidref_wait_for_terminate_and_check("(sd-test-check-userns)", &pidref, 0);
×
286
        if (!IN_SET(r, EXIT_SUCCESS, EXIT_FAILURE))
×
287
                log_debug("Failed to check if user namespaces can be used, assuming not.");
×
288

289
        return r == EXIT_SUCCESS;
×
290
}
291

292
static void _test(const char *file, unsigned line, const char *func,
×
293
                  Manager *m, const char *unit_name, int status_expected, int code_expected) {
294
        Unit *unit;
×
295

296
        ASSERT_NOT_NULL(unit_name);
×
297

298
        ASSERT_OK(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit));
×
299
        /* We need to start the slices as well otherwise the slice cgroups might be pruned
300
         * in on_cgroup_empty_event. */
301
        start_parent_slices(unit);
×
302
        ASSERT_OK(unit_start(unit, NULL));
×
303
        check_main_result(file, line, func, m, unit, status_expected, code_expected);
×
304

305
        ++n_ran_tests;
×
306
}
×
307
#define test(m, unit_name, status_expected, code_expected) \
308
        _test(PROJECT_FILE, __LINE__, __func__, m, unit_name, status_expected, code_expected)
309

310
static void _test_service(const char *file, unsigned line, const char *func,
×
311
                          Manager *m, const char *unit_name, ServiceResult result_expected) {
312
        Unit *unit;
×
313

314
        ASSERT_NOT_NULL(unit_name);
×
315

316
        ASSERT_OK(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit));
×
317
        ASSERT_OK(unit_start(unit, NULL));
×
318
        check_service_result(file, line, func, m, unit, result_expected);
×
319
}
×
320
#define test_service(m, unit_name, result_expected) \
321
        _test_service(PROJECT_FILE, __LINE__, __func__, m, unit_name, result_expected)
322

323
static void test_exec_bindpaths(Manager *m) {
×
324
        ASSERT_OK(mkdir_p("/tmp/test-exec-bindpaths", 0755));
×
325
        ASSERT_OK(mkdir_p("/tmp/test-exec-bindreadonlypaths", 0755));
×
326

327
        test(m, "exec-bindpaths.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
×
328

329
        (void) rm_rf("/tmp/test-exec-bindpaths", REMOVE_ROOT|REMOVE_PHYSICAL);
×
330
        (void) rm_rf("/tmp/test-exec-bindreadonlypaths", REMOVE_ROOT|REMOVE_PHYSICAL);
×
331
}
×
332

333
static void test_exec_cpuaffinity(Manager *m) {
×
334
        _cleanup_(cpu_set_done) CPUSet c = {};
×
335

336
        ASSERT_OK(cpu_set_realloc(&c, 8192)); /* just allocate the maximum possible size */
×
337
        ASSERT_OK_ERRNO(sched_getaffinity(0, c.allocated, c.set));
×
338

339
        if (!CPU_ISSET_S(0, c.allocated, c.set)) {
×
340
                log_notice("Cannot use CPU 0, skipping %s", __func__);
×
341
                return;
342
        }
343

344
        test(m, "exec-cpuaffinity1.service", 0, CLD_EXITED);
×
345
        test(m, "exec-cpuaffinity2.service", 0, CLD_EXITED);
×
346

347
        if (!CPU_ISSET_S(1, c.allocated, c.set) ||
×
348
            !CPU_ISSET_S(2, c.allocated, c.set)) {
×
349
                log_notice("Cannot use CPU 1 or 2, skipping remaining tests in %s", __func__);
×
350
                return;
351
        }
352

353
        test(m, "exec-cpuaffinity3.service", 0, CLD_EXITED);
×
354
}
355

356
static void test_exec_credentials(Manager *m) {
×
357
        test(m, "exec-set-credential.service", 0, CLD_EXITED);
×
358
        test(m, "exec-load-credential.service", 0, CLD_EXITED);
×
359
        test(m, "exec-credentials-dir-specifier.service", 0, CLD_EXITED);
×
360
}
×
361

362
static void test_exec_workingdirectory(Manager *m) {
×
363
        ASSERT_OK(mkdir_p("/tmp/test-exec_workingdirectory", 0755));
×
364

365
        test(m, "exec-workingdirectory.service", 0, CLD_EXITED);
×
366
        test(m, "exec-workingdirectory-trailing-dot.service", 0, CLD_EXITED);
×
367

368
        (void) rm_rf("/tmp/test-exec_workingdirectory", REMOVE_ROOT|REMOVE_PHYSICAL);
×
369
}
×
370

371
static void test_exec_execsearchpath(Manager *m) {
×
372
        int r;
×
373

374
        ASSERT_OK(r = is_symlink("/bin/ls"));
×
375
        if (r > 0)
×
376
                return (void) log_tests_skipped("/bin/ls is a symlink, maybe coreutils is built with --enable-single-binary=symlinks");
×
377

378
        ASSERT_OK(mkdir_p("/tmp/test-exec_execsearchpath", 0755));
×
379

380
        ASSERT_OK(copy_file("/bin/ls", "/tmp/test-exec_execsearchpath/ls_temp", 0,  0777, COPY_REPLACE));
×
381

382
        test(m, "exec-execsearchpath.service", 0, CLD_EXITED);
×
383

384
        ASSERT_OK(rm_rf("/tmp/test-exec_execsearchpath", REMOVE_ROOT|REMOVE_PHYSICAL));
×
385

386
        test(m, "exec-execsearchpath.service", EXIT_EXEC, CLD_EXITED);
×
387
}
388

389
static void test_exec_execsearchpath_specifier(Manager *m) {
×
390
        test(m, "exec-execsearchpath-unit-specifier.service", 0, CLD_EXITED);
×
391
}
×
392

393
static void test_exec_execsearchpath_environment(Manager *m) {
×
394
        test(m, "exec-execsearchpath-environment.service", 0, CLD_EXITED);
×
395
        test(m, "exec-execsearchpath-environment-path-set.service", 0, CLD_EXITED);
×
396
}
×
397

398
static void test_exec_execsearchpath_environment_files(Manager *m) {
×
399
        static const char path_not_set[] =
×
400
                "VAR1='word1 word2'\n"
401
                "VAR2=word3 \n"
402
                "# comment1\n"
403
                "\n"
404
                "; comment2\n"
405
                " ; # comment3\n"
406
                "line without an equal\n"
407
                "VAR3='$word 5 6'\n"
408
                "VAR4='new\nline'\n"
409
                "VAR5=password\\with\\backslashes";
410

411
        static const char path_set[] =
×
412
                "VAR1='word1 word2'\n"
413
                "VAR2=word3 \n"
414
                "# comment1\n"
415
                "\n"
416
                "; comment2\n"
417
                " ; # comment3\n"
418
                "line without an equal\n"
419
                "VAR3='$word 5 6'\n"
420
                "VAR4='new\nline'\n"
421
                "VAR5=password\\with\\backslashes\n"
422
                "PATH=/usr";
423

424
        int r;
×
425

426
        r = write_string_file("/tmp/test-exec_execsearchpath_environmentfile.conf", path_not_set, WRITE_STRING_FILE_CREATE);
×
427
        ASSERT_OK(r);
×
428

429
        test(m, "exec-execsearchpath-environmentfile.service", 0, CLD_EXITED);
×
430

431
        (void) unlink("/tmp/test-exec_environmentfile.conf");
×
432

433
        r = write_string_file("/tmp/test-exec_execsearchpath_environmentfile-set.conf", path_set, WRITE_STRING_FILE_CREATE);
×
434
        ASSERT_OK(r);
×
435

436
        test(m, "exec-execsearchpath-environmentfile-set.service", 0, CLD_EXITED);
×
437

438
        (void) unlink("/tmp/test-exec_environmentfile-set.conf");
×
439
}
×
440

441
static void test_exec_execsearchpath_passenvironment(Manager *m) {
×
442
        ASSERT_OK_ERRNO(setenv("VAR1", "word1 word2", 1));
×
443
        ASSERT_OK_ERRNO(setenv("VAR2", "word3", 1));
×
444
        ASSERT_OK_ERRNO(setenv("VAR3", "$word 5 6", 1));
×
445
        ASSERT_OK_ERRNO(setenv("VAR4", "new\nline", 1));
×
446
        ASSERT_OK_ERRNO(setenv("VAR5", "passwordwithbackslashes", 1));
×
447

448
        test(m, "exec-execsearchpath-passenvironment.service", 0, CLD_EXITED);
×
449

450
        ASSERT_OK_ERRNO(setenv("PATH", "/usr", 1));
×
451
        test(m, "exec-execsearchpath-passenvironment-set.service", 0, CLD_EXITED);
×
452

453
        ASSERT_OK_ERRNO(unsetenv("VAR1"));
×
454
        ASSERT_OK_ERRNO(unsetenv("VAR2"));
×
455
        ASSERT_OK_ERRNO(unsetenv("VAR3"));
×
456
        ASSERT_OK_ERRNO(unsetenv("VAR4"));
×
457
        ASSERT_OK_ERRNO(unsetenv("VAR5"));
×
458
        ASSERT_OK_ERRNO(unsetenv("PATH"));
×
459
}
×
460

461
static void test_exec_personality(Manager *m) {
×
462
#if defined(__x86_64__)
463
        test(m, "exec-personality-x86-64.service", 0, CLD_EXITED);
×
464

465
#elif defined(__s390x__)
466
        test(m, "exec-personality-s390x.service", 0, CLD_EXITED);
467

468
#elif defined(__s390__)
469
        test(m, "exec-personality-s390.service", 0, CLD_EXITED);
470

471
#elif defined(__powerpc64__)
472
#  if __BYTE_ORDER == __BIG_ENDIAN
473
        test(m, "exec-personality-ppc64.service", 0, CLD_EXITED);
474
#  else
475
        test(m, "exec-personality-ppc64le.service", 0, CLD_EXITED);
476
#  endif
477

478
#elif defined(__aarch64__)
479
        test(m, "exec-personality-aarch64.service", 0, CLD_EXITED);
480

481
#elif defined(__i386__)
482
        test(m, "exec-personality-x86.service", 0, CLD_EXITED);
483
#elif defined(__loongarch_lp64)
484
        test(m, "exec-personality-loongarch64.service", 0, CLD_EXITED);
485
#else
486
        log_notice("Unknown personality, skipping %s", __func__);
487
#endif
488
}
×
489

490
static void test_exec_ignoresigpipe(Manager *m) {
×
491
        test(m, "exec-ignoresigpipe-yes.service", 0, CLD_EXITED);
×
492
        test(m, "exec-ignoresigpipe-no.service", SIGPIPE, CLD_KILLED);
×
493
}
×
494

495
static void test_exec_privatetmp(Manager *m) {
×
496
        ASSERT_OK(touch("/tmp/test-exec_privatetmp"));
×
497

498
        if (MANAGER_IS_SYSTEM(m) || have_userns_privileges()) {
×
499
                test(m, "exec-privatetmp-yes.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
×
500
                test(m, "exec-privatetmp-disabled-by-prefix.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
×
501

502
                (void) unlink("/tmp/test-exec_privatetmp_disconnected");
×
503
                test(m, "exec-privatetmp-disconnected-nodefaultdeps-nor-sandboxing.service", 0, CLD_EXITED);
×
504
                ASSERT_OK_ERRNO(access("/tmp/test-exec_privatetmp_disconnected", F_OK));
×
505

506
                FOREACH_STRING(s,
×
507
                               "exec-privatetmp-disconnected.service",
508
                               "exec-privatetmp-disconnected-defaultdependencies-no.service",
509
                               "exec-privatetmp-disconnected-requires-mounts-for-var.service",
510
                               "exec-privatetmp-disconnected-wants-mounts-for-var.service",
511
                               "exec-privatetmp-disconnected-after-and-requires-for-var.service",
512
                               "exec-privatetmp-disconnected-after-and-wants-for-var.service") {
513
                        (void) unlink("/tmp/test-exec_privatetmp_disconnected");
×
514
                        (void) unlink("/var/tmp/test-exec_privatetmp_disconnected");
×
515
                        test(m, s, can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
×
516
                        ASSERT_FAIL(access("/tmp/test-exec_privatetmp_disconnected", F_OK));
×
517
                        ASSERT_FAIL(access("/var/tmp/test-exec_privatetmp_disconnected", F_OK));
×
518
                }
519
        }
520

521
        test(m, "exec-privatetmp-no.service", 0, CLD_EXITED);
×
522

523
        (void) unlink("/tmp/test-exec_privatetmp");
×
524
}
×
525

526
static void test_exec_privatedevices(Manager *m) {
×
527
        int r;
×
528

529
        if (detect_container() > 0) {
×
530
                log_notice("Testing in container, skipping %s", __func__);
×
531
                return;
532
        }
533
        if (!is_inaccessible_available()) {
×
534
                log_notice("Testing without inaccessible, skipping %s", __func__);
×
535
                return;
536
        }
537

538
        if (MANAGER_IS_SYSTEM(m) || have_userns_privileges()) {
×
539
                test(m, "exec-privatedevices-yes.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
×
540
                if (access("/dev/kmsg", F_OK) >= 0)
×
541
                        test(m, "exec-privatedevices-bind.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
×
542
                test(m, "exec-privatedevices-disabled-by-prefix.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
×
543
                test(m, "exec-privatedevices-yes-with-group.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
×
544
        }
545

546
        test(m, "exec-privatedevices-no.service", 0, CLD_EXITED);
×
547

548
        /* We use capsh to test if the capabilities are
549
         * properly set, so be sure that it exists */
550
        r = find_executable("capsh", NULL);
×
551
        if (r < 0) {
×
552
                log_notice_errno(r, "Could not find capsh binary, skipping remaining tests in %s: %m", __func__);
×
553
                return;
554
        }
555

556
        if (MANAGER_IS_SYSTEM(m) || have_userns_privileges()) {
×
557
                test(m, "exec-privatedevices-yes-capability-mknod.service", can_unshare || MANAGER_IS_SYSTEM(m) ? 0 : EXIT_NAMESPACE, CLD_EXITED);
×
558
                test(m, "exec-privatedevices-yes-capability-sys-rawio.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_NAMESPACE, CLD_EXITED);
×
559
        }
560

561
        test(m, "exec-privatedevices-no-capability-mknod.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_FAILURE, CLD_EXITED);
×
562
        test(m, "exec-privatedevices-no-capability-sys-rawio.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_FAILURE, CLD_EXITED);
×
563
}
564

565
static void test_exec_protecthome(Manager *m) {
×
566
        if (!can_unshare) {
×
567
                log_notice("Cannot reliably unshare, skipping %s", __func__);
×
568
                return;
569
        }
570

571
        test(m, "exec-protecthome-tmpfs-vs-protectsystem-strict.service", 0, CLD_EXITED);
×
572
}
573

574
static void test_exec_protectkernelmodules(Manager *m) {
×
575
        int r;
×
576

577
        if (detect_container() > 0) {
×
578
                log_notice("Testing in container, skipping %s", __func__);
×
579
                return;
580
        }
581
        if (!is_inaccessible_available()) {
×
582
                log_notice("Testing without inaccessible, skipping %s", __func__);
×
583
                return;
584
        }
585

586
        r = find_executable("capsh", NULL);
×
587
        if (r < 0) {
×
588
                log_notice_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
×
589
                return;
590
        }
591

592
        test(m, "exec-protectkernelmodules-no-capabilities.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_FAILURE, CLD_EXITED);
×
593

594
        if (MANAGER_IS_SYSTEM(m) || have_userns_privileges()) {
×
595
                test(m, "exec-protectkernelmodules-yes-capabilities.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_NAMESPACE, CLD_EXITED);
×
596
                test(m, "exec-protectkernelmodules-yes-mount-propagation.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
×
597
        }
598
}
599

600
static void test_exec_readonlypaths(Manager *m) {
×
601

602
        if (MANAGER_IS_SYSTEM(m) || have_userns_privileges())
×
603
                test(m, "exec-readonlypaths-simple.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
×
604

605
        if (path_is_read_only_fs("/var") > 0) {
×
606
                log_notice("Directory /var is readonly, skipping remaining tests in %s", __func__);
×
607
                return;
608
        }
609

610
        test(m, "exec-readonlypaths.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
×
611
        test(m, "exec-readonlypaths-with-bindpaths.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
×
612
        test(m, "exec-readonlypaths-mount-propagation.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
×
613
}
614

615
static void test_exec_readwritepaths(Manager *m) {
×
616

617
        if (path_is_read_only_fs("/") > 0) {
×
618
                log_notice("Root directory is readonly, skipping %s", __func__);
×
619
                return;
620
        }
621

622
        test(m, "exec-readwritepaths-mount-propagation.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
×
623
}
624

625
static void test_exec_inaccessiblepaths(Manager *m) {
×
626

627
        if (!is_inaccessible_available()) {
×
628
                log_notice("Testing without inaccessible, skipping %s", __func__);
×
629
                return;
630
        }
631

632
        if (MANAGER_IS_SYSTEM(m) || have_userns_privileges())
×
633
                test(m, "exec-inaccessiblepaths-sys.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
×
634

635
        if (path_is_read_only_fs("/") > 0) {
×
636
                log_notice("Root directory is readonly, skipping remaining tests in %s", __func__);
×
637
                return;
638
        }
639

640
        test(m, "exec-inaccessiblepaths-mount-propagation.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
×
641
}
642

643
#if !HAS_FEATURE_ADDRESS_SANITIZER
644
static int on_spawn_io(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
×
645
        char **result = userdata;
×
646
        char buf[4096];
×
647
        ssize_t l;
×
648

649
        ASSERT_NOT_NULL(s);
×
650
        ASSERT_GT(fd, 0);
×
651

652
        l = read(fd, buf, sizeof(buf) - 1);
×
653
        if (l < 0) {
×
654
                if (errno == EAGAIN)
×
655
                        goto reenable;
×
656

657
                return 0;
×
658
        }
659
        if (l == 0)
×
660
                return 0;
661

662
        buf[l] = '\0';
×
663
        if (result)
×
664
                ASSERT_NOT_NULL(strextend(result, buf));
×
665
        else
666
                log_error("ldd: %s", buf);
×
667

668
reenable:
×
669
        /* Re-enable the event source if we did not encounter EOF */
670
        ASSERT_OK(sd_event_source_set_enabled(s, SD_EVENT_ONESHOT));
×
671
        return 0;
672
}
673

674
static int on_spawn_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
×
675
        PidRef *pidref = ASSERT_PTR(userdata);
×
676

677
        (void) pidref_kill(pidref, SIGKILL);
×
678

679
        return 1;
×
680
}
681

682
static int on_spawn_exit(sd_event_source *s, const siginfo_t *si, void *userdata) {
×
683
        int ret = -EIO;
×
684

685
        ASSERT_NOT_NULL(si);
×
686

687
        if (si->si_code == CLD_EXITED)
×
688
                ret = si->si_status;
×
689

690
        sd_event_exit(sd_event_source_get_event(s), ret);
×
691
        return 1;
×
692
}
693

694
static int find_libraries(const char *exec, char ***ret) {
×
695
        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
×
696
        _cleanup_(sd_event_source_unrefp) sd_event_source *sigchld_source = NULL;
×
697
        _cleanup_(sd_event_source_unrefp) sd_event_source *stdout_source = NULL;
×
698
        _cleanup_(sd_event_source_unrefp) sd_event_source *stderr_source = NULL;
×
699
        _cleanup_close_pair_ int outpipe[2] = EBADF_PAIR, errpipe[2] = EBADF_PAIR;
×
700
        _cleanup_strv_free_ char **libraries = NULL;
×
701
        _cleanup_free_ char *result = NULL;
×
702
        int r;
×
703

704
        ASSERT_NOT_NULL(exec);
×
705
        ASSERT_NOT_NULL(ret);
×
706

707
        ASSERT_OK_ERRNO(pipe2(outpipe, O_NONBLOCK|O_CLOEXEC));
×
708
        ASSERT_OK_ERRNO(pipe2(errpipe, O_NONBLOCK|O_CLOEXEC));
×
709

710
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
×
711
        r = pidref_safe_fork_full(
×
712
                        "(spawn-ldd)",
713
                        (int[]) { -EBADF, outpipe[1], errpipe[1] },
×
714
                        NULL, 0,
715
                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG,
716
                        &pidref);
717
        ASSERT_OK(r);
×
718
        if (r == 0) {
×
719
                execlp("ldd", "ldd", exec, NULL);
×
720
                _exit(EXIT_FAILURE);
×
721
        }
722

723
        outpipe[1] = safe_close(outpipe[1]);
×
724
        errpipe[1] = safe_close(errpipe[1]);
×
725

726
        ASSERT_OK(sd_event_new(&e));
×
727

728
        ASSERT_OK(sd_event_add_time_relative(e, NULL, CLOCK_MONOTONIC,
×
729
                                             10 * USEC_PER_SEC, USEC_PER_SEC, on_spawn_timeout, &pidref));
730
        ASSERT_OK(sd_event_add_io(e, &stdout_source, outpipe[0], EPOLLIN, on_spawn_io, &result));
×
731
        ASSERT_OK(sd_event_source_set_enabled(stdout_source, SD_EVENT_ONESHOT));
×
732
        ASSERT_OK(sd_event_add_io(e, &stderr_source, errpipe[0], EPOLLIN, on_spawn_io, NULL));
×
733
        ASSERT_OK(sd_event_source_set_enabled(stderr_source, SD_EVENT_ONESHOT));
×
734
        ASSERT_OK(event_add_child_pidref(e, &sigchld_source, &pidref, WEXITED, on_spawn_exit, NULL));
×
735
        /* Child exit should be processed after IO is complete */
736
        ASSERT_OK(sd_event_source_set_priority(sigchld_source, SD_EVENT_PRIORITY_NORMAL + 1));
×
737

738
        ASSERT_OK(sd_event_loop(e));
×
739

740
        _cleanup_strv_free_ char **v = NULL;
×
741
        ASSERT_OK(strv_split_newlines_full(&v, result, 0));
×
742

743
        STRV_FOREACH(q, v) {
×
744
                _cleanup_free_ char *word = NULL;
×
745
                const char *p = *q;
×
746

747
                r = extract_first_word(&p, &word, NULL, 0);
×
748
                ASSERT_OK(r);
×
749
                if (r == 0)
×
750
                        continue;
×
751

752
                if (path_is_absolute(word)) {
×
753
                        ASSERT_OK(strv_consume(&libraries, TAKE_PTR(word)));
×
754
                        continue;
×
755
                }
756

757
                word = mfree(word);
×
758
                r = extract_first_word(&p, &word, NULL, 0);
×
759
                ASSERT_OK(r);
×
760
                if (r == 0)
×
761
                        continue;
×
762

763
                if (!streq_ptr(word, "=>"))
×
764
                        continue;
×
765

766
                word = mfree(word);
×
767
                r = extract_first_word(&p, &word, NULL, 0);
×
768
                ASSERT_OK(r);
×
769
                if (r == 0)
×
770
                        continue;
×
771

772
                if (path_is_absolute(word)) {
×
773
                        ASSERT_OK(strv_consume(&libraries, TAKE_PTR(word)));
×
774
                        continue;
×
775
                }
776
        }
777

778
        *ret = TAKE_PTR(libraries);
×
779
        return 0;
×
780
}
781
#endif
782

783
static void test_exec_mount_apivfs(Manager *m) {
×
784
#if !HAS_FEATURE_ADDRESS_SANITIZER
785
        _cleanup_free_ char *fullpath_touch = NULL, *fullpath_test = NULL, *data = NULL;
×
786
        _cleanup_strv_free_ char **libraries = NULL, **libraries_test = NULL;
×
787
        int r;
×
788

789
        ASSERT_NOT_NULL(user_runtime_unit_dir);
×
790

791
        r = find_executable("ldd", NULL);
×
792
        if (r < 0) {
×
793
                log_notice_errno(r, "Skipping %s, could not find 'ldd' command: %m", __func__);
×
794
                return;
795
        }
796
        r = find_executable("touch", &fullpath_touch);
×
797
        if (r < 0) {
×
798
                log_notice_errno(r, "Skipping %s, could not find 'touch' command: %m", __func__);
×
799
                return;
800
        }
801
        r = find_executable("test", &fullpath_test);
×
802
        if (r < 0) {
×
803
                log_notice_errno(r, "Skipping %s, could not find 'test' command: %m", __func__);
×
804
                return;
805
        }
806

807
        if (MANAGER_IS_USER(m) && !have_userns_privileges())
×
808
                return (void)log_notice("Skipping %s, do not have user namespace privileges", __func__);
×
809

810
        ASSERT_OK(find_libraries(fullpath_touch, &libraries));
×
811
        ASSERT_OK(find_libraries(fullpath_test, &libraries_test));
×
812
        ASSERT_OK(strv_extend_strv(&libraries, libraries_test, true));
×
813

814
        ASSERT_NOT_NULL(strextend(&data, "[Service]\n"));
×
815
        ASSERT_NOT_NULL((strextend(&data, "ExecStart=", fullpath_touch, " /aaa\n")));
×
816
        ASSERT_NOT_NULL((strextend(&data, "ExecStart=", fullpath_test, " -f /aaa\n")));
×
817
        ASSERT_NOT_NULL((strextend(&data, "BindReadOnlyPaths=", fullpath_touch, "\n")));
×
818
        ASSERT_NOT_NULL((strextend(&data, "BindReadOnlyPaths=", fullpath_test, "\n")));
×
819

820
        STRV_FOREACH(p, libraries)
×
821
                ASSERT_NOT_NULL((strextend(&data, "BindReadOnlyPaths=", *p, "\n")));
×
822

823
        ASSERT_OK(write_drop_in(user_runtime_unit_dir, "exec-mount-apivfs-no.service", 10, "bind-mount", data));
×
824

825
        ASSERT_OK(mkdir_p("/tmp/test-exec-mount-apivfs-no/root", 0755));
×
826

827
        test(m, "exec-mount-apivfs-no.service", can_unshare || !MANAGER_IS_SYSTEM(m) ? 0 : EXIT_NAMESPACE, CLD_EXITED);
×
828

829
        (void) rm_rf("/tmp/test-exec-mount-apivfs-no/root", REMOVE_ROOT|REMOVE_PHYSICAL);
×
830
#endif
831
}
832

833
static void test_exec_noexecpaths(Manager *m) {
×
834

835
        if (MANAGER_IS_SYSTEM(m) || have_userns_privileges())
×
836
                test(m, "exec-noexecpaths-simple.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
×
837
        else
838
                return (void)log_notice("Skipping %s, do not have user namespace privileges", __func__);
×
839
}
840

841
static void test_exec_temporaryfilesystem(Manager *m) {
×
842

843
        test(m, "exec-temporaryfilesystem-options.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
×
844
        test(m, "exec-temporaryfilesystem-ro.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
×
845
        test(m, "exec-temporaryfilesystem-rw.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
×
846
        test(m, "exec-temporaryfilesystem-usr.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
×
847
}
×
848

849
static void test_exec_systemcallfilter(Manager *m) {
×
850
#if HAVE_SECCOMP && !HAS_FEATURE_ADDRESS_SANITIZER
851
        int r;
×
852

853
        if (!is_seccomp_available()) {
×
854
                log_notice("Seccomp not available, skipping %s", __func__);
×
855
                return;
856
        }
857

858
        test(m, "exec-systemcallfilter-writing-handoff-timestamp.service", 0, CLD_EXITED);
×
859

860
        test(m, "exec-systemcallfilter-not-failing.service", 0, CLD_EXITED);
×
861
        test(m, "exec-systemcallfilter-not-failing2.service", 0, CLD_EXITED);
×
862
        test(m, "exec-systemcallfilter-not-failing3.service", 0, CLD_EXITED);
×
863
        test(m, "exec-systemcallfilter-failing.service", SIGSYS, CLD_KILLED);
×
864
        test(m, "exec-systemcallfilter-failing2.service", SIGSYS, CLD_KILLED);
×
865
        test(m, "exec-systemcallfilter-failing3.service", SIGSYS, CLD_KILLED);
×
866

867
        r = find_executable("python3", NULL);
×
868
        if (r < 0) {
×
869
                log_notice_errno(r, "Skipping remaining tests in %s, could not find python3 binary: %m", __func__);
×
870
                return;
871
        }
872

873
        test(m, "exec-systemcallfilter-with-errno-name.service", errno_from_name("EILSEQ"), CLD_EXITED);
×
874
        test(m, "exec-systemcallfilter-with-errno-number.service", 255, CLD_EXITED);
×
875
        test(m, "exec-systemcallfilter-with-errno-multi.service", errno_from_name("EILSEQ"), CLD_EXITED);
×
876
        test(m, "exec-systemcallfilter-with-errno-in-allow-list.service", errno_from_name("EILSEQ"), CLD_EXITED);
×
877
        test(m, "exec-systemcallfilter-override-error-action.service", SIGSYS, CLD_KILLED);
×
878
        test(m, "exec-systemcallfilter-override-error-action2.service", errno_from_name("EILSEQ"), CLD_EXITED);
×
879

880
        test(m, "exec-systemcallfilter-nonewprivileges.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
×
881
        test(m, "exec-systemcallfilter-nonewprivileges-protectclock.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
×
882

883
        r = find_executable("capsh", NULL);
×
884
        if (r < 0) {
×
885
                log_notice_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
×
886
                return;
887
        }
888

889
        test(m, "exec-systemcallfilter-nonewprivileges-bounding1.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
×
890
        test(m, "exec-systemcallfilter-nonewprivileges-bounding2.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
×
891
#endif
892
}
893

894
static void test_exec_systemcallerrornumber(Manager *m) {
×
895
#if HAVE_SECCOMP && !HAS_FEATURE_ADDRESS_SANITIZER
896
        int r;
×
897

898
        if (!is_seccomp_available()) {
×
899
                log_notice("Seccomp not available, skipping %s", __func__);
×
900
                return;
901
        }
902

903
        r = find_executable("python3", NULL);
×
904
        if (r < 0) {
×
905
                log_notice_errno(r, "Skipping %s, could not find python3 binary: %m", __func__);
×
906
                return;
907
        }
908

909
        test(m, "exec-systemcallerrornumber-name.service", errno_from_name("EACCES"), CLD_EXITED);
×
910
        test(m, "exec-systemcallerrornumber-number.service", 255, CLD_EXITED);
×
911
#endif
912
}
913

914
static void test_exec_restrictnamespaces(Manager *m) {
×
915
#if HAVE_SECCOMP
916
        if (!is_seccomp_available()) {
×
917
                log_notice("Seccomp not available, skipping %s", __func__);
×
918
                return;
919
        }
920

921
        test(m, "exec-restrictnamespaces-no.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
×
922
        test(m, "exec-restrictnamespaces-yes.service", 1, CLD_EXITED);
×
923
        test(m, "exec-restrictnamespaces-mnt.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
×
924
        test(m, "exec-restrictnamespaces-mnt-deny-list.service", 1, CLD_EXITED);
×
925
        test(m, "exec-restrictnamespaces-merge-and.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
×
926
        test(m, "exec-restrictnamespaces-merge-or.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
×
927
        test(m, "exec-restrictnamespaces-merge-all.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED);
×
928
#endif
929
}
930

931
static void test_exec_systemcallfilter_system(Manager *m) {
×
932
/* Skip this particular test case when running under ASan, as
933
 * LSan intermittently segfaults when accessing memory right
934
 * after the test finishes. Generally, ASan & LSan don't like
935
 * the seccomp stuff.
936
 */
937
#if HAVE_SECCOMP && !HAS_FEATURE_ADDRESS_SANITIZER
938
        if (!is_seccomp_available()) {
×
939
                log_notice("Seccomp not available, skipping %s", __func__);
×
940
                return;
941
        }
942

943
        test(m, "exec-systemcallfilter-system-user.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
×
944

945
        if (!check_nobody_user_and_group()) {
×
946
                log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
×
947
                return;
948
        }
949

950
        if (!STR_IN_SET(NOBODY_USER_NAME, "nobody", "nfsnobody")) {
×
951
                log_notice("Unsupported nobody user name '%s', skipping remaining tests in %s", NOBODY_USER_NAME, __func__);
×
952
                return;
×
953
        }
954

955
        test(m, "exec-systemcallfilter-system-user-" NOBODY_USER_NAME ".service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
×
956
#endif
957
}
958

959
static void test_exec_user(Manager *m) {
×
960
        test(m, "exec-user.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
×
961

962
        if (!check_nobody_user_and_group()) {
×
963
                log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
×
964
                return;
965
        }
966

967
        if (!STR_IN_SET(NOBODY_USER_NAME, "nobody", "nfsnobody")) {
×
968
                log_notice("Unsupported nobody user name '%s', skipping remaining tests in %s", NOBODY_USER_NAME, __func__);
×
969
                return;
×
970
        }
971

972
        test(m, "exec-user-" NOBODY_USER_NAME ".service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
×
973
}
974

975
static void test_exec_group(Manager *m) {
×
976
        test(m, "exec-group.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
×
977

978
        if (!check_nobody_user_and_group()) {
×
979
                log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
×
980
                return;
981
        }
982

983
        if (!STR_IN_SET(NOBODY_GROUP_NAME, "nobody", "nfsnobody", "nogroup")) {
×
984
                log_notice("Unsupported nobody group name '%s', skipping remaining tests in %s", NOBODY_GROUP_NAME, __func__);
×
985
                return;
×
986
        }
987

988
        test(m, "exec-group-" NOBODY_GROUP_NAME ".service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
×
989
}
990

991
static void test_exec_supplementarygroups(Manager *m) {
×
992
        int status = MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP;
×
993
        test(m, "exec-supplementarygroups.service", status, CLD_EXITED);
×
994
        test(m, "exec-supplementarygroups-single-group.service", status, CLD_EXITED);
×
995
        test(m, "exec-supplementarygroups-single-group-user.service", status, CLD_EXITED);
×
996
        test(m, "exec-supplementarygroups-multiple-groups-default-group-user.service", status, CLD_EXITED);
×
997
        test(m, "exec-supplementarygroups-multiple-groups-withgid.service", status, CLD_EXITED);
×
998
        test(m, "exec-supplementarygroups-multiple-groups-withuid.service", status, CLD_EXITED);
×
999
}
×
1000

1001
static char* private_directory_bad(Manager *m) {
×
1002
        /* This mirrors setup_exec_directory(). */
1003

1004
        for (ExecDirectoryType dt = 0; dt < _EXEC_DIRECTORY_TYPE_MAX; dt++) {
×
1005
                _cleanup_free_ char *p = NULL;
×
1006
                struct stat st;
×
1007

1008
                ASSERT_NOT_NULL((p = path_join(m->prefix[dt], "private")));
×
1009

1010
                if (stat(p, &st) >= 0 &&
×
1011
                    (st.st_mode & (S_IRWXG|S_IRWXO)))
×
1012
                        return TAKE_PTR(p);
×
1013
        }
1014

1015
        return NULL;
1016
}
1017

1018
static void test_exec_dynamicuser(Manager *m) {
×
1019
        if (MANAGER_IS_USER(m)) {
×
1020
                log_notice("Skipping %s for user manager", __func__);
×
1021
                return;
×
1022
        }
1023

1024
        _cleanup_free_ char *bad = private_directory_bad(m);
×
1025
        if (bad) {
×
1026
                log_warning("%s: %s has bad permissions, skipping test.", __func__, bad);
×
UNCOV
1027
                return;
×
1028
        }
1029

1030
        int status = can_unshare ? 0 : EXIT_NAMESPACE;
×
1031

UNCOV
1032
        test(m, "exec-dynamicuser-fixeduser.service", status, CLD_EXITED);
×
UNCOV
1033
        if (check_user_has_group_with_same_name("adm"))
×
UNCOV
1034
                test(m, "exec-dynamicuser-fixeduser-adm.service", status, CLD_EXITED);
×
1035
        if (check_user_has_group_with_same_name("games"))
×
UNCOV
1036
                test(m, "exec-dynamicuser-fixeduser-games.service", status, CLD_EXITED);
×
1037
        test(m, "exec-dynamicuser-fixeduser-one-supplementarygroup.service", status, CLD_EXITED);
×
1038
        test(m, "exec-dynamicuser-supplementarygroups.service", status, CLD_EXITED);
×
1039
        test(m, "exec-dynamicuser-statedir.service", status, CLD_EXITED);
×
1040

1041
        (void) rm_rf("/var/lib/quux", REMOVE_ROOT|REMOVE_PHYSICAL);
×
1042
        (void) rm_rf("/var/lib/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL);
×
1043
        (void) rm_rf("/var/lib/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL);
×
1044
        (void) rm_rf("/var/lib/waldo", REMOVE_ROOT|REMOVE_PHYSICAL);
×
UNCOV
1045
        (void) rm_rf("/var/lib/private/quux", REMOVE_ROOT|REMOVE_PHYSICAL);
×
1046
        (void) rm_rf("/var/lib/private/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL);
×
1047
        (void) rm_rf("/var/lib/private/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL);
×
1048
        (void) rm_rf("/var/lib/private/waldo", REMOVE_ROOT|REMOVE_PHYSICAL);
×
1049

1050
        test(m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED);
×
1051
        test(m, "exec-dynamicuser-statedir-migrate-step2.service", status, CLD_EXITED);
×
1052
        test(m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED);
×
1053

UNCOV
1054
        (void) rm_rf("/var/lib/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL);
×
1055
        (void) rm_rf("/var/lib/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL);
×
1056
        (void) rm_rf("/var/lib/private/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL);
×
1057
        (void) rm_rf("/var/lib/private/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL);
×
1058

1059
        test(m, "exec-dynamicuser-runtimedirectory1.service", status, CLD_EXITED);
×
1060
        test(m, "exec-dynamicuser-runtimedirectory2.service", status, CLD_EXITED);
×
1061
        test(m, "exec-dynamicuser-runtimedirectory3.service", status, CLD_EXITED);
×
1062
}
1063

1064
static void test_exec_environment(Manager *m) {
×
1065
        test(m, "exec-environment-no-substitute.service", 0, CLD_EXITED);
×
1066
        test(m, "exec-environment.service", 0, CLD_EXITED);
×
UNCOV
1067
        test(m, "exec-environment-multiple.service", 0, CLD_EXITED);
×
UNCOV
1068
        test(m, "exec-environment-empty.service", 0, CLD_EXITED);
×
1069
}
×
1070

1071
static void test_exec_environmentfile(Manager *m) {
×
1072
        static const char e[] =
×
1073
                "VAR1='word1 word2'\n"
1074
                "VAR2=word3 \n"
1075
                "# comment1\n"
1076
                "\n"
1077
                "; comment2\n"
1078
                " ; # comment3\n"
1079
                "line without an equal\n"
1080
                "VAR3='$word 5 6'\n"
1081
                "VAR4='new\nline'\n"
1082
                "VAR5=password\\with\\backslashes";
UNCOV
1083
        int r;
×
1084

UNCOV
1085
        r = write_string_file("/tmp/test-exec_environmentfile.conf", e, WRITE_STRING_FILE_CREATE);
×
UNCOV
1086
        ASSERT_OK(r);
×
1087

1088
        test(m, "exec-environmentfile.service", 0, CLD_EXITED);
×
1089

1090
        (void) unlink("/tmp/test-exec_environmentfile.conf");
×
1091
}
×
1092

1093
static void test_exec_passenvironment(Manager *m) {
×
1094
        /* test-execute runs under MANAGER_USER which, by default, forwards all
1095
         * variables present in the environment, but only those that are
1096
         * present _at the time it is created_!
1097
         *
1098
         * So these PassEnvironment checks are still expected to work, since we
1099
         * are ensuring the variables are not present at manager creation (they
1100
         * are unset explicitly in main) and are only set here.
1101
         *
1102
         * This is still a good approximation of how a test for MANAGER_SYSTEM
1103
         * would work.
1104
         */
UNCOV
1105
        ASSERT_OK_ERRNO(setenv("VAR1", "word1 word2", 1));
×
UNCOV
1106
        ASSERT_OK_ERRNO(setenv("VAR2", "word3", 1));
×
UNCOV
1107
        ASSERT_OK_ERRNO(setenv("VAR3", "$word 5 6", 1));
×
UNCOV
1108
        ASSERT_OK_ERRNO(setenv("VAR4", "new\nline", 1));
×
UNCOV
1109
        ASSERT_OK_ERRNO(setenv("VAR5", "passwordwithbackslashes", 1));
×
1110
        test(m, "exec-passenvironment.service", 0, CLD_EXITED);
×
1111
        test(m, "exec-passenvironment-repeated.service", 0, CLD_EXITED);
×
1112
        test(m, "exec-passenvironment-empty.service", 0, CLD_EXITED);
×
1113
        ASSERT_OK_ERRNO(unsetenv("VAR1"));
×
1114
        ASSERT_OK_ERRNO(unsetenv("VAR2"));
×
1115
        ASSERT_OK_ERRNO(unsetenv("VAR3"));
×
1116
        ASSERT_OK_ERRNO(unsetenv("VAR4"));
×
1117
        ASSERT_OK_ERRNO(unsetenv("VAR5"));
×
1118
        test(m, "exec-passenvironment-absent.service", 0, CLD_EXITED);
×
1119
}
×
1120

1121
static void test_exec_umask(Manager *m) {
×
1122
        if (MANAGER_IS_SYSTEM(m) || have_userns_privileges()) {
×
1123
                test(m, "exec-umask-default.service", can_unshare || MANAGER_IS_SYSTEM(m) ? 0 : EXIT_NAMESPACE, CLD_EXITED);
×
1124
                test(m, "exec-umask-0177.service", can_unshare || MANAGER_IS_SYSTEM(m) ? 0 : EXIT_NAMESPACE, CLD_EXITED);
×
1125
        } else
1126
                return (void)log_notice("Skipping %s, do not have user namespace privileges", __func__);
×
1127
}
1128

1129
static void test_exec_runtimedirectory(Manager *m) {
×
UNCOV
1130
        (void) rm_rf("/run/test-exec_runtimedirectory2", REMOVE_ROOT|REMOVE_PHYSICAL);
×
1131
        test(m, "exec-runtimedirectory.service", 0, CLD_EXITED);
×
UNCOV
1132
        (void) rm_rf("/run/test-exec_runtimedirectory2", REMOVE_ROOT|REMOVE_PHYSICAL);
×
1133

1134
        test(m, "exec-runtimedirectory-mode.service", 0, CLD_EXITED);
×
1135
        test(m, "exec-runtimedirectory-owner.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
×
1136

1137
        if (!check_nobody_user_and_group()) {
×
UNCOV
1138
                log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
×
1139
                return;
1140
        }
1141

1142
        if (!STR_IN_SET(NOBODY_GROUP_NAME, "nobody", "nfsnobody", "nogroup")) {
×
1143
                log_notice("Unsupported nobody group name '%s', skipping remaining tests in %s", NOBODY_GROUP_NAME, __func__);
×
UNCOV
1144
                return;
×
1145
        }
1146

1147
        test(m, "exec-runtimedirectory-owner-" NOBODY_GROUP_NAME ".service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_GROUP, CLD_EXITED);
×
1148
}
1149

UNCOV
1150
static void test_exec_capabilityboundingset(Manager *m) {
×
UNCOV
1151
        int r;
×
1152

UNCOV
1153
        r = find_executable("capsh", NULL);
×
UNCOV
1154
        if (r < 0) {
×
1155
                log_notice_errno(r, "Skipping %s, could not find capsh binary: %m", __func__);
×
1156
                return;
1157
        }
1158

1159
        if (have_effective_cap(CAP_CHOWN) <= 0 ||
×
1160
            have_effective_cap(CAP_FOWNER) <= 0 ||
×
UNCOV
1161
            have_effective_cap(CAP_KILL) <= 0) {
×
UNCOV
1162
                log_notice("Skipping %s, this process does not have enough capabilities", __func__);
×
1163
                return;
1164
        }
1165

1166
        test(m, "exec-capabilityboundingset-simple.service", 0, CLD_EXITED);
×
1167
        test(m, "exec-capabilityboundingset-reset.service", 0, CLD_EXITED);
×
UNCOV
1168
        test(m, "exec-capabilityboundingset-merge.service", 0, CLD_EXITED);
×
UNCOV
1169
        test(m, "exec-capabilityboundingset-invert.service", 0, CLD_EXITED);
×
1170
}
1171

1172
static void test_exec_basic(Manager *m) {
×
1173
        if (isempty(gnu_get_libc_version()))
×
1174
                return (void) log_tests_skipped("ConditionVersion=glibc will not pass under musl");
×
1175

UNCOV
1176
        if (MANAGER_IS_SYSTEM(m) || have_userns_privileges())
×
1177
                test(m, "exec-basic.service", can_unshare || MANAGER_IS_SYSTEM(m) ? 0 : EXIT_NAMESPACE, CLD_EXITED);
×
1178
        else
1179
                return (void)log_notice("Skipping %s, do not have user namespace privileges", __func__);
×
1180
}
1181

1182
static void test_exec_ambientcapabilities(Manager *m) {
×
UNCOV
1183
        int r;
×
1184

1185
        /* Check if the kernel has support for ambient capabilities. Run
1186
         * the tests only if that's the case. Clearing all ambient
1187
         * capabilities is fine, since we are expecting them to be unset
1188
         * in the first place for the tests. */
UNCOV
1189
        r = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
×
UNCOV
1190
        if (r < 0 && IN_SET(errno, EINVAL, EOPNOTSUPP, ENOSYS)) {
×
UNCOV
1191
                log_notice("Skipping %s, the kernel does not support ambient capabilities", __func__);
×
1192
                return;
1193
        }
1194

1195
        if (have_effective_cap(CAP_CHOWN) <= 0 ||
×
1196
            have_effective_cap(CAP_NET_RAW) <= 0) {
×
UNCOV
1197
                log_notice("Skipping %s, this process does not have enough capabilities", __func__);
×
1198
                return;
1199
        }
1200

1201
        test(m, "exec-ambientcapabilities.service", 0, CLD_EXITED);
×
1202
        test(m, "exec-ambientcapabilities-merge.service", 0, CLD_EXITED);
×
1203

UNCOV
1204
        if (have_effective_cap(CAP_SETUID) > 0)
×
UNCOV
1205
                test(m, "exec-ambientcapabilities-dynuser.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED);
×
1206

1207
        if (!check_nobody_user_and_group()) {
×
UNCOV
1208
                log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__);
×
1209
                return;
1210
        }
1211

1212
        if (!STR_IN_SET(NOBODY_USER_NAME, "nobody", "nfsnobody")) {
×
1213
                log_notice("Unsupported nobody user name '%s', skipping remaining tests in %s", NOBODY_USER_NAME, __func__);
×
UNCOV
1214
                return;
×
1215
        }
1216

1217
        test(m, "exec-ambientcapabilities-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
×
1218
        test(m, "exec-ambientcapabilities-merge-" NOBODY_USER_NAME ".service", 0, CLD_EXITED);
×
1219
}
1220

UNCOV
1221
static void test_exec_privatenetwork(Manager *m) {
×
1222
        int r;
×
1223

UNCOV
1224
        if (!have_net_dummy)
×
UNCOV
1225
                return (void)log_notice("Skipping %s, dummy network interface not available", __func__);
×
1226

1227
        if (MANAGER_IS_USER(m) && !have_userns_privileges())
×
UNCOV
1228
                return (void)log_notice("Skipping %s, do not have user namespace privileges", __func__);
×
1229

1230
        r = find_executable("ip", NULL);
×
UNCOV
1231
        if (r < 0) {
×
1232
                log_notice_errno(r, "Skipping %s, could not find ip binary: %m", __func__);
×
1233
                return;
1234
        }
1235

1236
        test(m, "exec-privatenetwork-yes-privatemounts-no.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_NETWORK : EXIT_FAILURE, CLD_EXITED);
×
1237
        test(m, "exec-privatenetwork-yes-privatemounts-yes.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_NETWORK : EXIT_NAMESPACE, CLD_EXITED);
×
1238
}
1239

UNCOV
1240
static void test_exec_networknamespacepath(Manager *m) {
×
1241
        int r;
×
1242

UNCOV
1243
        if (!have_net_dummy)
×
UNCOV
1244
                return (void)log_notice("Skipping %s, dummy network interface not available", __func__);
×
1245

1246
        if (!have_netns)
×
UNCOV
1247
                return (void)log_notice("Skipping %s, network namespace not available", __func__);
×
1248

1249
        if (MANAGER_IS_USER(m) && !have_userns_privileges())
×
UNCOV
1250
                return (void)log_notice("Skipping %s, do not have user namespace privileges", __func__);
×
1251

1252
        r = find_executable("ip", NULL);
×
UNCOV
1253
        if (r < 0) {
×
1254
                log_notice_errno(r, "Skipping %s, could not find ip binary: %m", __func__);
×
1255
                return;
1256
        }
1257

1258
        test(m, "exec-networknamespacepath-privatemounts-no.service", MANAGER_IS_SYSTEM(m) ? EXIT_SUCCESS : EXIT_FAILURE, CLD_EXITED);
×
1259
        test(m, "exec-networknamespacepath-privatemounts-yes.service", can_unshare ? EXIT_SUCCESS : MANAGER_IS_SYSTEM(m) ? EXIT_FAILURE : EXIT_NAMESPACE, CLD_EXITED);
×
1260
}
1261

UNCOV
1262
static void test_exec_oomscoreadjust(Manager *m) {
×
1263
        test(m, "exec-oomscoreadjust-positive.service", 0, CLD_EXITED);
×
1264

UNCOV
1265
        if (detect_container() > 0) {
×
UNCOV
1266
                log_notice("Testing in container, skipping remaining tests in %s", __func__);
×
1267
                return;
1268
        }
UNCOV
1269
        test(m, "exec-oomscoreadjust-negative.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_FAILURE, CLD_EXITED);
×
1270
}
1271

UNCOV
1272
static void test_exec_ioschedulingclass(Manager *m) {
×
UNCOV
1273
        test(m, "exec-ioschedulingclass-none.service", 0, CLD_EXITED);
×
1274
        test(m, "exec-ioschedulingclass-idle.service", 0, CLD_EXITED);
×
UNCOV
1275
        test(m, "exec-ioschedulingclass-best-effort.service", 0, CLD_EXITED);
×
1276

1277
        if (detect_container() > 0) {
×
1278
                log_notice("Testing in container, skipping remaining tests in %s", __func__);
×
1279
                return;
1280
        }
UNCOV
1281
        test(m, "exec-ioschedulingclass-realtime.service", MANAGER_IS_SYSTEM(m) ? 0 : EXIT_IOPRIO, CLD_EXITED);
×
1282
}
1283

UNCOV
1284
static void test_exec_unsetenvironment(Manager *m) {
×
UNCOV
1285
        test(m, "exec-unsetenvironment.service", 0, CLD_EXITED);
×
1286
}
×
1287

UNCOV
1288
static void test_exec_specifier(Manager *m) {
×
1289
        test(m, "exec-specifier.service", 0, CLD_EXITED);
×
1290
        if (MANAGER_IS_SYSTEM(m))
×
1291
                test(m, "exec-specifier-system.service", 0, CLD_EXITED);
×
1292
        else
1293
                test(m, "exec-specifier-user.service", 0, CLD_EXITED);
×
1294
        test(m, "exec-specifier@foo-bar.service", 0, CLD_EXITED);
×
1295
        test(m, "exec-specifier-interpolation.service", 0, CLD_EXITED);
×
1296
}
×
1297

1298
static void test_exec_standardinput(Manager *m) {
×
1299
        test(m, "exec-standardinput-data.service", 0, CLD_EXITED);
×
1300
        test(m, "exec-standardinput-file.service", 0, CLD_EXITED);
×
1301

UNCOV
1302
        ExecOutput saved = m->defaults.std_output;
×
1303
        m->defaults.std_output = EXEC_OUTPUT_NULL;
×
1304
        test(m, "exec-standardinput-file-cat.service", 0, CLD_EXITED);
×
1305
        m->defaults.std_output = saved;
×
UNCOV
1306
}
×
1307

1308
static void test_exec_standardoutput(Manager *m) {
×
1309
        test(m, "exec-standardoutput-file.service", 0, CLD_EXITED);
×
1310
}
×
1311

UNCOV
1312
static void test_exec_standardoutput_append(Manager *m) {
×
1313
        test(m, "exec-standardoutput-append.service", 0, CLD_EXITED);
×
1314
}
×
1315

UNCOV
1316
static void test_exec_standardoutput_truncate(Manager *m) {
×
1317
        test(m, "exec-standardoutput-truncate.service", 0, CLD_EXITED);
×
1318
}
×
1319

UNCOV
1320
static void test_exec_condition(Manager *m) {
×
1321
        test_service(m, "exec-condition-failed.service", SERVICE_FAILURE_EXIT_CODE);
×
1322
        test_service(m, "exec-condition-skip.service", SERVICE_SKIP_CONDITION);
×
1323
}
×
1324

1325
static void test_exec_umask_namespace(Manager *m) {
×
1326
        /* exec-specifier-credentials-dir.service creates /run/credentials and enables implicit
1327
         * InaccessiblePath= for the directory for all later services with mount namespace. */
1328
        if (!is_inaccessible_available()) {
×
UNCOV
1329
                log_notice("Testing without inaccessible, skipping %s", __func__);
×
1330
                return;
1331
        }
UNCOV
1332
        test(m, "exec-umask-namespace.service", can_unshare ? 0 : MANAGER_IS_SYSTEM(m) ? EXIT_NAMESPACE : EXIT_GROUP, CLD_EXITED);
×
1333
}
1334

1335
typedef struct test_entry {
1336
        test_function_t f;
1337
        const char *name;
1338
} test_entry;
1339

1340
#define entry(x) {x, #x}
1341

UNCOV
1342
static void run_tests(RuntimeScope scope, char **patterns) {
×
UNCOV
1343
        _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
×
UNCOV
1344
        _cleanup_free_ char *unit_paths = NULL;
×
UNCOV
1345
        _cleanup_(manager_freep) Manager *m = NULL;
×
UNCOV
1346
        usec_t start, finish;
×
1347
        int r;
×
1348

1349
        static const test_entry tests[] = {
×
1350
                entry(test_exec_basic),
1351
                entry(test_exec_ambientcapabilities),
1352
                entry(test_exec_bindpaths),
1353
                entry(test_exec_capabilityboundingset),
1354
                entry(test_exec_condition),
1355
                entry(test_exec_cpuaffinity),
1356
                entry(test_exec_credentials),
1357
                entry(test_exec_dynamicuser),
1358
                entry(test_exec_environment),
1359
                entry(test_exec_environmentfile),
1360
                entry(test_exec_execsearchpath),
1361
                entry(test_exec_execsearchpath_environment),
1362
                entry(test_exec_execsearchpath_environment_files),
1363
                entry(test_exec_execsearchpath_passenvironment),
1364
                entry(test_exec_execsearchpath_specifier),
1365
                entry(test_exec_group),
1366
                entry(test_exec_ignoresigpipe),
1367
                entry(test_exec_inaccessiblepaths),
1368
                entry(test_exec_ioschedulingclass),
1369
                entry(test_exec_mount_apivfs),
1370
                entry(test_exec_networknamespacepath),
1371
                entry(test_exec_noexecpaths),
1372
                entry(test_exec_oomscoreadjust),
1373
                entry(test_exec_passenvironment),
1374
                entry(test_exec_personality),
1375
                entry(test_exec_privatedevices),
1376
                entry(test_exec_privatenetwork),
1377
                entry(test_exec_privatetmp),
1378
                entry(test_exec_protecthome),
1379
                entry(test_exec_protectkernelmodules),
1380
                entry(test_exec_readonlypaths),
1381
                entry(test_exec_readwritepaths),
1382
                entry(test_exec_restrictnamespaces),
1383
                entry(test_exec_runtimedirectory),
1384
                entry(test_exec_specifier),
1385
                entry(test_exec_standardinput),
1386
                entry(test_exec_standardoutput),
1387
                entry(test_exec_standardoutput_append),
1388
                entry(test_exec_standardoutput_truncate),
1389
                entry(test_exec_supplementarygroups),
1390
                entry(test_exec_systemcallerrornumber),
1391
                entry(test_exec_systemcallfilter),
1392
                entry(test_exec_systemcallfilter_system),
1393
                entry(test_exec_temporaryfilesystem),
1394
                entry(test_exec_umask),
1395
                entry(test_exec_umask_namespace),
1396
                entry(test_exec_unsetenvironment),
1397
                entry(test_exec_user),
1398
                entry(test_exec_workingdirectory),
1399
                {},
1400
        };
1401

UNCOV
1402
        ASSERT_OK_ERRNO(unsetenv("USER"));
×
UNCOV
1403
        ASSERT_OK_ERRNO(unsetenv("LOGNAME"));
×
UNCOV
1404
        ASSERT_OK_ERRNO(unsetenv("SHELL"));
×
UNCOV
1405
        ASSERT_OK_ERRNO(unsetenv("HOME"));
×
UNCOV
1406
        ASSERT_OK_ERRNO(unsetenv("TMPDIR"));
×
1407

1408
        /* Unset VARx, especially, VAR1, VAR2 and VAR3, which are used in the PassEnvironment test cases,
1409
         * otherwise (and if they are present in the environment), `manager_default_environment` will copy
1410
         * them into the default environment which is passed to each created job, which will make the tests
1411
         * that expect those not to be present to fail. */
UNCOV
1412
        ASSERT_OK_ERRNO(unsetenv("VAR1"));
×
UNCOV
1413
        ASSERT_OK_ERRNO(unsetenv("VAR2"));
×
UNCOV
1414
        ASSERT_OK_ERRNO(unsetenv("VAR3"));
×
UNCOV
1415
        ASSERT_OK_ERRNO(unsetenv("VAR4"));
×
UNCOV
1416
        ASSERT_OK_ERRNO(unsetenv("VAR5"));
×
1417

1418
        ASSERT_NOT_NULL((runtime_dir = setup_fake_runtime_dir()));
×
1419
        ASSERT_NOT_NULL((user_runtime_unit_dir = path_join(runtime_dir, "systemd/user")));
×
1420
        ASSERT_NOT_NULL((unit_paths = strjoin(PRIVATE_UNIT_DIR, ":", user_runtime_unit_dir)));
×
1421
        ASSERT_OK(setenv_unit_path(unit_paths));
×
1422

1423
        /* Write credential for test-execute-load-credential to the fake runtime dir, too */
1424
        _cleanup_free_ char *j = ASSERT_PTR(path_join(runtime_dir, "credstore/test-execute.load-credential"));
×
1425
        ASSERT_OK(write_string_file(j, "foo", WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_MKDIR_0755));
×
1426

UNCOV
1427
        r = manager_new(scope, MANAGER_TEST_RUN_BASIC, &m);
×
UNCOV
1428
        if (manager_errno_skip_test(r))
×
1429
                return (void) log_tests_skipped_errno(r, "manager_new");
×
1430
        ASSERT_OK(r);
×
1431

1432
        m->defaults.std_output = EXEC_OUTPUT_INHERIT; /* don't rely on host journald */
×
1433
        ASSERT_OK(manager_startup(m, NULL, NULL, NULL, NULL));
×
1434

1435
        /* Uncomment below if you want to make debugging logs stored to journal. */
1436
        //manager_override_log_target(m, LOG_TARGET_AUTO);
1437
        //manager_override_log_level(m, LOG_DEBUG);
1438

1439
        /* Measure and print the time that it takes to run tests, excluding startup of the manager object,
1440
         * to try and measure latency of spawning services */
UNCOV
1441
        n_ran_tests = 0;
×
UNCOV
1442
        start = now(CLOCK_MONOTONIC);
×
1443

UNCOV
1444
        for (const test_entry *test = tests; test->f; test++)
×
UNCOV
1445
                if (strv_fnmatch_or_empty(patterns, test->name, FNM_NOESCAPE)) {
×
1446
                        log_info("Starting %s.", test->name);
×
1447
                        test->f(m);
×
1448
                } else
1449
                        log_info("Skipping %s because it does not match any pattern.", test->name);
×
1450

1451
        finish = now(CLOCK_MONOTONIC);
×
1452

UNCOV
1453
        log_info("ran %u tests with %s manager + unshare=%s in: %s",
×
1454
                 n_ran_tests,
1455
                 scope == RUNTIME_SCOPE_SYSTEM ? "system" : "user",
1456
                 yes_no(can_unshare),
1457
                 FORMAT_TIMESPAN(finish - start, USEC_PER_MSEC));
1458
}
1459

UNCOV
1460
static int prepare_ns(const char *process_name) {
×
UNCOV
1461
        int r;
×
1462

UNCOV
1463
        r = pidref_safe_fork(
×
1464
                        process_name,
1465
                        FORK_RESET_SIGNALS|
1466
                        FORK_CLOSE_ALL_FDS|
1467
                        FORK_DEATHSIG_SIGTERM|
1468
                        FORK_WAIT|
1469
                        FORK_REOPEN_LOG|
1470
                        FORK_LOG|
1471
                        FORK_NEW_MOUNTNS|
1472
                        FORK_MOUNTNS_SLAVE,
1473
                        NULL);
UNCOV
1474
        ASSERT_OK(r);
×
UNCOV
1475
        if (r == 0) {
×
UNCOV
1476
                _cleanup_free_ char *unit_dir = NULL, *build_dir = NULL, *build_dir_mount = NULL;
×
1477

UNCOV
1478
                const char *coverage = getenv("COVERAGE_BUILD_DIR");
×
1479
                if (!coverage)
×
1480
                        /* Make "/" read-only. */
1481
                        ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, NULL, "/", NULL, MS_BIND|MS_REMOUNT|MS_RDONLY, NULL));
×
1482

1483
                /* Creating a new user namespace in the above means all MS_SHARED mounts become MS_SLAVE.
1484
                 * Let's put them back to MS_SHARED here, since that's what we want as defaults. (This will
1485
                 * not reconnect propagation, but simply create new peer groups for all our mounts). */
1486
                ASSERT_OK(mount_follow_verbose(LOG_DEBUG, NULL, "/", NULL, MS_SHARED|MS_REC, NULL));
×
1487

UNCOV
1488
                ASSERT_OK(mkdir_p(PRIVATE_UNIT_DIR, 0755));
×
UNCOV
1489
                ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", PRIVATE_UNIT_DIR, "tmpfs", MS_NOSUID|MS_NODEV, NULL));
×
1490
                /* Mark our test "playground" as MS_SLAVE, so we can MS_MOVE mounts underneath it. */
1491
                ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, NULL, PRIVATE_UNIT_DIR, NULL, MS_SLAVE, NULL));
×
1492

1493
                /* Copy unit files to make them accessible even when unprivileged. */
1494
                ASSERT_OK(get_testdata_dir("test-execute/", &unit_dir));
×
UNCOV
1495
                ASSERT_OK(copy_directory_at(AT_FDCWD, unit_dir, AT_FDCWD, PRIVATE_UNIT_DIR, UID_INVALID, GID_INVALID, COPY_MERGE_EMPTY));
×
1496

1497
                /* Mount tmpfs on the following directories to make not StateDirectory= or friends disturb the host. */
UNCOV
1498
                ASSERT_OK_OR(get_build_exec_dir(&build_dir), -ENOEXEC);
×
1499

1500
                if (build_dir) {
×
1501
                        /* Account for a build directory being in one of the soon-to-be-tmpfs directories. If we
1502
                         * overmount it with an empty tmpfs, manager_new() will pin the wrong systemd-executor binary,
1503
                         * which can then lead to unexpected (and painful to debug) test fails. */
UNCOV
1504
                        ASSERT_OK_ERRNO(access(build_dir, F_OK));
×
1505
                        ASSERT_NOT_NULL((build_dir_mount = path_join(PRIVATE_UNIT_DIR, "build_dir")));
×
UNCOV
1506
                        ASSERT_OK(mkdir_p(build_dir_mount, 0755));
×
UNCOV
1507
                        ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, build_dir, build_dir_mount, NULL, MS_BIND, NULL));
×
1508
                }
1509

1510
                FOREACH_STRING(p, "/dev/shm", "/root", "/tmp", "/var/tmp", "/var/lib")
×
1511
                        ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", p, "tmpfs", MS_NOSUID|MS_NODEV, NULL));
×
1512

UNCOV
1513
                if (build_dir_mount) {
×
UNCOV
1514
                        int k;
×
1515

1516
                        ASSERT_OK_OR(k = RET_NERRNO(access(build_dir, F_OK)), -ENOENT);
×
1517

1518
                        if (k == -ENOENT) {
×
1519
                                /* The build directory got overmounted by tmpfs, so let's use the "backup" bind mount to
1520
                                 * bring it back. */
1521
                                ASSERT_OK(mkdir_p(build_dir, 0755));
×
UNCOV
1522
                                ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, build_dir_mount, build_dir, NULL, MS_MOVE, NULL));
×
1523
                        }
1524
                }
1525

1526
                /* Prepare credstore like tmpfiles.d/credstore.conf for LoadCredential= tests. */
1527
                FOREACH_STRING(p, "/run/credstore", "/run/credstore.encrypted") {
×
UNCOV
1528
                        ASSERT_OK(mkdir_p(p, 0700));
×
UNCOV
1529
                        ASSERT_OK(mount_nofollow_verbose(LOG_DEBUG, "tmpfs", p, "tmpfs", MS_NOSUID|MS_NODEV, "mode=0700"));
×
1530
                }
1531

1532
                ASSERT_OK(write_string_file("/run/credstore/test-execute.load-credential", "foo", WRITE_STRING_FILE_CREATE));
×
1533
        }
1534

UNCOV
1535
        return r;
×
1536
}
1537

UNCOV
1538
TEST(run_tests_root) {
×
UNCOV
1539
        _cleanup_strv_free_ char **filters = NULL;
×
1540

UNCOV
1541
        if (!have_namespaces())
×
UNCOV
1542
                return (void) log_tests_skipped("unshare() is disabled");
×
1543

1544
        /* safe_fork() clears saved_argv in the child process. Let's copy it. */
UNCOV
1545
        ASSERT_NOT_NULL((filters = strv_copy(strv_skip(saved_argv, 1))));
×
1546

1547
        if (prepare_ns("(test-execute-root)") == 0) {
×
UNCOV
1548
                can_unshare = true;
×
UNCOV
1549
                run_tests(RUNTIME_SCOPE_SYSTEM, filters);
×
1550
                _exit(EXIT_SUCCESS);
×
1551
        }
1552
}
1553

1554
TEST(run_tests_without_unshare) {
×
1555
        if (!have_namespaces()) {
×
1556
                /* unshare() is already filtered. */
UNCOV
1557
                can_unshare = false;
×
UNCOV
1558
                run_tests(RUNTIME_SCOPE_SYSTEM, strv_skip(saved_argv, 1));
×
1559
                return;
×
1560
        }
1561

1562
#if HAVE_SECCOMP
1563
        _cleanup_strv_free_ char **filters = NULL;
×
1564
        int r;
×
1565

1566
        /* The following tests are for 1beab8b0d0ff2d7d1436b52d4a0c3d56dc908962. */
UNCOV
1567
        if (!is_seccomp_available())
×
1568
                return (void) log_tests_skipped("Seccomp not available, cannot run unshare() filtered tests");
×
1569

1570
        /* safe_fork() clears saved_argv in the child process. Let's copy it. */
UNCOV
1571
        ASSERT_NOT_NULL((filters = strv_copy(strv_skip(saved_argv, 1))));
×
1572

1573
        if (prepare_ns("(test-execute-without-unshare)") == 0) {
×
UNCOV
1574
                _cleanup_hashmap_free_ Hashmap *s = NULL;
×
1575

1576
                r = sym_seccomp_syscall_resolve_name("unshare");
×
UNCOV
1577
                ASSERT_NE(r, __NR_SCMP_ERROR);
×
1578
                ASSERT_OK(hashmap_ensure_put(&s, NULL, UINT32_TO_PTR(r + 1), INT_TO_PTR(-1)));
×
1579
                ASSERT_OK(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EOPNOTSUPP), true));
×
1580

1581
                /* Check unshare() is actually filtered. */
1582
                ASSERT_ERROR_ERRNO(unshare(CLONE_NEWNS), EOPNOTSUPP);
×
1583

1584
                can_unshare = false;
×
UNCOV
1585
                run_tests(RUNTIME_SCOPE_SYSTEM, filters);
×
UNCOV
1586
                _exit(EXIT_SUCCESS);
×
1587
        }
1588
#else
1589
        log_tests_skipped("Built without seccomp support, cannot run unshare() filtered tests");
1590
#endif
1591
}
1592

UNCOV
1593
TEST(run_tests_unprivileged) {
×
UNCOV
1594
        _cleanup_strv_free_ char **filters = NULL;
×
1595

UNCOV
1596
        if (!have_namespaces())
×
UNCOV
1597
                return (void) log_tests_skipped("unshare() is disabled");
×
1598

1599
        /* safe_fork() clears saved_argv in the child process. Let's copy it. */
UNCOV
1600
        ASSERT_NOT_NULL((filters = strv_copy(strv_skip(saved_argv, 1))));
×
1601

1602
        if (prepare_ns("(test-execute-unprivileged)") == 0) {
×
UNCOV
1603
                ASSERT_OK(capability_bounding_set_drop(0, /* right_now= */ true));
×
1604

1605
                can_unshare = false;
×
UNCOV
1606
                run_tests(RUNTIME_SCOPE_USER, filters);
×
1607
                _exit(EXIT_SUCCESS);
×
1608
        }
1609
}
1610

1611
static int intro(void) {
1✔
1612
        int r;
1✔
1613

1614
        /* It is needed otherwise cgroup creation fails */
1615
        if (geteuid() != 0 || have_effective_cap(CAP_SYS_ADMIN) <= 0)
1✔
UNCOV
1616
                return log_tests_skipped("not privileged");
×
1617

1618
        if (running_in_chroot() != 0)
1✔
UNCOV
1619
                return log_tests_skipped("running in chroot");
×
1620

1621
        if (enter_cgroup_subroot(NULL) == -ENOMEDIUM)
1✔
UNCOV
1622
                return log_tests_skipped("cgroupfs not available");
×
1623

1624
        if (path_is_read_only_fs("/sys") > 0)
1✔
1625
                return log_tests_skipped("/sys is mounted read-only");
1✔
1626

UNCOV
1627
        r = dlopen_libmount(LOG_DEBUG);
×
1628
        if (r < 0)
×
UNCOV
1629
                return log_tests_skipped("libmount not available.");
×
1630

1631
        /* Create dummy network interface for testing PrivateNetwork=yes */
UNCOV
1632
        have_net_dummy = system("ip link add dummy-test-exec type dummy") == 0;
×
1633

UNCOV
1634
        if (have_net_dummy) {
×
1635
                /* Create a network namespace and a dummy interface in it for NetworkNamespacePath= */
1636
                have_netns = system("ip netns add test-execute-netns") == 0;
×
1637
                have_netns = have_netns && system("ip netns exec test-execute-netns ip link add dummy-test-ns type dummy") == 0;
×
1638
        }
1639

1640
        return EXIT_SUCCESS;
1641
}
1642

1643
static int outro(void) {
1✔
1644
        if (have_net_dummy) {
1✔
1645
                (void) system("ip link del dummy-test-exec");
×
1646
                (void) system("ip netns del test-execute-netns");
×
1647
        }
1648

1649
        (void) rmdir(PRIVATE_UNIT_DIR);
1✔
1650

1651
        return EXIT_SUCCESS;
1✔
1652
}
1653

1654
DEFINE_TEST_MAIN_FULL(LOG_DEBUG, intro, outro);
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc