From 03e93e27d98f32267393ff70fecb82d63d77f489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Sun, 29 Jan 2017 17:22:41 -0500 Subject: [PATCH] A number of backported patches, incl. seccomp fix --- ...est-seccomp-against-seccomp-libs-456.patch | 5 +- ...tall-use-exit-instead-of-return-4565.patch | 5 +- ...-exit-code-for-help-and-version-4609.patch | 33 + ...he-unified-hierarchy-for-the-systemd.patch | 40 + ...rectory-and-ProtectKernelModules-wor.patch | 335 +++ ...one-strdup-by-using-free_and_replace.patch | 37 + 0007-nspawn-slight-simplification.patch | 63 + ...ount-and-free-failed-paths-inside-ch.patch | 122 + ...serns-check-on-CONFIG_USER_NS-n-kern.patch | 32 + ...ADJ_MAXERROR-to-keep-STA_UNSYNC-clea.patch | 44 + ...offload-features-initialization-4639.patch | 26 + ...ix-sd_event_source_get_priority-4712.patch | 51 + ...for-lz4-in-the-old-and-new-numbering.patch | 29 +- ...e-of-MTUBytes-so-that-it-does-not-ov.patch | 31 + ...GTERM-as-a-clean-exit-status-for-ini.patch | 50 + ...nitrd-switch-root-command-survives-P.patch | 30 +- ...rtup-problem-when-code-is-compiled-w.patch | 33 + ...ling-unit_free-NULL-in-device-setup-.patch | 36 + ...k-correct-return-value-of-fcntl-4758.patch | 27 + ...-enabled-exit-status-on-failure-when.patch | 27 + ...e-to-initially-populate-the-space-in.patch | 33 + ..._enter_configured-remove-assert-4800.patch | 50 + ...MC-device-partitions-with-partition-.patch | 28 + ...wn-add-missing-E-to-getopt_long-4860.patch | 26 + ...-arm-as-secondary-architecture-for-a.patch | 27 + ...ing-SIGCHLD-make-sure-it-s-from-the-.patch | 78 + ...hinectl-make-machinectl-E-shell-work.patch | 31 + ...r-properly-translate-sysv-facilities.patch | 57 + ...-Time-has-been-changed-to-debug-4906.patch | 43 + ...e-EOPNOTSUPP-from-print_addresses-49.patch | 28 + ...ion-for-systemd-journal-catalog-upda.patch | 30 + ...r-length-calculation-for-sockaddr_pr.patch | 60 + ...hared-fix-double-free-in-unmask-5005.patch | 43 + 0034-shared-fix-double-free-in-link.patch | 120 + 0035-shared-check-strdup-NULL.patch | 32 + ...nothing-if-systemd-is-not-running-50.patch | 60 + ...l-install-avoid-process-substitution.patch | 4 +- ...-redirect-all-errors-from-systemctl-.patch | 44 + ...-cryptsetup-fix-unitialized-variable.patch | 29 + 0040-systemctl-uninitalized-variable.patch | 31 + ...etion-journalctl-add-missing-options.patch | 32 + ...-journalctl-Complete-t-option-values.patch | 29 + ...-Fixi-caching-in-zsh-completion-5122.patch | 77 + ...-completion-add-support-for-now-5155.patch | 26 + 0045-core-dbus-fix-two-strv-memleaks.patch | 38 + 0046-core-execute-fix-strv-memleak.patch | 40 + 0047-resolve-fix-strv-memleak.patch | 28 + ...ix-clobbering-of-selinux-context-arg.patch | 30 + ...db-fix-to-work-with-pyparsing-2.1.10.patch | 32 + ...lush-to-var-log-journal-before-we-ge.patch | 136 ++ ...wide-drop-NULL-sentinel-from-strjoin.patch | 2121 +++++++++++++++++ ...RestrictNamespaces-unit-file-setting.patch | 872 +++++++ ...eccomp-code-to-improve-compat-with-s.patch | 2021 ++++++++++++++++ systemd.spec | 68 +- 54 files changed, 7428 insertions(+), 32 deletions(-) create mode 100644 0003-nspawn-fix-exit-code-for-help-and-version-4609.patch create mode 100644 0004-core-don-t-use-the-unified-hierarchy-for-the-systemd.patch create mode 100644 0005-core-make-RootDirectory-and-ProtectKernelModules-wor.patch create mode 100644 0006-nspawn-avoid-one-strdup-by-using-free_and_replace.patch create mode 100644 0007-nspawn-slight-simplification.patch create mode 100644 0008-core-namespace-count-and-free-failed-paths-inside-ch.patch create mode 100644 0009-basic-virt-fix-userns-check-on-CONFIG_USER_NS-n-kern.patch create mode 100644 0010-timesyncd-clear-ADJ_MAXERROR-to-keep-STA_UNSYNC-clea.patch create mode 100644 0011-link-fix-offload-features-initialization-4639.patch create mode 100644 0012-sd-event-fix-sd_event_source_get_priority-4712.patch rename 0004-build-sys-check-for-lz4-in-the-old-and-new-numbering.patch => 0013-build-sys-check-for-lz4-in-the-old-and-new-numbering.patch (59%) create mode 100644 0014-networkd-fix-size-of-MTUBytes-so-that-it-does-not-ov.patch create mode 100644 0015-core-consider-SIGTERM-as-a-clean-exit-status-for-ini.patch rename 0005-core-make-sure-initrd-switch-root-command-survives-P.patch => 0016-core-make-sure-initrd-switch-root-command-survives-P.patch (58%) create mode 100644 0017-fix-journald-startup-problem-when-code-is-compiled-w.patch create mode 100644 0018-device-Avoid-calling-unit_free-NULL-in-device-setup-.patch create mode 100644 0019-udevd-check-correct-return-value-of-fcntl-4758.patch create mode 100644 0020-systemctl-fix-is-enabled-exit-status-on-failure-when.patch create mode 100644 0021-journal-make-sure-to-initially-populate-the-space-in.patch create mode 100644 0022-networkd-link_enter_configured-remove-assert-4800.patch create mode 100644 0023-rules-consider-MMC-device-partitions-with-partition-.patch create mode 100644 0024-nspawn-add-missing-E-to-getopt_long-4860.patch create mode 100644 0025-build-sys-define-arm-as-secondary-architecture-for-a.patch create mode 100644 0026-nspawn-when-getting-SIGCHLD-make-sure-it-s-from-the-.patch create mode 100644 0027-machinectl-make-machinectl-E-shell-work.patch create mode 100644 0028-sysv-generator-properly-translate-sysv-facilities.patch create mode 100644 0029-core-downgrade-Time-has-been-changed-to-debug-4906.patch create mode 100644 0030-machinectl-handle-EOPNOTSUPP-from-print_addresses-49.patch create mode 100644 0031-units-fix-condition-for-systemd-journal-catalog-upda.patch create mode 100644 0032-core-fix-sockaddr-length-calculation-for-sockaddr_pr.patch create mode 100644 0033-shared-fix-double-free-in-unmask-5005.patch create mode 100644 0034-shared-fix-double-free-in-link.patch create mode 100644 0035-shared-check-strdup-NULL.patch create mode 100644 0036-rpm-triggers-do-nothing-if-systemd-is-not-running-50.patch rename 0003-kernel-install-avoid-process-substitution.patch => 0037-kernel-install-avoid-process-substitution.patch (91%) create mode 100644 0038-shell-completion-redirect-all-errors-from-systemctl-.patch create mode 100644 0039-cryptsetup-fix-unitialized-variable.patch create mode 100644 0040-systemctl-uninitalized-variable.patch create mode 100644 0041-bash_completion-journalctl-add-missing-options.patch create mode 100644 0042-bash_completion-journalctl-Complete-t-option-values.patch create mode 100644 0043-Fixi-caching-in-zsh-completion-5122.patch create mode 100644 0044-bash-completion-add-support-for-now-5155.patch create mode 100644 0045-core-dbus-fix-two-strv-memleaks.patch create mode 100644 0046-core-execute-fix-strv-memleak.patch create mode 100644 0047-resolve-fix-strv-memleak.patch create mode 100644 0048-nspawn-fix-clobbering-of-selinux-context-arg.patch create mode 100644 0049-parse_hwdb-fix-to-work-with-pyparsing-2.1.10.patch create mode 100644 0050-journald-don-t-flush-to-var-log-journal-before-we-ge.patch create mode 100644 0051-tree-wide-drop-NULL-sentinel-from-strjoin.patch create mode 100644 0052-core-add-new-RestrictNamespaces-unit-file-setting.patch create mode 100644 0053-seccomp-rework-seccomp-code-to-improve-compat-with-s.patch diff --git a/0001-build-sys-link-test-seccomp-against-seccomp-libs-456.patch b/0001-build-sys-link-test-seccomp-against-seccomp-libs-456.patch index 11fbc90..be99128 100644 --- a/0001-build-sys-link-test-seccomp-against-seccomp-libs-456.patch +++ b/0001-build-sys-link-test-seccomp-against-seccomp-libs-456.patch @@ -1,4 +1,4 @@ -From 55edef4fb8c4c0b6536ad824ef05ce412a701c6a Mon Sep 17 00:00:00 2001 +From abb0253b3cdc8fb871d88b3ef92e4d9c613bed34 Mon Sep 17 00:00:00 2001 From: Martin Pitt Date: Thu, 3 Nov 2016 23:15:33 +0200 Subject: [PATCH] build-sys: link test-seccomp against seccomp libs (#4560) @@ -26,3 +26,6 @@ index f7652c2346..f2d8bf57f7 100644 test_namespace_LDADD = \ libcore.la +-- +2.9.3 + diff --git a/0002-kernel-install-use-exit-instead-of-return-4565.patch b/0002-kernel-install-use-exit-instead-of-return-4565.patch index cf0ddb1..709bcc6 100644 --- a/0002-kernel-install-use-exit-instead-of-return-4565.patch +++ b/0002-kernel-install-use-exit-instead-of-return-4565.patch @@ -1,4 +1,4 @@ -From a9facbdf54ed3397365f1f4203afa4f606ce7234 Mon Sep 17 00:00:00 2001 +From 5f48a027cec516b9c5496e5597b10456b676875f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 4 Nov 2016 21:58:41 +0900 Subject: [PATCH] kernel-install: use exit instead of return (#4565) @@ -33,3 +33,6 @@ index 0c0ee718ac..a95b9717f0 100644 fi ((ret+=$x)) fi +-- +2.9.3 + diff --git a/0003-nspawn-fix-exit-code-for-help-and-version-4609.patch b/0003-nspawn-fix-exit-code-for-help-and-version-4609.patch new file mode 100644 index 0000000..eb0cee7 --- /dev/null +++ b/0003-nspawn-fix-exit-code-for-help-and-version-4609.patch @@ -0,0 +1,33 @@ +From 5e8f1f054d332ed955414d16c57dfd406381b3e2 Mon Sep 17 00:00:00 2001 +From: Martin Pitt +Date: Tue, 8 Nov 2016 05:31:55 +0100 +Subject: [PATCH] nspawn: fix exit code for --help and --version (#4609) + +Commit b006762 inverted the initial exit code which is relevant for --help and +--version without a particular reason. For these special options, parse_argv() +returns 0 so that our main() immediately skips to the end without adjusting +"ret". Otherwise, if an actual container is being started, ret is set on error +in run(), which still provides the "non-zero exit on error" behaviour. + +Fixes #4605. +(cherry picked from commit cfed63f60dd7412c199652825ed172c319b02b3c) +--- + src/nspawn/nspawn.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c +index c56af6e6f4..c8fc011907 100644 +--- a/src/nspawn/nspawn.c ++++ b/src/nspawn/nspawn.c +@@ -4033,7 +4033,7 @@ int main(int argc, char *argv[]) { + bool root_device_rw = true, home_device_rw = true, srv_device_rw = true; + _cleanup_close_ int master = -1, image_fd = -1; + _cleanup_fdset_free_ FDSet *fds = NULL; +- int r, n_fd_passed, loop_nr = -1, ret = EXIT_FAILURE; ++ int r, n_fd_passed, loop_nr = -1, ret = EXIT_SUCCESS; + char veth_name[IFNAMSIZ] = ""; + bool secondary = false, remove_subvol = false; + pid_t pid = 0; +-- +2.9.3 + diff --git a/0004-core-don-t-use-the-unified-hierarchy-for-the-systemd.patch b/0004-core-don-t-use-the-unified-hierarchy-for-the-systemd.patch new file mode 100644 index 0000000..8e2a562 --- /dev/null +++ b/0004-core-don-t-use-the-unified-hierarchy-for-the-systemd.patch @@ -0,0 +1,40 @@ +From 68b669677dd85eaf789733eee38da3f870b1af51 Mon Sep 17 00:00:00 2001 +From: Martin Pitt +Date: Thu, 10 Nov 2016 05:33:13 +0100 +Subject: [PATCH] core: don't use the unified hierarchy for the systemd cgroup + yet (#4628) + +Too many things don't get along with the unified hierarchy yet: + + * https://github.com/opencontainers/runc/issues/1175 + * https://github.com/docker/docker/issues/28109 + * https://github.com/lxc/lxc/issues/1280 + +So revert the default to the legacy hierarchy for now. Developers of the above +software can opt into the unified hierarchy with +"systemd.legacy_systemd_cgroup_controller=0". +(cherry picked from commit 843d5baf6aad6c53fc00ea8d95d83209a4f92de1) +--- + src/basic/cgroup-util.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c +index cede835920..134e6e3664 100644 +--- a/src/basic/cgroup-util.c ++++ b/src/basic/cgroup-util.c +@@ -2423,10 +2423,10 @@ bool cg_is_unified_systemd_controller_wanted(void) { + + r = get_proc_cmdline_key("systemd.legacy_systemd_cgroup_controller=", &value); + if (r < 0) +- return true; ++ return false; + + if (r == 0) +- wanted = true; ++ wanted = false; + else + wanted = parse_boolean(value) <= 0; + } +-- +2.9.3 + diff --git a/0005-core-make-RootDirectory-and-ProtectKernelModules-wor.patch b/0005-core-make-RootDirectory-and-ProtectKernelModules-wor.patch new file mode 100644 index 0000000..65899d2 --- /dev/null +++ b/0005-core-make-RootDirectory-and-ProtectKernelModules-wor.patch @@ -0,0 +1,335 @@ +From 5ad2a68bfc8541599aa61420a8507a497d87ac3c Mon Sep 17 00:00:00 2001 +From: Djalal Harouni +Date: Sun, 6 Nov 2016 22:51:49 +0100 +Subject: [PATCH] core: make RootDirectory= and ProtectKernelModules= work + +Instead of having two fields inside BindMount struct where one is stack +based and the other one is heap, use one field to store the full path +and updated it when we chase symlinks. This way we avoid dealing with +both at the same time. + +This makes RootDirectory= work with ProtectHome= and ProtectKernelModules=yes + +Fixes: https://github.com/systemd/systemd/issues/4567 +(cherry picked from commit f0a4feb0a5318e52107b0df63997a9b13f5be668) +--- + src/core/namespace.c | 142 +++++++++++++++++++++++++++++++-------------------- + 1 file changed, 86 insertions(+), 56 deletions(-) + +diff --git a/src/core/namespace.c b/src/core/namespace.c +index 1195e9a854..c45c120193 100644 +--- a/src/core/namespace.c ++++ b/src/core/namespace.c +@@ -58,8 +58,7 @@ typedef enum MountMode { + } MountMode; + + typedef struct BindMount { +- const char *path; /* stack memory, doesn't need to be freed explicitly */ +- char *chased; /* malloc()ed memory, needs to be freed */ ++ char *path; + MountMode mode; + bool ignore; /* Ignore if path does not exist */ + } BindMount; +@@ -155,12 +154,27 @@ static const TargetMount protect_system_strict_table[] = { + { "/root", READWRITE, true }, /* ProtectHome= */ + }; + +-static void set_bind_mount(BindMount **p, const char *path, MountMode mode, bool ignore) { ++static void set_bind_mount(BindMount **p, char *path, MountMode mode, bool ignore) { + (*p)->path = path; + (*p)->mode = mode; + (*p)->ignore = ignore; + } + ++static int append_one_mount(BindMount **p, const char *root_directory, ++ const char *path, MountMode mode, bool ignore) { ++ char *lpath; ++ assert(p); ++ ++ lpath = prefix_root(root_directory, path); ++ if (!lpath) ++ return -ENOMEM; ++ ++ set_bind_mount(p, lpath, mode, ignore); ++ (*p)++; ++ ++ return 0; ++} ++ + static int append_mounts(BindMount **p, char **strv, MountMode mode) { + char **i; + +@@ -168,6 +182,7 @@ static int append_mounts(BindMount **p, char **strv, MountMode mode) { + + STRV_FOREACH(i, strv) { + bool ignore = false; ++ char *path; + + if (IN_SET(mode, INACCESSIBLE, READONLY, READWRITE) && startswith(*i, "-")) { + (*i)++; +@@ -177,7 +192,11 @@ static int append_mounts(BindMount **p, char **strv, MountMode mode) { + if (!path_is_absolute(*i)) + return -EINVAL; + +- set_bind_mount(p, *i, mode, ignore); ++ path = strdup(*i); ++ if (!path) ++ return -ENOMEM; ++ ++ set_bind_mount(p, path, mode, ignore); + (*p)++; + } + +@@ -196,7 +215,11 @@ static int append_target_mounts(BindMount **p, const char *root_directory, const + * declaration we do not support "-" at the beginning. + */ + const TargetMount *m = &mounts[i]; +- const char *path = prefix_roota(root_directory, m->path); ++ char *path; ++ ++ path = prefix_root(root_directory, m->path); ++ if (!path) ++ return -ENOMEM; + + if (!path_is_absolute(path)) + return -EINVAL; +@@ -309,6 +332,7 @@ static void drop_duplicates(BindMount *m, unsigned *n) { + * above. */ + if (previous && path_equal(f->path, previous->path)) { + log_debug("%s is duplicate.", f->path); ++ f->path = mfree(f->path); + continue; + } + +@@ -336,6 +360,7 @@ static void drop_inaccessible(BindMount *m, unsigned *n) { + * it, as inaccessible paths really should drop the entire subtree. */ + if (clear && path_startswith(f->path, clear)) { + log_debug("%s is masked by %s.", f->path, clear); ++ f->path = mfree(f->path); + continue; + } + +@@ -375,6 +400,7 @@ static void drop_nop(BindMount *m, unsigned *n) { + /* We found it, let's see if it's the same mode, if so, we can drop this entry */ + if (found && p->mode == f->mode) { + log_debug("%s is redundant by %s", f->path, p->path); ++ f->path = mfree(f->path); + continue; + } + } +@@ -401,6 +427,7 @@ static void drop_outside_root(const char *root_directory, BindMount *m, unsigned + + if (!path_startswith(f->path, root_directory)) { + log_debug("%s is outside of root directory.", f->path); ++ f->path = mfree(f->path); + continue; + } + +@@ -652,18 +679,21 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned + * can't resolve the path, and which have been marked for such removal. */ + + for (f = m, t = m; f < m+*n; f++) { +- +- r = chase_symlinks(f->path, root_directory, &f->chased); +- if (r == -ENOENT && f->ignore) /* Doesn't exist? Then remove it! */ ++ _cleanup_free_ char *chased = NULL; ++ r = chase_symlinks(f->path, root_directory, &chased); ++ if (r == -ENOENT && f->ignore) { ++ /* Doesn't exist? Then remove it! */ ++ f->path = mfree(f->path); + continue; ++ } + if (r < 0) + return log_debug_errno(r, "Failed to chase symlinks for %s: %m", f->path); + +- if (path_equal(f->path, f->chased)) +- f->chased = mfree(f->chased); +- else { +- log_debug("Chased %s → %s", f->path, f->chased); +- f->path = f->chased; ++ if (!path_equal(f->path, chased)) { ++ log_debug("Chased %s → %s", f->path, chased); ++ r = free_and_strdup(&f->path, chased); ++ if (r < 0) ++ return r; + } + + *t = *f; +@@ -724,96 +754,96 @@ int setup_namespace( + + BindMount *m, *mounts = NULL; + bool make_slave = false; +- unsigned n; ++ unsigned n_mounts; + int r = 0; + + if (mount_flags == 0) + mount_flags = MS_SHARED; + +- n = namespace_calculate_mounts(ns_info, +- read_write_paths, +- read_only_paths, +- inaccessible_paths, +- tmp_dir, var_tmp_dir, +- protect_home, protect_system); ++ n_mounts = namespace_calculate_mounts(ns_info, ++ read_write_paths, ++ read_only_paths, ++ inaccessible_paths, ++ tmp_dir, var_tmp_dir, ++ protect_home, protect_system); + + /* Set mount slave mode */ +- if (root_directory || n > 0) ++ if (root_directory || n_mounts > 0) + make_slave = true; + +- if (n > 0) { +- m = mounts = (BindMount *) alloca0(n * sizeof(BindMount)); ++ if (n_mounts > 0) { ++ m = mounts = (BindMount *) alloca0(n_mounts * sizeof(BindMount)); + r = append_mounts(&m, read_write_paths, READWRITE); + if (r < 0) +- return r; ++ goto finish; + + r = append_mounts(&m, read_only_paths, READONLY); + if (r < 0) +- return r; ++ goto finish; + + r = append_mounts(&m, inaccessible_paths, INACCESSIBLE); + if (r < 0) +- return r; ++ goto finish; + + if (tmp_dir) { +- m->path = prefix_roota(root_directory, "/tmp"); +- m->mode = PRIVATE_TMP; +- m++; ++ r = append_one_mount(&m, root_directory, "/tmp", PRIVATE_TMP, false); ++ if (r < 0) ++ goto finish; + } + + if (var_tmp_dir) { +- m->path = prefix_roota(root_directory, "/var/tmp"); +- m->mode = PRIVATE_VAR_TMP; +- m++; ++ r = append_one_mount(&m, root_directory, "/var/tmp", PRIVATE_VAR_TMP, false); ++ if (r < 0) ++ goto finish; + } + + if (ns_info->private_dev) { +- m->path = prefix_roota(root_directory, "/dev"); +- m->mode = PRIVATE_DEV; +- m++; ++ r = append_one_mount(&m, root_directory, "/dev", PRIVATE_DEV, false); ++ if (r < 0) ++ goto finish; + } + + if (ns_info->protect_kernel_tunables) { + r = append_protect_kernel_tunables(&m, root_directory); + if (r < 0) +- return r; ++ goto finish; + } + + if (ns_info->protect_kernel_modules) { + r = append_protect_kernel_modules(&m, root_directory); + if (r < 0) +- return r; ++ goto finish; + } + + if (ns_info->protect_control_groups) { +- m->path = prefix_roota(root_directory, "/sys/fs/cgroup"); +- m->mode = READONLY; +- m++; ++ r = append_one_mount(&m, root_directory, "/sys/fs/cgroup", READONLY, false); ++ if (r < 0) ++ goto finish; + } + + r = append_protect_home(&m, root_directory, protect_home); + if (r < 0) +- return r; ++ goto finish; + + r = append_protect_system(&m, root_directory, protect_system); + if (r < 0) +- return r; ++ goto finish; + +- assert(mounts + n == m); ++ assert(mounts + n_mounts == m); + + /* Resolve symlinks manually first, as mount() will always follow them relative to the host's + * root. Moreover we want to suppress duplicates based on the resolved paths. This of course is a bit + * racy. */ +- r = chase_all_symlinks(root_directory, mounts, &n); ++ r = chase_all_symlinks(root_directory, mounts, &n_mounts); + if (r < 0) + goto finish; + +- qsort(mounts, n, sizeof(BindMount), mount_path_compare); ++ qsort(mounts, n_mounts, sizeof(BindMount), mount_path_compare); + +- drop_duplicates(mounts, &n); +- drop_outside_root(root_directory, mounts, &n); +- drop_inaccessible(mounts, &n); +- drop_nop(mounts, &n); ++ drop_duplicates(mounts, &n_mounts); ++ drop_outside_root(root_directory, mounts, &n_mounts); ++ drop_inaccessible(mounts, &n_mounts); ++ drop_nop(mounts, &n_mounts); + } + + if (unshare(CLONE_NEWNS) < 0) { +@@ -843,25 +873,25 @@ int setup_namespace( + } + } + +- if (n > 0) { ++ if (n_mounts > 0) { + char **blacklist; + unsigned j; + + /* First round, add in all special mounts we need */ +- for (m = mounts; m < mounts + n; ++m) { ++ for (m = mounts; m < mounts + n_mounts; ++m) { + r = apply_mount(m, tmp_dir, var_tmp_dir); + if (r < 0) + goto finish; + } + + /* Create a blacklist we can pass to bind_mount_recursive() */ +- blacklist = newa(char*, n+1); +- for (j = 0; j < n; j++) ++ blacklist = newa(char*, n_mounts+1); ++ for (j = 0; j < n_mounts; j++) + blacklist[j] = (char*) mounts[j].path; + blacklist[j] = NULL; + + /* Second round, flip the ro bits if necessary. */ +- for (m = mounts; m < mounts + n; ++m) { ++ for (m = mounts; m < mounts + n_mounts; ++m) { + r = make_read_only(m, blacklist); + if (r < 0) + goto finish; +@@ -886,8 +916,8 @@ int setup_namespace( + r = 0; + + finish: +- for (m = mounts; m < mounts + n; m++) +- free(m->chased); ++ for (m = mounts; m < mounts + n_mounts; m++) ++ free(m->path); + + return r; + } +-- +2.9.3 + diff --git a/0006-nspawn-avoid-one-strdup-by-using-free_and_replace.patch b/0006-nspawn-avoid-one-strdup-by-using-free_and_replace.patch new file mode 100644 index 0000000..1c93a7c --- /dev/null +++ b/0006-nspawn-avoid-one-strdup-by-using-free_and_replace.patch @@ -0,0 +1,37 @@ +From 14788b363be244f3dce87ccd5447b2d6bb7ce930 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Mon, 7 Nov 2016 08:54:47 -0500 +Subject: [PATCH] nspawn: avoid one strdup by using free_and_replace + +(cherry picked from commit 49fedb4094b69d279095ef8ce8d9203161aed5d9) +--- + src/core/namespace.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/src/core/namespace.c b/src/core/namespace.c +index c45c120193..524416ecbd 100644 +--- a/src/core/namespace.c ++++ b/src/core/namespace.c +@@ -678,8 +678,9 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned + * chase the symlinks on our own first. This call wil do so for all entries and remove all entries where we + * can't resolve the path, and which have been marked for such removal. */ + +- for (f = m, t = m; f < m+*n; f++) { ++ for (f = m, t = m; f < m + *n; f++) { + _cleanup_free_ char *chased = NULL; ++ + r = chase_symlinks(f->path, root_directory, &chased); + if (r == -ENOENT && f->ignore) { + /* Doesn't exist? Then remove it! */ +@@ -691,7 +692,7 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned + + if (!path_equal(f->path, chased)) { + log_debug("Chased %s → %s", f->path, chased); +- r = free_and_strdup(&f->path, chased); ++ r = free_and_replace(f->path, chased); + if (r < 0) + return r; + } +-- +2.9.3 + diff --git a/0007-nspawn-slight-simplification.patch b/0007-nspawn-slight-simplification.patch new file mode 100644 index 0000000..2735a5c --- /dev/null +++ b/0007-nspawn-slight-simplification.patch @@ -0,0 +1,63 @@ +From a5f0ac8737df6f00cd1827bc2ed78bf6ab1cef48 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Mon, 7 Nov 2016 08:57:30 -0500 +Subject: [PATCH] nspawn: slight simplification + +(cherry picked from commit 46c3230dd0985062f06341809faa05e73fa1ccd1) +--- + src/core/namespace.c | 18 +++++++----------- + 1 file changed, 7 insertions(+), 11 deletions(-) + +diff --git a/src/core/namespace.c b/src/core/namespace.c +index 524416ecbd..b18fcf3e29 100644 +--- a/src/core/namespace.c ++++ b/src/core/namespace.c +@@ -154,10 +154,10 @@ static const TargetMount protect_system_strict_table[] = { + { "/root", READWRITE, true }, /* ProtectHome= */ + }; + +-static void set_bind_mount(BindMount **p, char *path, MountMode mode, bool ignore) { +- (*p)->path = path; +- (*p)->mode = mode; +- (*p)->ignore = ignore; ++static void set_bind_mount(BindMount *p, char *path, MountMode mode, bool ignore) { ++ p->path = path; ++ p->mode = mode; ++ p->ignore = ignore; + } + + static int append_one_mount(BindMount **p, const char *root_directory, +@@ -169,9 +169,7 @@ static int append_one_mount(BindMount **p, const char *root_directory, + if (!lpath) + return -ENOMEM; + +- set_bind_mount(p, lpath, mode, ignore); +- (*p)++; +- ++ set_bind_mount((*p)++, lpath, mode, ignore); + return 0; + } + +@@ -196,8 +194,7 @@ static int append_mounts(BindMount **p, char **strv, MountMode mode) { + if (!path) + return -ENOMEM; + +- set_bind_mount(p, path, mode, ignore); +- (*p)++; ++ set_bind_mount((*p)++, path, mode, ignore); + } + + return 0; +@@ -224,8 +221,7 @@ static int append_target_mounts(BindMount **p, const char *root_directory, const + if (!path_is_absolute(path)) + return -EINVAL; + +- set_bind_mount(p, path, m->mode, m->ignore); +- (*p)++; ++ set_bind_mount((*p)++, path, m->mode, m->ignore); + } + + return 0; +-- +2.9.3 + diff --git a/0008-core-namespace-count-and-free-failed-paths-inside-ch.patch b/0008-core-namespace-count-and-free-failed-paths-inside-ch.patch new file mode 100644 index 0000000..df06ce9 --- /dev/null +++ b/0008-core-namespace-count-and-free-failed-paths-inside-ch.patch @@ -0,0 +1,122 @@ +From 6075c25fa4c1411b072955445f0f4a2418f54b0c Mon Sep 17 00:00:00 2001 +From: Djalal Harouni +Date: Thu, 10 Nov 2016 18:11:37 +0100 +Subject: [PATCH] core:namespace: count and free failed paths inside + chase_all_symlinks() (#4619) + +This certainly fixes a bug that was introduced by PR +https://github.com/systemd/systemd/pull/4594 that intended to fix +https://github.com/systemd/systemd/issues/4567. + +The fix was not complete. This patch makes sure that we count and free +all paths that fail inside chase_all_symlinks(). + +Fixes https://github.com/systemd/systemd/issues/4567 +(cherry picked from commit 1d54cd5d255065c8659ca68d2591e810e6104714) +--- + src/core/namespace.c | 32 ++++++++++++++++++-------------- + 1 file changed, 18 insertions(+), 14 deletions(-) + +diff --git a/src/core/namespace.c b/src/core/namespace.c +index b18fcf3e29..49a50c7b61 100644 +--- a/src/core/namespace.c ++++ b/src/core/namespace.c +@@ -322,7 +322,7 @@ static void drop_duplicates(BindMount *m, unsigned *n) { + + /* Drops duplicate entries. Expects that the array is properly ordered already. */ + +- for (f = m, t = m, previous = NULL; f < m+*n; f++) { ++ for (f = m, t = m, previous = NULL; f < m + *n; f++) { + + /* The first one wins (which is the one with the more restrictive mode), see mount_path_compare() + * above. */ +@@ -350,7 +350,7 @@ static void drop_inaccessible(BindMount *m, unsigned *n) { + /* Drops all entries obstructed by another entry further up the tree. Expects that the array is properly + * ordered already. */ + +- for (f = m, t = m; f < m+*n; f++) { ++ for (f = m, t = m; f < m + *n; f++) { + + /* If we found a path set for INACCESSIBLE earlier, and this entry has it as prefix we should drop + * it, as inaccessible paths really should drop the entire subtree. */ +@@ -378,7 +378,7 @@ static void drop_nop(BindMount *m, unsigned *n) { + /* Drops all entries which have an immediate parent that has the same type, as they are redundant. Assumes the + * list is ordered by prefixes. */ + +- for (f = m, t = m; f < m+*n; f++) { ++ for (f = m, t = m; f < m + *n; f++) { + + /* Only suppress such subtrees for READONLY and READWRITE entries */ + if (IN_SET(f->mode, READONLY, READWRITE)) { +@@ -414,12 +414,13 @@ static void drop_outside_root(const char *root_directory, BindMount *m, unsigned + assert(m); + assert(n); + ++ /* Nothing to do */ + if (!root_directory) + return; + + /* Drops all mounts that are outside of the root directory. */ + +- for (f = m, t = m; f < m+*n; f++) { ++ for (f = m, t = m; f < m + *n; f++) { + + if (!path_startswith(f->path, root_directory)) { + log_debug("%s is outside of root directory.", f->path); +@@ -663,9 +664,10 @@ static int make_read_only(BindMount *m, char **blacklist) { + return r; + } + ++/* Chase symlinks and remove failed paths from mounts */ + static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned *n) { + BindMount *f, *t; +- int r; ++ int r = 0; + + assert(m); + assert(n); +@@ -675,22 +677,24 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned + * can't resolve the path, and which have been marked for such removal. */ + + for (f = m, t = m; f < m + *n; f++) { ++ int k; + _cleanup_free_ char *chased = NULL; + +- r = chase_symlinks(f->path, root_directory, &chased); +- if (r == -ENOENT && f->ignore) { +- /* Doesn't exist? Then remove it! */ ++ k = chase_symlinks(f->path, root_directory, &chased); ++ if (k < 0) { ++ /* Get only real errors */ ++ if (r >= 0 && (k != -ENOENT || !f->ignore)) ++ r = k; ++ ++ log_debug_errno(r, "Failed to chase symlinks for %s: %m", f->path); ++ /* Doesn't exist or failed? Then remove it and continue! */ + f->path = mfree(f->path); + continue; + } +- if (r < 0) +- return log_debug_errno(r, "Failed to chase symlinks for %s: %m", f->path); + + if (!path_equal(f->path, chased)) { + log_debug("Chased %s → %s", f->path, chased); +- r = free_and_replace(f->path, chased); +- if (r < 0) +- return r; ++ free_and_replace(f->path, chased); + } + + *t = *f; +@@ -698,7 +702,7 @@ static int chase_all_symlinks(const char *root_directory, BindMount *m, unsigned + } + + *n = t - m; +- return 0; ++ return r; + } + + static unsigned namespace_calculate_mounts( +-- +2.9.3 + diff --git a/0009-basic-virt-fix-userns-check-on-CONFIG_USER_NS-n-kern.patch b/0009-basic-virt-fix-userns-check-on-CONFIG_USER_NS-n-kern.patch new file mode 100644 index 0000000..9ae482b --- /dev/null +++ b/0009-basic-virt-fix-userns-check-on-CONFIG_USER_NS-n-kern.patch @@ -0,0 +1,32 @@ +From 99a1035c5243535f8ca01aec3b28c09405d09184 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 11 Nov 2016 10:54:54 -0500 +Subject: [PATCH] basic/virt: fix userns check on CONFIG_USER_NS=n kernel + (#4651) + +ENOENT should be treated as "false", but because of the broken errno check it +was treated as an error. So ConditionVirtualization=user-namespaces probably +returned the correct answer, but only by accident. + +Fixes #4608. +(cherry picked from commit abd67ce74858491565cde157c7b08fda43d3279c) +--- + src/basic/virt.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/basic/virt.c b/src/basic/virt.c +index 69b0f96183..d8d57381ad 100644 +--- a/src/basic/virt.c ++++ b/src/basic/virt.c +@@ -496,7 +496,7 @@ static int userns_has_mapping(const char *name) { + f = fopen(name, "re"); + if (!f) { + log_debug_errno(errno, "Failed to open %s: %m", name); +- return errno == -ENOENT ? false : -errno; ++ return errno == ENOENT ? false : -errno; + } + + n = getline(&buf, &n_allocated, f); +-- +2.9.3 + diff --git a/0010-timesyncd-clear-ADJ_MAXERROR-to-keep-STA_UNSYNC-clea.patch b/0010-timesyncd-clear-ADJ_MAXERROR-to-keep-STA_UNSYNC-clea.patch new file mode 100644 index 0000000..8c7ca11 --- /dev/null +++ b/0010-timesyncd-clear-ADJ_MAXERROR-to-keep-STA_UNSYNC-clea.patch @@ -0,0 +1,44 @@ +From 7c120409dcb6ad0658f06aef660913450e538f6d Mon Sep 17 00:00:00 2001 +From: akochetkov +Date: Fri, 11 Nov 2016 20:50:46 +0300 +Subject: [PATCH] timesyncd: clear ADJ_MAXERROR to keep STA_UNSYNC cleared + after jump adjust (#4626) + +NTP use jump adjust if system has incorrect time read from RTC during boot. +It is desireble to update RTC time as soon as NTP set correct system time. +Sometimes kernel failed to update RTC due to STA_UNSYNC get set before RTC +update finised. In that case RTC time wouldn't be updated within long time. +The commit makes RTC updates stable. + +When NTP do jump time adjust using ADJ_SETOFFSET it clears STA_UNSYNC flag. +If don't clear ADJ_MAXERROR, STA_UNSYNC will be set again by kernel within +1 second (by second_overflow() function). STA_UNSYNC flag prevent RTC updates +in kernel. Sometimes the kernel is able to update RTC withing 1 second, +but sometimes it falied. +(cherry picked from commit 5f36e3d30375cf04292bbc1bf3f4d7512cf80139) +--- + src/timesync/timesyncd-manager.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c +index d5e16db3a0..419f0fa279 100644 +--- a/src/timesync/timesyncd-manager.c ++++ b/src/timesync/timesyncd-manager.c +@@ -330,11 +330,13 @@ static int manager_adjust_clock(Manager *m, double offset, int leap_sec) { + tmx.esterror = 0; + log_debug(" adjust (slew): %+.3f sec", offset); + } else { +- tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET; ++ tmx.modes = ADJ_STATUS | ADJ_NANO | ADJ_SETOFFSET | ADJ_MAXERROR | ADJ_ESTERROR; + + /* ADJ_NANO uses nanoseconds in the microseconds field */ + tmx.time.tv_sec = (long)offset; + tmx.time.tv_usec = (offset - tmx.time.tv_sec) * NSEC_PER_SEC; ++ tmx.maxerror = 0; ++ tmx.esterror = 0; + + /* the kernel expects -0.3s as {-1, 7000.000.000} */ + if (tmx.time.tv_usec < 0) { +-- +2.9.3 + diff --git a/0011-link-fix-offload-features-initialization-4639.patch b/0011-link-fix-offload-features-initialization-4639.patch new file mode 100644 index 0000000..bf0b718 --- /dev/null +++ b/0011-link-fix-offload-features-initialization-4639.patch @@ -0,0 +1,26 @@ +From 6c19e0abceb3c22100edc0ce8be2680a5abd6945 Mon Sep 17 00:00:00 2001 +From: Maks Naumov +Date: Mon, 14 Nov 2016 12:33:43 +0200 +Subject: [PATCH] link: fix offload features initialization (#4639) + +(cherry picked from commit 45d34fa7fb9f587f09e8cca882966050b4cbb5be) +--- + src/udev/net/link-config.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c +index ece9248c2a..fa8fe1fb9a 100644 +--- a/src/udev/net/link-config.c ++++ b/src/udev/net/link-config.c +@@ -168,7 +168,7 @@ static int load_link(link_config_ctx *ctx, const char *filename) { + link->wol = _WOL_INVALID; + link->duplex = _DUP_INVALID; + +- memset(&link->features, -1, _NET_DEV_FEAT_MAX); ++ memset(&link->features, -1, sizeof(link->features)); + + r = config_parse(NULL, filename, file, + "Match\0Link\0Ethernet\0", +-- +2.9.3 + diff --git a/0012-sd-event-fix-sd_event_source_get_priority-4712.patch b/0012-sd-event-fix-sd_event_source_get_priority-4712.patch new file mode 100644 index 0000000..d6d353b --- /dev/null +++ b/0012-sd-event-fix-sd_event_source_get_priority-4712.patch @@ -0,0 +1,51 @@ +From fd2aab7bff3842df53a93e7ab8239a279bb3ad8f Mon Sep 17 00:00:00 2001 +From: Martin Ejdestig +Date: Tue, 22 Nov 2016 01:21:00 +0100 +Subject: [PATCH] sd-event: fix sd_event_source_get_priority() (#4712) + +To properly store priority in passed in pointer and return 0 for success. +Also add a test for verifying that it works correctly. +(cherry picked from commit 6680b8d118490bbb3e5522729ec50d9975088fd5) +--- + src/libsystemd/sd-event/sd-event.c | 3 ++- + src/libsystemd/sd-event/test-event.c | 3 +++ + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c +index 9857f8b1fc..f94959adac 100644 +--- a/src/libsystemd/sd-event/sd-event.c ++++ b/src/libsystemd/sd-event/sd-event.c +@@ -1539,7 +1539,8 @@ _public_ int sd_event_source_get_priority(sd_event_source *s, int64_t *priority) + assert_return(s, -EINVAL); + assert_return(!event_pid_changed(s->event), -ECHILD); + +- return s->priority; ++ *priority = s->priority; ++ return 0; + } + + _public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority) { +diff --git a/src/libsystemd/sd-event/test-event.c b/src/libsystemd/sd-event/test-event.c +index 289114490c..c0e5e06a18 100644 +--- a/src/libsystemd/sd-event/test-event.c ++++ b/src/libsystemd/sd-event/test-event.c +@@ -172,6 +172,7 @@ static void test_basic(void) { + static const char ch = 'x'; + int a[2] = { -1, -1 }, b[2] = { -1, -1}, d[2] = { -1, -1}, k[2] = { -1, -1 }; + uint64_t event_now; ++ int64_t priority; + + assert_se(pipe(a) >= 0); + assert_se(pipe(b) >= 0); +@@ -209,6 +210,8 @@ static void test_basic(void) { + assert_se(sd_event_add_exit(e, &q, exit_handler, INT_TO_PTR('g')) >= 0); + + assert_se(sd_event_source_set_priority(x, 99) >= 0); ++ assert_se(sd_event_source_get_priority(x, &priority) >= 0); ++ assert_se(priority == 99); + assert_se(sd_event_source_set_enabled(y, SD_EVENT_ONESHOT) >= 0); + assert_se(sd_event_source_set_prepare(x, prepare_handler) >= 0); + assert_se(sd_event_source_set_priority(z, 50) >= 0); +-- +2.9.3 + diff --git a/0004-build-sys-check-for-lz4-in-the-old-and-new-numbering.patch b/0013-build-sys-check-for-lz4-in-the-old-and-new-numbering.patch similarity index 59% rename from 0004-build-sys-check-for-lz4-in-the-old-and-new-numbering.patch rename to 0013-build-sys-check-for-lz4-in-the-old-and-new-numbering.patch index aadac4f..3fd1a44 100644 --- a/0004-build-sys-check-for-lz4-in-the-old-and-new-numbering.patch +++ b/0013-build-sys-check-for-lz4-in-the-old-and-new-numbering.patch @@ -1,32 +1,29 @@ -From 047be3e733cff7717ccd49d6422ea0002bd0c515 Mon Sep 17 00:00:00 2001 -From: Jan Synacek -Date: Mon, 16 Jan 2017 13:00:03 +0100 +From b6c1a9e4cdea2d3e614125707389bf69c00769b0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 23 Nov 2016 10:18:30 -0500 Subject: [PATCH] build-sys: check for lz4 in the old and new numbering scheme (#4717) lz4 upstream decided to switch to an incompatible numbering scheme -(1.7.3 follows 131, to match the so version). PKG_CHECK_MODULES does -not allow two version matches for the same package, so e.g. lz4 < 10 || -lz4 >= 125 cannot be used. Check twice, once for "new" numbers (anything -below 10 is assume to be new), once for the "old" numbers (anything -above >= 125). This assumes that the "new" versioning will not get to 10 -to quickly. I think that's a safe assumption, lz4 is a mature project. +(1.7.3 follows 131, to match the so version). +PKG_CHECK_MODULES does not allow two version matches for the same package, +so e.g. lz4 < 10 || lz4 >= 125 cannot be used. Check twice, once for +"new" numbers (anything below 10 is assume to be new), once for the "old" +numbers (anything above >= 125). This assumes that the "new" versioning +will not get to 10 to quickly. I think that's a safe assumption, lz4 is a +mature project. Fixed #4690. - -Cherry-picked from -https://github.com/systemd/systemd/commit/3d4cf7de48a74726694abbaa09f9804b845ff3ba - -Resolves: #1404406 +(cherry picked from commit 3d4cf7de48a74726694abbaa09f9804b845ff3ba) --- configure.ac | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac -index cf595e6..4a0b269 100644 +index 0b10fc7de7..1928e65bde 100644 --- a/configure.ac +++ b/configure.ac -@@ -602,10 +602,13 @@ AM_CONDITIONAL(HAVE_BZIP2, [test "$have_bzip2" = "yes"]) +@@ -623,10 +623,13 @@ AM_CONDITIONAL(HAVE_BZIP2, [test "$have_bzip2" = "yes"]) have_lz4=no AC_ARG_ENABLE(lz4, AS_HELP_STRING([--disable-lz4], [Disable optional LZ4 support])) AS_IF([test "x$enable_lz4" != "xno"], [ diff --git a/0014-networkd-fix-size-of-MTUBytes-so-that-it-does-not-ov.patch b/0014-networkd-fix-size-of-MTUBytes-so-that-it-does-not-ov.patch new file mode 100644 index 0000000..f31b015 --- /dev/null +++ b/0014-networkd-fix-size-of-MTUBytes-so-that-it-does-not-ov.patch @@ -0,0 +1,31 @@ +From abe63bfc21e0737b23b5c8f6eeb088c4030cc631 Mon Sep 17 00:00:00 2001 +From: Susant Sahani +Date: Wed, 23 Nov 2016 21:03:01 +0530 +Subject: [PATCH] networkd: fix size of MTUBytes so that it does not overwrites + ARP (#4707) + +config_parse_iec_size overwrites the next varible that is ARP. +Now the mtu is unsigned . Make it size_t . + +Fixes #4644 +(cherry picked from commit b8b40317d0355bc70bb23a6240a36f3630c4952b) +--- + src/network/networkd-network.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h +index 42fc82d392..11ff34b5b5 100644 +--- a/src/network/networkd-network.h ++++ b/src/network/networkd-network.h +@@ -173,7 +173,7 @@ struct Network { + IPv6PrivacyExtensions ipv6_privacy_extensions; + + struct ether_addr *mac; +- unsigned mtu; ++ size_t mtu; + int arp; + uint32_t iaid; + DUID duid; +-- +2.9.3 + diff --git a/0015-core-consider-SIGTERM-as-a-clean-exit-status-for-ini.patch b/0015-core-consider-SIGTERM-as-a-clean-exit-status-for-ini.patch new file mode 100644 index 0000000..46e4bb6 --- /dev/null +++ b/0015-core-consider-SIGTERM-as-a-clean-exit-status-for-ini.patch @@ -0,0 +1,50 @@ +From 18eb87fdddbcb03940b2009e6cb43852e8735e9a Mon Sep 17 00:00:00 2001 +From: Franck Bui +Date: Wed, 23 Nov 2016 16:31:24 +0100 +Subject: [PATCH] core: consider SIGTERM as a clean exit status for + initrd-switch-root.service (#4713) + +Since commit 1f0958f640b8717, systemd considers SIGTERM for short-running +services (aka Type=oneshot) as a failure. + +This can be an issue with initrd-switch-root.service as the command run by this +service (in order to switch to the new rootfs) may still be running when +systemd does the switch. + +However PID1 sends SIGTERM to all remaining processes right before +switching and initrd-switch-root.service can be one of those. + +After systemd is reexecuted and its previous state is deserialized, systemd +notices that initrd-switch-root.service was killed with SIGTERM and considers +this as a failure which leads to the emergency shell. + +To prevent this, this patch teaches systemd to consider a SIGTERM exit as a +clean one for this service. + +It also removes "KillMode=none" since this is pretty useless as the service is +never stopped by systemd but it either exits normally or it's killed by a +SIGTERM as described previously. +(cherry picked from commit a4021390fef27f4136497328f2e35e79bc88855d) +--- + units/initrd-switch-root.service.in | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/units/initrd-switch-root.service.in b/units/initrd-switch-root.service.in +index 82893dafb1..b89f2348c7 100644 +--- a/units/initrd-switch-root.service.in ++++ b/units/initrd-switch-root.service.in +@@ -17,4 +17,10 @@ AllowIsolate=yes + Type=oneshot + # we have to use "--force" here, otherwise systemd would umount /run + ExecStart=@rootbindir@/systemctl --no-block --force switch-root /sysroot +-KillMode=none ++ ++# Just before switching to the new rootfs, systemd might send us a TERM signal ++# depending on how fast we are to execute the main command and exit. If we get ++# the SIGTERM signal that simply means that we succeed but haven't got enough ++# time to exit properly. Since systemd considers SIGTERM as a failure for ++# short-running process (aka Type=oneshot), instruct it to ignore this case. ++SuccessExitStatus=SIGTERM +-- +2.9.3 + diff --git a/0005-core-make-sure-initrd-switch-root-command-survives-P.patch b/0016-core-make-sure-initrd-switch-root-command-survives-P.patch similarity index 58% rename from 0005-core-make-sure-initrd-switch-root-command-survives-P.patch rename to 0016-core-make-sure-initrd-switch-root-command-survives-P.patch index 11d1f8c..0555e91 100644 --- a/0005-core-make-sure-initrd-switch-root-command-survives-P.patch +++ b/0016-core-make-sure-initrd-switch-root-command-survives-P.patch @@ -1,4 +1,4 @@ -From acc28e2e3037d689d6481e4664925cf31d4d087b Mon Sep 17 00:00:00 2001 +From 6fbbf006adc4830f0ed956042e54e747168fa879 Mon Sep 17 00:00:00 2001 From: Franck Bui Date: Thu, 24 Nov 2016 18:52:04 +0100 Subject: [PATCH] core: make sure initrd-switch-root command survives PID1's @@ -14,12 +14,14 @@ See: https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons/ for more details. We had to backup argv[0] because argv is modified by dispatch_verb(). +(cherry picked from commit acc28e2e3037d689d6481e4664925cf31d4d087b) --- src/systemctl/systemctl.c | 10 ++++++++++ - 1 files changed, 10 insertions(+), 0 deletions(-) + units/initrd-switch-root.service.in | 8 +------- + 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c -index 4fd8d7ba2..ed1c7178b 100644 +index dd3b931cd6..a2b667481d 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -142,6 +142,7 @@ static const char *arg_kill_who = NULL; @@ -30,7 +32,7 @@ index 4fd8d7ba2..ed1c7178b 100644 static enum action { _ACTION_INVALID, ACTION_SYSTEMCTL, -@@ -5638,6 +5639,13 @@ static int switch_root(int argc, char *argv[], void *userdata) { +@@ -5584,6 +5585,13 @@ static int switch_root(int argc, char *argv[], void *userdata) { init = NULL; } @@ -44,7 +46,7 @@ index 4fd8d7ba2..ed1c7178b 100644 r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) return r; -@@ -8373,6 +8381,8 @@ static int logind_cancel_shutdown(void) { +@@ -8324,6 +8332,8 @@ static int logind_cancel_shutdown(void) { int main(int argc, char*argv[]) { int r; @@ -53,6 +55,22 @@ index 4fd8d7ba2..ed1c7178b 100644 setlocale(LC_ALL, ""); log_parse_environment(); log_open(); +diff --git a/units/initrd-switch-root.service.in b/units/initrd-switch-root.service.in +index b89f2348c7..82893dafb1 100644 +--- a/units/initrd-switch-root.service.in ++++ b/units/initrd-switch-root.service.in +@@ -17,10 +17,4 @@ AllowIsolate=yes + Type=oneshot + # we have to use "--force" here, otherwise systemd would umount /run + ExecStart=@rootbindir@/systemctl --no-block --force switch-root /sysroot +- +-# Just before switching to the new rootfs, systemd might send us a TERM signal +-# depending on how fast we are to execute the main command and exit. If we get +-# the SIGTERM signal that simply means that we succeed but haven't got enough +-# time to exit properly. Since systemd considers SIGTERM as a failure for +-# short-running process (aka Type=oneshot), instruct it to ignore this case. +-SuccessExitStatus=SIGTERM ++KillMode=none -- -2.11.0 +2.9.3 diff --git a/0017-fix-journald-startup-problem-when-code-is-compiled-w.patch b/0017-fix-journald-startup-problem-when-code-is-compiled-w.patch new file mode 100644 index 0000000..cbea6d3 --- /dev/null +++ b/0017-fix-journald-startup-problem-when-code-is-compiled-w.patch @@ -0,0 +1,33 @@ +From 95bb77653fe45f8865bae23c46bd1b25f14a35dc Mon Sep 17 00:00:00 2001 +From: Waldemar Brodkorb +Date: Fri, 25 Nov 2016 11:24:58 +0100 +Subject: [PATCH] fix journald startup problem when code is compiled with + -DNDEBUG (#4735) + +Similar to this patch from here: +http://systemd-devel.freedesktop.narkive.com/AvfCbi6c/patch-0-3-using-assert-se-on-actions-with-side-effects-on-test-cases + +If the code is compiled with -DNDEBUG which is the default for +some embedded buildsystems, systemd-journald does not startup +and silently fails. +(cherry picked from commit 9bab3b65b0ad625ebbf50583c46f5c7c35b18a70) +--- + src/journal/journald-server.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c +index 908c7b8eeb..c0987625cd 100644 +--- a/src/journal/journald-server.c ++++ b/src/journal/journald-server.c +@@ -1480,7 +1480,7 @@ static int setup_signals(Server *s) { + + assert(s); + +- assert(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGRTMIN+1, -1) >= 0); ++ assert_se(sigprocmask_many(SIG_SETMASK, NULL, SIGINT, SIGTERM, SIGUSR1, SIGUSR2, SIGRTMIN+1, -1) >= 0); + + r = sd_event_add_signal(s->event, &s->sigusr1_event_source, SIGUSR1, dispatch_sigusr1, s); + if (r < 0) +-- +2.9.3 + diff --git a/0018-device-Avoid-calling-unit_free-NULL-in-device-setup-.patch b/0018-device-Avoid-calling-unit_free-NULL-in-device-setup-.patch new file mode 100644 index 0000000..3ff7458 --- /dev/null +++ b/0018-device-Avoid-calling-unit_free-NULL-in-device-setup-.patch @@ -0,0 +1,36 @@ +From 23c99602bc227de3a4104cb01b655ae6d034e853 Mon Sep 17 00:00:00 2001 +From: Dave Reisner +Date: Sun, 27 Nov 2016 17:05:39 -0500 +Subject: [PATCH] device: Avoid calling unit_free(NULL) in device setup logic + (#4748) + +Since a581e45ae8f9bb5c, there's a few function calls to +unit_new_for_name which will unit_free on failure. Prior to this commit, +a failure would result in calling unit_free with a NULL unit, and hit an +assertion failure, seen at least via device_setup_unit: + +Assertion 'u' failed at src/core/unit.c:519, function unit_free(). Aborting. + +Fixes #4747 +https://bugs.archlinux.org/task/51950 +(cherry picked from commit d112eae7da77899be245ab52aa1747d4675549f1) +--- + src/core/device.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/device.c b/src/core/device.c +index bd87a447cd..4b9e84aeb6 100644 +--- a/src/core/device.c ++++ b/src/core/device.c +@@ -365,7 +365,7 @@ static int device_setup_unit(Manager *m, struct udev_device *dev, const char *pa + fail: + log_unit_warning_errno(u, r, "Failed to set up device unit: %m"); + +- if (delete) ++ if (delete && u) + unit_free(u); + + return r; +-- +2.9.3 + diff --git a/0019-udevd-check-correct-return-value-of-fcntl-4758.patch b/0019-udevd-check-correct-return-value-of-fcntl-4758.patch new file mode 100644 index 0000000..559fea8 --- /dev/null +++ b/0019-udevd-check-correct-return-value-of-fcntl-4758.patch @@ -0,0 +1,27 @@ +From f20e908529c7005fdc31c9e4e694b8c39291d10d Mon Sep 17 00:00:00 2001 +From: Daniel Wagner +Date: Mon, 28 Nov 2016 18:24:26 +0100 +Subject: [PATCH] udevd: check correct return value of fcntl() (#4758) + +This looks like a copy&paste error from the code block above. +(cherry picked from commit a92cf7840ffefc279bc5cc409197d13752dec6e7) +--- + src/udev/udevd.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/udev/udevd.c b/src/udev/udevd.c +index badbab6205..c97c52e962 100644 +--- a/src/udev/udevd.c ++++ b/src/udev/udevd.c +@@ -1345,7 +1345,7 @@ static int listen_fds(int *rctrl, int *rnetlink) { + return log_error_errno(netlink_fd, "could not get uevent fd: %m"); + + netlink_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3); +- if (ctrl_fd < 0) ++ if (netlink_fd < 0) + return log_error_errno(errno, "could not dup netlink fd: %m"); + } + +-- +2.9.3 + diff --git a/0020-systemctl-fix-is-enabled-exit-status-on-failure-when.patch b/0020-systemctl-fix-is-enabled-exit-status-on-failure-when.patch new file mode 100644 index 0000000..c520508 --- /dev/null +++ b/0020-systemctl-fix-is-enabled-exit-status-on-failure-when.patch @@ -0,0 +1,27 @@ +From fe2621a4b37b6138bd0734d36e786050799e6e48 Mon Sep 17 00:00:00 2001 +From: Franck Bui +Date: Wed, 30 Nov 2016 18:27:42 +0100 +Subject: [PATCH] systemctl: fix 'is-enabled' exit status on failure when + executed in chroot (#4773) + +(cherry picked from commit c5024cd05c194b93ae960bf38e567d3d998f2a03) +--- + src/systemctl/systemctl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index a2b667481d..4edc690f5c 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -6384,7 +6384,7 @@ static int unit_is_enabled(int argc, char *argv[], void *userdata) { + + r = unit_file_get_state(arg_scope, arg_root, *name, &state); + if (r < 0) +- return log_error_errno(state, "Failed to get unit file state for %s: %m", *name); ++ return log_error_errno(r, "Failed to get unit file state for %s: %m", *name); + + if (IN_SET(state, + UNIT_FILE_ENABLED, +-- +2.9.3 + diff --git a/0021-journal-make-sure-to-initially-populate-the-space-in.patch b/0021-journal-make-sure-to-initially-populate-the-space-in.patch new file mode 100644 index 0000000..7175a73 --- /dev/null +++ b/0021-journal-make-sure-to-initially-populate-the-space-in.patch @@ -0,0 +1,33 @@ +From 7ad53973a94ab6699e52305501362e26af159fd2 Mon Sep 17 00:00:00 2001 +From: Franck Bui +Date: Fri, 2 Dec 2016 18:40:10 +0100 +Subject: [PATCH] journal: make sure to initially populate the space info cache + (#4807) + +Make sure to populate the cache in cache_space_refresh() at least once +otherwise it's possible that the system boots fast enough (and the journal +flush service is finished) before the invalidate cache timeout (30 us) has +expired. + +Fixes: #4790 +(cherry picked from commit 3099caf2b5bb9498b1d0227c40926435ca81f26f) +--- + src/journal/journald-server.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c +index c0987625cd..1d2fce8dc7 100644 +--- a/src/journal/journald-server.c ++++ b/src/journal/journald-server.c +@@ -144,7 +144,7 @@ static int cache_space_refresh(Server *s, JournalStorage *storage) { + + ts = now(CLOCK_MONOTONIC); + +- if (space->timestamp + RECHECK_SPACE_USEC > ts) ++ if (space->timestamp != 0 && space->timestamp + RECHECK_SPACE_USEC > ts) + return 0; + + r = determine_path_usage(s, storage->path, &vfs_used, &vfs_avail); +-- +2.9.3 + diff --git a/0022-networkd-link_enter_configured-remove-assert-4800.patch b/0022-networkd-link_enter_configured-remove-assert-4800.patch new file mode 100644 index 0000000..73e1b56 --- /dev/null +++ b/0022-networkd-link_enter_configured-remove-assert-4800.patch @@ -0,0 +1,50 @@ +From 6e6a4e9b53c8a9a4efd3a22c8496f5b1d840c969 Mon Sep 17 00:00:00 2001 +From: Susant Sahani +Date: Mon, 5 Dec 2016 20:01:48 +0530 +Subject: [PATCH] networkd: link_enter_configured remove assert (#4800) + +When we are in link_enter_configured we assume that the +link->state should be LINK_STATE_SETTING_ROUTES but in some +situation it's LINK_STATlE_SETTING_ADDRESSES. + +Just ignore the wrong state. + +Also since the return value not used any where +make link_enter_configured return type void. + +Fixes: #4746 +(cherry picked from commit e3a7b04848a438f7b5a9a2bf5d7e0e59c5c49a00) +--- + src/network/networkd-link.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c +index aefe7335b9..4dfbe0bf7e 100644 +--- a/src/network/networkd-link.c ++++ b/src/network/networkd-link.c +@@ -686,18 +686,18 @@ static Address* link_find_dhcp_server_address(Link *link) { + return NULL; + } + +-static int link_enter_configured(Link *link) { ++static void link_enter_configured(Link *link) { + assert(link); + assert(link->network); +- assert(link->state == LINK_STATE_SETTING_ROUTES); ++ ++ if (link->state != LINK_STATE_SETTING_ROUTES) ++ return; + + log_link_info(link, "Configured"); + + link_set_state(link, LINK_STATE_CONFIGURED); + + link_dirty(link); +- +- return 0; + } + + void link_check_ready(Link *link) { +-- +2.9.3 + diff --git a/0023-rules-consider-MMC-device-partitions-with-partition-.patch b/0023-rules-consider-MMC-device-partitions-with-partition-.patch new file mode 100644 index 0000000..6700e0a --- /dev/null +++ b/0023-rules-consider-MMC-device-partitions-with-partition-.patch @@ -0,0 +1,28 @@ +From d51c2d12dc965fbde394e56a1a3484793c72ef1f Mon Sep 17 00:00:00 2001 +From: Kieran Colford +Date: Tue, 6 Dec 2016 04:46:13 -0500 +Subject: [PATCH] rules: consider MMC device partitions with partition number > + 9 (#4831) + +Add entries for extra partitions found on MMC devices (common in Chromebooks). +(cherry picked from commit 471b9850ee10dea07233af485e125897d2d35a00) +--- + rules/60-persistent-storage.rules | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/rules/60-persistent-storage.rules b/rules/60-persistent-storage.rules +index c13d05cdb1..bbd52e825e 100644 +--- a/rules/60-persistent-storage.rules ++++ b/rules/60-persistent-storage.rules +@@ -54,7 +54,7 @@ KERNEL=="sd*[0-9]", ATTRS{ieee1394_id}=="?*", SYMLINK+="disk/by-id/ieee1394-$att + # MMC + KERNEL=="mmcblk[0-9]", SUBSYSTEMS=="mmc", ATTRS{name}=="?*", ATTRS{serial}=="?*", \ + ENV{ID_NAME}="$attr{name}", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}" +-KERNEL=="mmcblk[0-9]p[0-9]", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}-part%n" ++KERNEL=="mmcblk[0-9]p[0-9]*", ENV{ID_NAME}=="?*", ENV{ID_SERIAL}=="?*", SYMLINK+="disk/by-id/mmc-$env{ID_NAME}_$env{ID_SERIAL}-part%n" + + # Memstick + KERNEL=="msblk[0-9]|mspblk[0-9]", SUBSYSTEMS=="memstick", ATTRS{name}=="?*", ATTRS{serial}=="?*", \ +-- +2.9.3 + diff --git a/0024-nspawn-add-missing-E-to-getopt_long-4860.patch b/0024-nspawn-add-missing-E-to-getopt_long-4860.patch new file mode 100644 index 0000000..60f2387 --- /dev/null +++ b/0024-nspawn-add-missing-E-to-getopt_long-4860.patch @@ -0,0 +1,26 @@ +From de52d82792835bb4f81802dc4cab6dc9c031f154 Mon Sep 17 00:00:00 2001 +From: Wim de With +Date: Sat, 10 Dec 2016 05:33:58 +0100 +Subject: [PATCH] nspawn: add missing -E to getopt_long (#4860) + +(cherry picked from commit 2e1f244efd2dfc1a60d032bef3d88b9ba6e0444b) +--- + src/nspawn/nspawn.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c +index c8fc011907..81573be95a 100644 +--- a/src/nspawn/nspawn.c ++++ b/src/nspawn/nspawn.c +@@ -472,7 +472,7 @@ static int parse_argv(int argc, char *argv[]) { + assert(argc >= 0); + assert(argv); + +- while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:nU", options, NULL)) >= 0) ++ while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:nUE:", options, NULL)) >= 0) + + switch (c) { + +-- +2.9.3 + diff --git a/0025-build-sys-define-arm-as-secondary-architecture-for-a.patch b/0025-build-sys-define-arm-as-secondary-architecture-for-a.patch new file mode 100644 index 0000000..7e133aa --- /dev/null +++ b/0025-build-sys-define-arm-as-secondary-architecture-for-a.patch @@ -0,0 +1,27 @@ +From 9dcc80915fc571f926aeeb5afae47aa2a03cb7d1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sat, 10 Dec 2016 13:01:22 -0500 +Subject: [PATCH] build-sys: define arm as secondary architecture for arm64 + +Completely unstested. Fixes #4862. + +(cherry picked from commit b7cac52779d52eff6ed03103922ffe1208a7baa9) +--- + src/basic/architecture.h | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/basic/architecture.h b/src/basic/architecture.h +index 5a77c31932..b329df2f6d 100644 +--- a/src/basic/architecture.h ++++ b/src/basic/architecture.h +@@ -150,6 +150,7 @@ int uname_architecture(void); + # else + # define native_architecture() ARCHITECTURE_ARM64 + # define LIB_ARCH_TUPLE "aarch64-linux-gnu" ++# define SECONDARY_ARCHITECTURE ARCHITECTURE_ARM + # endif + #elif defined(__arm__) + # if __BYTE_ORDER == __BIG_ENDIAN +-- +2.9.3 + diff --git a/0026-nspawn-when-getting-SIGCHLD-make-sure-it-s-from-the-.patch b/0026-nspawn-when-getting-SIGCHLD-make-sure-it-s-from-the-.patch new file mode 100644 index 0000000..3f74d27 --- /dev/null +++ b/0026-nspawn-when-getting-SIGCHLD-make-sure-it-s-from-the-.patch @@ -0,0 +1,78 @@ +From 7046c0424a0c521169f258ab02d90739dade6269 Mon Sep 17 00:00:00 2001 +From: Andrey Ulanov +Date: Mon, 12 Dec 2016 17:38:18 -0800 +Subject: [PATCH] nspawn: when getting SIGCHLD make sure it's from the first + child (#4855) + +When getting SIGCHLD we should not assume that it was the first +child forked from system-nspawn that has died as it may also be coming +from an orphan process. This change adds a signal handler that ignores +SIGCHLD unless it came from the first containerized child - the real +child. + +Before this change the problem can be reproduced as follows: + +$ sudo systemd-nspawn --directory=/container-root --share-system +Press ^] three times within 1s to kill container. +[root@andreyu-coreos ~]# { true & } & +[1] 22201 +[root@andreyu-coreos ~]# +Container root-fedora-latest terminated by signal KILL +(cherry picked from commit 6916b164642d8bb4938878f4284f8ee5ccf3efd6) +--- + src/nspawn/nspawn.c | 25 +++++++++++++++++++++++-- + 1 file changed, 23 insertions(+), 2 deletions(-) + +diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c +index 81573be95a..503265545b 100644 +--- a/src/nspawn/nspawn.c ++++ b/src/nspawn/nspawn.c +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + #include + + #include "sd-daemon.h" +@@ -2538,6 +2539,26 @@ static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo + return 0; + } + ++static int on_sigchld(sd_event_source *s, const struct signalfd_siginfo *ssi, void *userdata) { ++ for (;;) { ++ siginfo_t si = {}; ++ if (waitid(P_ALL, 0, &si, WNOHANG|WNOWAIT|WEXITED) < 0) ++ return log_error_errno(errno, "Failed to waitid(): %m"); ++ if (si.si_pid == 0) /* No pending children. */ ++ break; ++ if (si.si_pid == PTR_TO_PID(userdata)) { ++ /* The main process we care for has exited. Return from ++ * signal handler but leave the zombie. */ ++ sd_event_exit(sd_event_source_get_event(s), 0); ++ break; ++ } ++ /* Reap all other children. */ ++ (void) waitid(P_PID, si.si_pid, &si, WNOHANG|WEXITED); ++ } ++ ++ return 0; ++} ++ + static int determine_names(void) { + int r; + +@@ -3952,8 +3973,8 @@ static int run(int master, + sd_event_add_signal(event, NULL, SIGTERM, NULL, NULL); + } + +- /* simply exit on sigchld */ +- sd_event_add_signal(event, NULL, SIGCHLD, NULL, NULL); ++ /* Exit when the child exits */ ++ sd_event_add_signal(event, NULL, SIGCHLD, on_sigchld, PID_TO_PTR(*pid)); + + if (arg_expose_ports) { + r = expose_port_watch_rtnl(event, rtnl_socket_pair[0], on_address_change, exposed, &rtnl); +-- +2.9.3 + diff --git a/0027-machinectl-make-machinectl-E-shell-work.patch b/0027-machinectl-make-machinectl-E-shell-work.patch new file mode 100644 index 0000000..9416c19 --- /dev/null +++ b/0027-machinectl-make-machinectl-E-shell-work.patch @@ -0,0 +1,31 @@ +From 60f9c0937c0a24b7d1e4147ec4354d26bf6b092b Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 6 Dec 2016 19:35:31 +0100 +Subject: [PATCH] =?UTF-8?q?machinectl:=20make=20"machinectl=20-E=20?= + =?UTF-8?q?=E2=80=A6=20shell"=20work?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Fixes: #4823 +(cherry picked from commit 61f638e5446d0d4a5b5e7f81c174e4f072bd01f7) +--- + src/machine/machinectl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c +index 7b9be3b425..d54caecf46 100644 +--- a/src/machine/machinectl.c ++++ b/src/machine/machinectl.c +@@ -2756,7 +2756,7 @@ static int parse_argv(int argc, char *argv[]) { + assert(argv); + + for (;;) { +- static const char option_string[] = "-hp:als:H:M:qn:o:"; ++ static const char option_string[] = "-hp:als:H:M:qn:o:E:"; + + c = getopt_long(argc, argv, option_string + reorder, options, NULL); + if (c < 0) +-- +2.9.3 + diff --git a/0028-sysv-generator-properly-translate-sysv-facilities.patch b/0028-sysv-generator-properly-translate-sysv-facilities.patch new file mode 100644 index 0000000..f08d302 --- /dev/null +++ b/0028-sysv-generator-properly-translate-sysv-facilities.patch @@ -0,0 +1,57 @@ +From 827c431cad554cd4d5c477b0f6e608e36edb1301 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 6 Dec 2016 19:36:30 +0100 +Subject: [PATCH] sysv-generator: properly translate sysv facilities + +We used the wrong return value in one case, so that our translations were +thrown away. + +While we are at it, make sure to always initialize *ret on successful function +exits. + +Fixes: #4762 +(cherry picked from commit e932f5407ef5ad05d25d7dfefa4cda0fe81cc346) +--- + src/sysv-generator/sysv-generator.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c +index c2c80175a2..212cf7a988 100644 +--- a/src/sysv-generator/sysv-generator.c ++++ b/src/sysv-generator/sysv-generator.c +@@ -292,8 +292,10 @@ static int sysv_translate_facility(SysvStub *s, unsigned line, const char *name, + if (!streq(table[i], n)) + continue; + +- if (!table[i+1]) ++ if (!table[i+1]) { ++ *ret = NULL; + return 0; ++ } + + m = strdup(table[i+1]); + if (!m) +@@ -312,7 +314,7 @@ static int sysv_translate_facility(SysvStub *s, unsigned line, const char *name, + if (r < 0) + return log_error_errno(r, "[%s:%u] Could not build name for facility %s: %m", s->path, line, name); + +- return r; ++ return 1; + } + + /* Strip ".sh" suffix from file name for comparison */ +@@ -324,8 +326,10 @@ static int sysv_translate_facility(SysvStub *s, unsigned line, const char *name, + } + + /* Names equaling the file name of the services are redundant */ +- if (streq_ptr(n, filename)) ++ if (streq_ptr(n, filename)) { ++ *ret = NULL; + return 0; ++ } + + /* Everything else we assume to be normal service names */ + m = sysv_translate_name(n); +-- +2.9.3 + diff --git a/0029-core-downgrade-Time-has-been-changed-to-debug-4906.patch b/0029-core-downgrade-Time-has-been-changed-to-debug-4906.patch new file mode 100644 index 0000000..cf374f2 --- /dev/null +++ b/0029-core-downgrade-Time-has-been-changed-to-debug-4906.patch @@ -0,0 +1,43 @@ +From 9f4c52f375beeea987d4ca19920a8bb9d18e69ca Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sun, 18 Dec 2016 07:21:19 -0500 +Subject: [PATCH] core: downgrade "Time has been changed" to debug (#4906) + +That message is emitted by every systemd instance on every resume: +Dec 06 08:03:38 laptop systemd[1]: Time has been changed +Dec 06 08:03:38 laptop systemd[823]: Time has been changed +Dec 06 08:03:38 laptop systemd[916]: Time has been changed +Dec 07 08:00:32 laptop systemd[1]: Time has been changed +Dec 07 08:00:32 laptop systemd[823]: Time has been changed +Dec 07 08:00:32 laptop systemd[916]: Time has been changed +-- Reboot -- +Dec 07 08:02:46 laptop systemd[836]: Time has been changed +Dec 07 08:02:46 laptop systemd[1]: Time has been changed +Dec 07 08:02:46 laptop systemd[926]: Time has been changed +Dec 07 19:48:12 laptop systemd[1]: Time has been changed +Dec 07 19:48:12 laptop systemd[836]: Time has been changed +Dec 07 19:48:12 laptop systemd[926]: Time has been changed +... + +Fixes #4896. +(cherry picked from commit a80c1575065c3e3cbf97fd97993ff98598fa01bb) +--- + src/core/manager.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/core/manager.c b/src/core/manager.c +index ffccfdcd5e..322e69a69c 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -2145,7 +2145,7 @@ static int manager_dispatch_time_change_fd(sd_event_source *source, int fd, uint + assert(m); + assert(m->time_change_fd == fd); + +- log_struct(LOG_INFO, ++ log_struct(LOG_DEBUG, + LOG_MESSAGE_ID(SD_MESSAGE_TIME_CHANGE), + LOG_MESSAGE("Time has been changed"), + NULL); +-- +2.9.3 + diff --git a/0030-machinectl-handle-EOPNOTSUPP-from-print_addresses-49.patch b/0030-machinectl-handle-EOPNOTSUPP-from-print_addresses-49.patch new file mode 100644 index 0000000..b69d670 --- /dev/null +++ b/0030-machinectl-handle-EOPNOTSUPP-from-print_addresses-49.patch @@ -0,0 +1,28 @@ +From f8b6aec183d393c6932c0ac75c106646cd33d3af Mon Sep 17 00:00:00 2001 +From: Graeme Lawes +Date: Sun, 25 Dec 2016 06:14:41 -0500 +Subject: [PATCH] machinectl: handle EOPNOTSUPP from print_addresses (#4979) + +Print addresses returns EOPNOTSUPP, not ENOSYS, when trying to print +addresses for non-container machines. +(cherry picked from commit 3a0a40d1667e53b623e891527871e9f3ee6cb7b4) +--- + src/machine/machinectl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c +index d54caecf46..f84226f58f 100644 +--- a/src/machine/machinectl.c ++++ b/src/machine/machinectl.c +@@ -326,7 +326,7 @@ static int list_machines(int argc, char *argv[], void *userdata) { + (int) max_version_id, strdash_if_empty(machines[j].version_id)); + + r = print_addresses(bus, machines[j].name, 0, "", prefix, arg_addrs); +- if (r == -ENOSYS) ++ if (r == -EOPNOTSUPP) + printf("-\n"); + } + +-- +2.9.3 + diff --git a/0031-units-fix-condition-for-systemd-journal-catalog-upda.patch b/0031-units-fix-condition-for-systemd-journal-catalog-upda.patch new file mode 100644 index 0000000..96e61e1 --- /dev/null +++ b/0031-units-fix-condition-for-systemd-journal-catalog-upda.patch @@ -0,0 +1,30 @@ +From b089ddffd2eef138a80849f6cdb35c330747ad27 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 29 Dec 2016 10:38:52 +0100 +Subject: [PATCH] units: fix condition for + systemd-journal-catalog-update.service (#4990) + +The service is supposed to regenerate the catalog index whenever /usr is +updated, but /var is not. Hence the ConditionNeedsUpdate= line should +actually reference /var, as that's where the index file is located. +(cherry picked from commit 73c729d76871a64840a4d89fc61a5a64cc96e596) +--- + units/systemd-journal-catalog-update.service.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/units/systemd-journal-catalog-update.service.in b/units/systemd-journal-catalog-update.service.in +index 6370dd478f..276f052b1a 100644 +--- a/units/systemd-journal-catalog-update.service.in ++++ b/units/systemd-journal-catalog-update.service.in +@@ -12,7 +12,7 @@ DefaultDependencies=no + Conflicts=shutdown.target + After=local-fs.target + Before=sysinit.target shutdown.target systemd-update-done.service +-ConditionNeedsUpdate=/etc ++ConditionNeedsUpdate=/var + + [Service] + Type=oneshot +-- +2.9.3 + diff --git a/0032-core-fix-sockaddr-length-calculation-for-sockaddr_pr.patch b/0032-core-fix-sockaddr-length-calculation-for-sockaddr_pr.patch new file mode 100644 index 0000000..19c3c52 --- /dev/null +++ b/0032-core-fix-sockaddr-length-calculation-for-sockaddr_pr.patch @@ -0,0 +1,60 @@ +From 93836ed31bf91d10667b67f445af374c2e513bae Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Thu, 29 Dec 2016 11:21:37 +0100 +Subject: [PATCH] core: fix sockaddr length calculation for sockaddr_pretty() + (#4966) + +Let's simply store the socket address length in the SocketPeer object so +that we can use it when invoking sockaddr_pretty(): + +This fixes the issue described in #4943, but avoids calling +getpeername() twice. +(cherry picked from commit 41733ae1e0035c538505bc5be1ca5d67a80b4a82) +--- + src/core/socket.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/src/core/socket.c b/src/core/socket.c +index 0b1c4acfec..fe2020008f 100644 +--- a/src/core/socket.c ++++ b/src/core/socket.c +@@ -64,6 +64,7 @@ struct SocketPeer { + + Socket *socket; + union sockaddr_union peer; ++ socklen_t peer_salen; + }; + + static const UnitActiveState state_translation_table[_SOCKET_STATE_MAX] = { +@@ -490,8 +491,10 @@ static void peer_address_hash_func(const void *p, struct siphash *state) { + + if (s->peer.sa.sa_family == AF_INET) + siphash24_compress(&s->peer.in.sin_addr, sizeof(s->peer.in.sin_addr), state); +- else ++ else if (s->peer.sa.sa_family == AF_INET6) + siphash24_compress(&s->peer.in6.sin6_addr, sizeof(s->peer.in6.sin6_addr), state); ++ else ++ assert_not_reached("Unknown address family."); + } + + static int peer_address_compare_func(const void *a, const void *b) { +@@ -609,6 +612,7 @@ int socket_acquire_peer(Socket *s, int fd, SocketPeer **p) { + return log_oom(); + + remote->peer = sa.peer; ++ remote->peer_salen = salen; + + r = set_put(s->peers_by_address, remote); + if (r < 0) +@@ -2196,7 +2200,7 @@ static void socket_enter_running(Socket *s, int cfd) { + } else if (r > 0 && p->n_ref > s->max_connections_per_source) { + _cleanup_free_ char *t = NULL; + +- sockaddr_pretty(&p->peer.sa, FAMILY_ADDRESS_SIZE(p->peer.sa.sa_family), true, false, &t); ++ (void) sockaddr_pretty(&p->peer.sa, p->peer_salen, true, false, &t); + + log_unit_warning(UNIT(s), + "Too many incoming connections (%u) from source %s, dropping connection.", +-- +2.9.3 + diff --git a/0033-shared-fix-double-free-in-unmask-5005.patch b/0033-shared-fix-double-free-in-unmask-5005.patch new file mode 100644 index 0000000..2c43e5c --- /dev/null +++ b/0033-shared-fix-double-free-in-unmask-5005.patch @@ -0,0 +1,43 @@ +From ff9411f6d3f63e765bf29853b920605965a16794 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Tue, 3 Jan 2017 21:34:36 +0100 +Subject: [PATCH] shared: fix double free in unmask (#5005) + +Easily reproducible: +1) systemctl mask foo +2) systemctl unmask foo foo + +The problem here is that the *i that is put into todo[] is later freed +in strv_uniq(), which is not directly visible from this patch. Somewhere +further in the code, the string that *i pointed to is freed again. That +happens only when multiple services with the same name/path are specified. +(cherry picked from commit dc7dd61de610e9330abe7014860acfa733887d5e) +--- + src/shared/install.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/shared/install.c b/src/shared/install.c +index 96fba6e25b..5f0eec3ccb 100644 +--- a/src/shared/install.c ++++ b/src/shared/install.c +@@ -1861,7 +1861,7 @@ int unit_file_unmask( + + _cleanup_lookup_paths_free_ LookupPaths paths = {}; + _cleanup_set_free_free_ Set *remove_symlinks_to = NULL; +- _cleanup_free_ char **todo = NULL; ++ _cleanup_strv_free_ char **todo = NULL; + size_t n_todo = 0, n_allocated = 0; + const char *config_path; + char **i; +@@ -1899,7 +1899,7 @@ int unit_file_unmask( + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + +- todo[n_todo++] = *i; ++ todo[n_todo++] = strdup(*i); + } + + strv_uniq(todo); +-- +2.9.3 + diff --git a/0034-shared-fix-double-free-in-link.patch b/0034-shared-fix-double-free-in-link.patch new file mode 100644 index 0000000..dbf26e6 --- /dev/null +++ b/0034-shared-fix-double-free-in-link.patch @@ -0,0 +1,120 @@ +From 588e4827d274279941533a9708101835b2d2e986 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Mon, 9 Jan 2017 04:46:11 +0000 +Subject: [PATCH] shared: fix double free in link + +Fixes: +``` +touch hola.service +systemctl link $(pwd)/hola.service $(pwd)/hola.service +``` + +``` +==1==ERROR: AddressSanitizer: attempting double-free on 0x60300002c560 in thread T0 (systemd): + #0 0x7fc8c961cb00 in free (/lib64/libasan.so.3+0xc6b00) + #1 0x7fc8c90ebd3b in strv_clear src/basic/strv.c:83 + #2 0x7fc8c90ebdb6 in strv_free src/basic/strv.c:89 + #3 0x55637c758c77 in strv_freep src/basic/strv.h:37 + #4 0x55637c763ba9 in method_enable_unit_files_generic src/core/dbus-manager.c:1960 + #5 0x55637c763d16 in method_link_unit_files src/core/dbus-manager.c:2001 + #6 0x7fc8c92537ec in method_callbacks_run src/libsystemd/sd-bus/bus-objects.c:418 + #7 0x7fc8c9258830 in object_find_and_run src/libsystemd/sd-bus/bus-objects.c:1255 + #8 0x7fc8c92594d7 in bus_process_object src/libsystemd/sd-bus/bus-objects.c:1371 + #9 0x7fc8c91e7553 in process_message src/libsystemd/sd-bus/sd-bus.c:2563 + #10 0x7fc8c91e78ce in process_running src/libsystemd/sd-bus/sd-bus.c:2605 + #11 0x7fc8c91e8f61 in bus_process_internal src/libsystemd/sd-bus/sd-bus.c:2837 + #12 0x7fc8c91e90d2 in sd_bus_process src/libsystemd/sd-bus/sd-bus.c:2856 + #13 0x7fc8c91ea8f9 in io_callback src/libsystemd/sd-bus/sd-bus.c:3126 + #14 0x7fc8c928333b in source_dispatch src/libsystemd/sd-event/sd-event.c:2268 + #15 0x7fc8c9285cf7 in sd_event_dispatch src/libsystemd/sd-event/sd-event.c:2627 + #16 0x7fc8c92865fa in sd_event_run src/libsystemd/sd-event/sd-event.c:2686 + #17 0x55637c6b5257 in manager_loop src/core/manager.c:2274 + #18 0x55637c6a2194 in main src/core/main.c:1920 + #19 0x7fc8c7ac7400 in __libc_start_main (/lib64/libc.so.6+0x20400) + #20 0x55637c697339 in _start (/usr/lib/systemd/systemd+0xcd339) + +0x60300002c560 is located 0 bytes inside of 19-byte region [0x60300002c560,0x60300002c573) +freed by thread T0 (systemd) here: + #0 0x7fc8c961cb00 in free (/lib64/libasan.so.3+0xc6b00) + #1 0x7fc8c90ee320 in strv_remove src/basic/strv.c:630 + #2 0x7fc8c90ee190 in strv_uniq src/basic/strv.c:602 + #3 0x7fc8c9180533 in unit_file_link src/shared/install.c:1996 + #4 0x55637c763b25 in method_enable_unit_files_generic src/core/dbus-manager.c:1985 + #5 0x55637c763d16 in method_link_unit_files src/core/dbus-manager.c:2001 + #6 0x7fc8c92537ec in method_callbacks_run src/libsystemd/sd-bus/bus-objects.c:418 + #7 0x7fc8c9258830 in object_find_and_run src/libsystemd/sd-bus/bus-objects.c:1255 + #8 0x7fc8c92594d7 in bus_process_object src/libsystemd/sd-bus/bus-objects.c:1371 + #9 0x7fc8c91e7553 in process_message src/libsystemd/sd-bus/sd-bus.c:2563 + #10 0x7fc8c91e78ce in process_running src/libsystemd/sd-bus/sd-bus.c:2605 + #11 0x7fc8c91e8f61 in bus_process_internal src/libsystemd/sd-bus/sd-bus.c:2837 + #12 0x7fc8c91e90d2 in sd_bus_process src/libsystemd/sd-bus/sd-bus.c:2856 + #13 0x7fc8c91ea8f9 in io_callback src/libsystemd/sd-bus/sd-bus.c:3126 + #14 0x7fc8c928333b in source_dispatch src/libsystemd/sd-event/sd-event.c:2268 + #15 0x7fc8c9285cf7 in sd_event_dispatch src/libsystemd/sd-event/sd-event.c:2627 + #16 0x7fc8c92865fa in sd_event_run src/libsystemd/sd-event/sd-event.c:2686 + #17 0x55637c6b5257 in manager_loop src/core/manager.c:2274 + #18 0x55637c6a2194 in main src/core/main.c:1920 + #19 0x7fc8c7ac7400 in __libc_start_main (/lib64/libc.so.6+0x20400) + +previously allocated by thread T0 (systemd) here: + #0 0x7fc8c95b0160 in strdup (/lib64/libasan.so.3+0x5a160) + #1 0x7fc8c90edf32 in strv_extend src/basic/strv.c:552 + #2 0x7fc8c923ae41 in bus_message_read_strv_extend src/libsystemd/sd-bus/bus-message.c:5578 + #3 0x7fc8c923b0de in sd_bus_message_read_strv src/libsystemd/sd-bus/bus-message.c:5600 + #4 0x55637c7639d1 in method_enable_unit_files_generic src/core/dbus-manager.c:1969 + #5 0x55637c763d16 in method_link_unit_files src/core/dbus-manager.c:2001 + #6 0x7fc8c92537ec in method_callbacks_run src/libsystemd/sd-bus/bus-objects.c:418 + #7 0x7fc8c9258830 in object_find_and_run src/libsystemd/sd-bus/bus-objects.c:1255 + #8 0x7fc8c92594d7 in bus_process_object src/libsystemd/sd-bus/bus-objects.c:1371 + #9 0x7fc8c91e7553 in process_message src/libsystemd/sd-bus/sd-bus.c:2563 + #10 0x7fc8c91e78ce in process_running src/libsystemd/sd-bus/sd-bus.c:2605 + #11 0x7fc8c91e8f61 in bus_process_internal src/libsystemd/sd-bus/sd-bus.c:2837 + #12 0x7fc8c91e90d2 in sd_bus_process src/libsystemd/sd-bus/sd-bus.c:2856 + #13 0x7fc8c91ea8f9 in io_callback src/libsystemd/sd-bus/sd-bus.c:3126 + #14 0x7fc8c928333b in source_dispatch src/libsystemd/sd-event/sd-event.c:2268 + #15 0x7fc8c9285cf7 in sd_event_dispatch src/libsystemd/sd-event/sd-event.c:2627 + #16 0x7fc8c92865fa in sd_event_run src/libsystemd/sd-event/sd-event.c:2686 + #17 0x55637c6b5257 in manager_loop src/core/manager.c:2274 + #18 0x55637c6a2194 in main src/core/main.c:1920 + #19 0x7fc8c7ac7400 in __libc_start_main (/lib64/libc.so.6+0x20400) + +SUMMARY: AddressSanitizer: double-free (/lib64/libasan.so.3+0xc6b00) in free +==1==ABORTING +``` + +Closes #5015 + +(cherry picked from commit 8af35ba681116eb79a46e3dbd65b166c1efd6164) +--- + src/shared/install.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/src/shared/install.c b/src/shared/install.c +index 5f0eec3ccb..64fe522ebb 100644 +--- a/src/shared/install.c ++++ b/src/shared/install.c +@@ -1947,7 +1947,7 @@ int unit_file_link( + unsigned *n_changes) { + + _cleanup_lookup_paths_free_ LookupPaths paths = {}; +- _cleanup_free_ char **todo = NULL; ++ _cleanup_strv_free_ char **todo = NULL; + size_t n_todo = 0, n_allocated = 0; + const char *config_path; + char **i; +@@ -1996,7 +1996,11 @@ int unit_file_link( + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + +- todo[n_todo++] = *i; ++ todo[n_todo] = strdup(*i); ++ if (!todo[n_todo]) ++ return -ENOMEM; ++ ++ n_todo++; + } + + strv_uniq(todo); +-- +2.9.3 + diff --git a/0035-shared-check-strdup-NULL.patch b/0035-shared-check-strdup-NULL.patch new file mode 100644 index 0000000..98295e4 --- /dev/null +++ b/0035-shared-check-strdup-NULL.patch @@ -0,0 +1,32 @@ +From a60dee7506c9f8895f3cdeea9dbf48f134072470 Mon Sep 17 00:00:00 2001 +From: Evgeny Vereshchagin +Date: Mon, 9 Jan 2017 22:45:41 +0000 +Subject: [PATCH] shared: check strdup != NULL + +This is a follow-up for dc7dd61de610e9330 + +(cherry picked from commit d054eae6c954baa857170bb60072c8a2ecea0d6b) +--- + src/shared/install.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/shared/install.c b/src/shared/install.c +index 64fe522ebb..0bb47ac527 100644 +--- a/src/shared/install.c ++++ b/src/shared/install.c +@@ -1899,7 +1899,11 @@ int unit_file_unmask( + if (!GREEDY_REALLOC0(todo, n_allocated, n_todo + 2)) + return -ENOMEM; + +- todo[n_todo++] = strdup(*i); ++ todo[n_todo] = strdup(*i); ++ if (!todo[n_todo]) ++ return -ENOMEM; ++ ++ n_todo++; + } + + strv_uniq(todo); +-- +2.9.3 + diff --git a/0036-rpm-triggers-do-nothing-if-systemd-is-not-running-50.patch b/0036-rpm-triggers-do-nothing-if-systemd-is-not-running-50.patch new file mode 100644 index 0000000..66f51a9 --- /dev/null +++ b/0036-rpm-triggers-do-nothing-if-systemd-is-not-running-50.patch @@ -0,0 +1,60 @@ +From b3ac083a877c6cd1153f7e99e7304e6eccfd6c8b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Thu, 12 Jan 2017 04:16:20 -0500 +Subject: [PATCH] rpm triggers: do nothing if systemd is not running (#5065) + +If we are running in a chroot/container/..., we would print a useless warning about +not being able to communicate with systemd. Trying to do daemon-reload is pointless +in those cases, so let's just skip all actions in that case. + +The check uses /run/system/system, as recommended by sd_booted(3). + +https://bugzilla.redhat.com/show_bug.cgi?id=1411299 +(cherry picked from commit 13749f547357f8e9ec398680548f818dea3aba1c) +--- + src/core/triggers.systemd.in | 22 +++++++++++++--------- + 1 file changed, 13 insertions(+), 9 deletions(-) + +diff --git a/src/core/triggers.systemd.in b/src/core/triggers.systemd.in +index 0d8c303136..f8c8cbc5f9 100644 +--- a/src/core/triggers.systemd.in ++++ b/src/core/triggers.systemd.in +@@ -27,11 +27,13 @@ + -- installed, because other cases are covered by the *un scriptlets, + -- so sometimes we will reload needlessly. + +-pid = posix.fork() +-if pid == 0 then +- assert(posix.exec("%{_bindir}/systemctl", "daemon-reload")) +-elseif pid > 0 then +- posix.wait(pid) ++if posix.access("/run/systemd/system") then ++ pid = posix.fork() ++ if pid == 0 then ++ assert(posix.exec("%{_bindir}/systemctl", "daemon-reload")) ++ elseif pid > 0 then ++ posix.wait(pid) ++ end + end + + %transfiletriggerun -p -- @systemunitdir@ /etc/systemd/system +@@ -48,10 +50,12 @@ end + -- file in %transfiletriggerun and execute the daemon-reload in + -- the first %filetriggerpostun. + +-posix.mkdir("%{_localstatedir}/lib") +-posix.mkdir("%{_localstatedir}/lib/rpm-state") +-posix.mkdir("%{_localstatedir}/lib/rpm-state/systemd") +-io.open("%{_localstatedir}/lib/rpm-state/systemd/needs-reload", "w") ++if posix.access("/run/systemd/system") then ++ posix.mkdir("%{_localstatedir}/lib") ++ posix.mkdir("%{_localstatedir}/lib/rpm-state") ++ posix.mkdir("%{_localstatedir}/lib/rpm-state/systemd") ++ io.open("%{_localstatedir}/lib/rpm-state/systemd/needs-reload", "w") ++end + + %filetriggerpostun -P 1000100 -p -- @systemunitdir@ /etc/systemd/system + if posix.access("%{_localstatedir}/lib/rpm-state/systemd/needs-reload") then +-- +2.9.3 + diff --git a/0003-kernel-install-avoid-process-substitution.patch b/0037-kernel-install-avoid-process-substitution.patch similarity index 91% rename from 0003-kernel-install-avoid-process-substitution.patch rename to 0037-kernel-install-avoid-process-substitution.patch index 6c5c725..1cc9ea6 100644 --- a/0003-kernel-install-avoid-process-substitution.patch +++ b/0037-kernel-install-avoid-process-substitution.patch @@ -1,4 +1,4 @@ -From 83a5e5d1f889744bedac8c9e813bf5d3d002a700 Mon Sep 17 00:00:00 2001 +From 5d98b45dac05bfde38c68d86e286bb8ce34bf932 Mon Sep 17 00:00:00 2001 From: Michal Schmidt Date: Fri, 16 Dec 2016 18:14:47 +0100 Subject: [PATCH] kernel-install: avoid process substitution @@ -9,6 +9,8 @@ scriptlets without /dev. We can use here-strings instead. bash uses temporary files to implement those. + +(cherry picked from commit db1e2bfc4f049261ae2d407568ff39569b36d98c) --- src/kernel-install/kernel-install | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/0038-shell-completion-redirect-all-errors-from-systemctl-.patch b/0038-shell-completion-redirect-all-errors-from-systemctl-.patch new file mode 100644 index 0000000..80ec35d --- /dev/null +++ b/0038-shell-completion-redirect-all-errors-from-systemctl-.patch @@ -0,0 +1,44 @@ +From da44d79b06cdfbcf77fb2e1c9ce8b826c2826748 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Wed, 11 Jan 2017 14:45:19 -0500 +Subject: [PATCH] shell-completion: redirect all errors from systemctl to + /dev/null + +Completion scripts should not generate errors, ever. + +https://bugzilla.redhat.com/show_bug.cgi?id=1409649 +(cherry picked from commit 99171d2fdf720ebb64fee75db4177af5d048d0f9) +--- + shell-completion/bash/systemctl.in | 2 +- + shell-completion/zsh/_systemctl.in | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in +index dcf71a1f51..34589e2888 100644 +--- a/shell-completion/bash/systemctl.in ++++ b/shell-completion/bash/systemctl.in +@@ -19,7 +19,7 @@ + + __systemctl() { + local mode=$1; shift 1 +- systemctl $mode --full --no-legend "$@" ++ systemctl $mode --full --no-legend "$@" 2>/dev/null + } + + __systemd_properties() { +diff --git a/shell-completion/zsh/_systemctl.in b/shell-completion/zsh/_systemctl.in +index 03a1c930b0..d77a2df74e 100644 +--- a/shell-completion/zsh/_systemctl.in ++++ b/shell-completion/zsh/_systemctl.in +@@ -91,7 +91,7 @@ + + __systemctl() + { +- systemctl $_sys_service_mgr --full --no-legend --no-pager "$@" ++ systemctl $_sys_service_mgr --full --no-legend --no-pager "$@" 2>/dev/null + } + + +-- +2.9.3 + diff --git a/0039-cryptsetup-fix-unitialized-variable.patch b/0039-cryptsetup-fix-unitialized-variable.patch new file mode 100644 index 0000000..b3360b9 --- /dev/null +++ b/0039-cryptsetup-fix-unitialized-variable.patch @@ -0,0 +1,29 @@ +From 6c089c4f0c306e2192ec17f85c12c71f8ee87794 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sun, 15 Jan 2017 11:51:48 -0500 +Subject: [PATCH] cryptsetup: fix unitialized variable + +CID #1368416. + +Coverity web interface is back, yay! + +(cherry picked from commit 6fefc0eded00974e5b2862bd5efc7ef975c6a318) +--- + src/cryptsetup/cryptsetup.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c +index ff5a3f36fb..a90bee81bb 100644 +--- a/src/cryptsetup/cryptsetup.c ++++ b/src/cryptsetup/cryptsetup.c +@@ -752,6 +752,7 @@ int main(int argc, char *argv[]) { + + } else { + log_error("Unknown verb %s.", argv[1]); ++ r = -EINVAL; + goto finish; + } + +-- +2.9.3 + diff --git a/0040-systemctl-uninitalized-variable.patch b/0040-systemctl-uninitalized-variable.patch new file mode 100644 index 0000000..dbf5876 --- /dev/null +++ b/0040-systemctl-uninitalized-variable.patch @@ -0,0 +1,31 @@ +From 05db1e6a7adc1ce6e95bbee1751f1f5b124f3d20 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sun, 15 Jan 2017 13:27:37 -0500 +Subject: [PATCH] systemctl: uninitalized variable + +CID #1368270. + +Easily reproduced with COLUMNS=50 ./systemctl --no-pager. + +(cherry picked from commit 43479f8d21a770cdb4870087fcec446b3d845cbc) +--- + src/systemctl/systemctl.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index 4edc690f5c..1a4a201740 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -467,7 +467,8 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) { + id_len += incr; + desc_len += MIN(extra_len - incr, max_desc_len - desc_len); + } +- } ++ } else ++ desc_len = 0; + } else { + id_len = max_id_len; + desc_len = max_desc_len; +-- +2.9.3 + diff --git a/0041-bash_completion-journalctl-add-missing-options.patch b/0041-bash_completion-journalctl-add-missing-options.patch new file mode 100644 index 0000000..636b217 --- /dev/null +++ b/0041-bash_completion-journalctl-add-missing-options.patch @@ -0,0 +1,32 @@ +From c11e97561716e1378f7812e880269638b4130d26 Mon Sep 17 00:00:00 2001 +From: Namhyung Kim +Date: Sat, 21 Jan 2017 16:36:20 +0900 +Subject: [PATCH] bash_completion: journalctl: add missing options + +The --no-hostname and --vacuum-files were missing, add them. + +(cherry picked from commit ed52c971bfa546bfe1f8b329a41ac06077464fb6) +--- + shell-completion/bash/journalctl | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/shell-completion/bash/journalctl b/shell-completion/bash/journalctl +index a999a10df1..ded92cb73a 100644 +--- a/shell-completion/bash/journalctl ++++ b/shell-completion/bash/journalctl +@@ -42,10 +42,10 @@ _journalctl() { + --version --list-catalog --update-catalog --list-boots + --show-cursor --dmesg -k --pager-end -e -r --reverse + --utc -x --catalog --no-full --force --dump-catalog +- --flush --rotate --sync' ++ --flush --rotate --sync --no-hostname' + [ARG]='-b --boot --this-boot -D --directory --file -F --field + -M --machine -o --output -u --unit --user-unit -p --priority +- --vacuum-size --vacuum-time' ++ --vacuum-size --vacuum-time --vacuum-files' + [ARGUNKNOWN]='-c --cursor --interval -n --lines -S --since -U --until + --after-cursor --verify-key -t --identifier + --root' +-- +2.9.3 + diff --git a/0042-bash_completion-journalctl-Complete-t-option-values.patch b/0042-bash_completion-journalctl-Complete-t-option-values.patch new file mode 100644 index 0000000..6af4f9b --- /dev/null +++ b/0042-bash_completion-journalctl-Complete-t-option-values.patch @@ -0,0 +1,29 @@ +From 7123cd16a4cb8c25d16d0b366e79598a22fa0dc5 Mon Sep 17 00:00:00 2001 +From: Namhyung Kim +Date: Sat, 21 Jan 2017 16:38:23 +0900 +Subject: [PATCH] bash_completion: journalctl: Complete -t option values + +The -t or --identifier requires a syslog identifier. + +(cherry picked from commit 28b6b5de3f6c8943e83afe4e213cdb6a8c7e1a72) +--- + shell-completion/bash/journalctl | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/shell-completion/bash/journalctl b/shell-completion/bash/journalctl +index ded92cb73a..c90a114497 100644 +--- a/shell-completion/bash/journalctl ++++ b/shell-completion/bash/journalctl +@@ -82,6 +82,9 @@ _journalctl() { + --user-unit) + comps=$(journalctl -F '_SYSTEMD_USER_UNIT' 2>/dev/null) + ;; ++ --identifier|-t) ++ comps=$(journalctl -F 'SYSLOG_IDENTIFIER' 2>/dev/null) ++ ;; + *) + return 0 + ;; +-- +2.9.3 + diff --git a/0043-Fixi-caching-in-zsh-completion-5122.patch b/0043-Fixi-caching-in-zsh-completion-5122.patch new file mode 100644 index 0000000..6f3f669 --- /dev/null +++ b/0043-Fixi-caching-in-zsh-completion-5122.patch @@ -0,0 +1,77 @@ +From 44d387cadd019d812bbed0f79360f9e66b7e96c2 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= + =?UTF-8?q?=D0=A2=D0=B8=D1=85=D0=BE=D0=BD=D0=BE=D0=B2?= + +Date: Sat, 21 Jan 2017 23:53:09 +0400 +Subject: [PATCH] Fixi caching in zsh completion (#5122) + +I found several issues with zsh completion code: + +1. typo in cache filename: "SYS_ALL_PROPRTIES", so cache just not loading from this file +2. cache stored in one file, despite user or system mode. So it can be loaded later in wrong mode +3. most serious problem: broken logic - it retrieves cache when _cache_invalid is true + +How to reproduce: type "systemctl --user status " and you will see user units. Then press +control+C and type "systemctl --system status " in same session and you'll see user units again +(cherry picked from commit 88e4dbd505ed4f8480b1f3b837b3c2ac55f1b1dd) +--- + shell-completion/zsh/_systemctl.in | 18 +++++++++--------- + 1 file changed, 9 insertions(+), 9 deletions(-) + +diff --git a/shell-completion/zsh/_systemctl.in b/shell-completion/zsh/_systemctl.in +index d77a2df74e..553216da5e 100644 +--- a/shell-completion/zsh/_systemctl.in ++++ b/shell-completion/zsh/_systemctl.in +@@ -98,11 +98,11 @@ __systemctl() + # Fills the unit list + _systemctl_all_units() + { +- if ( [[ ${+_sys_all_units} -eq 0 ]] || _cache_invalid SYS_ALL_UNITS ) && +- ! _retrieve_cache SYS_ALL_UNITS; ++ if ( [[ ${+_sys_all_units} -eq 0 ]] || _cache_invalid SYS_ALL_UNITS$_sys_service_mgr ) || ++ ! _retrieve_cache SYS_ALL_UNITS$_sys_service_mgr; + then + _sys_all_units=( ${${(f)"$(__systemctl list-units --all)"}%% *} ) +- _store_cache SYS_ALL_UNITS _sys_all_units ++ _store_cache SYS_ALL_UNITS$_sys_service_mgr _sys_all_units + fi + } + +@@ -111,14 +111,14 @@ _systemctl_really_all_units() + { + local -a all_unit_files; + local -a really_all_units; +- if ( [[ ${+_sys_really_all_units} -eq 0 ]] || _cache_invalid SYS_REALLY_ALL_UNITS ) && +- ! _retrieve_cache SYS_REALLY_ALL_UNITS; ++ if ( [[ ${+_sys_really_all_units} -eq 0 ]] || _cache_invalid SYS_REALLY_ALL_UNITS$_sys_service_mgr ) || ++ ! _retrieve_cache SYS_REALLY_ALL_UNITS$_sys_service_mgr; + then + all_unit_files=( ${${(f)"$(__systemctl list-unit-files)"}%% *} ) + _systemctl_all_units + really_all_units=($_sys_all_units $all_unit_files) + _sys_really_all_units=(${(u)really_all_units}) +- _store_cache SYS_REALLY_ALL_UNITS _sys_really_all_units ++ _store_cache SYS_REALLY_ALL_UNITS$_sys_service_mgr _sys_really_all_units + fi + } + +@@ -330,13 +330,13 @@ _unit_types() { + } + + _unit_properties() { +- if ( [[ ${+_sys_all_properties} -eq 0 ]] || _cache_invalid SYS_ALL_PROPERTIES ) && +- ! _retrieve_cache SYS_ALL_PROPERTIES; ++ if ( [[ ${+_sys_all_properties} -eq 0 ]] || _cache_invalid SYS_ALL_PROPERTIES$_sys_service_mgr ) || ++ ! _retrieve_cache SYS_ALL_PROPERTIES$_sys_service_mgr; + then + _sys_all_properties=( ${${(M)${(f)"$(__systemctl show --all; + @rootlibexecdir@/systemd --dump-configuration-items)"}##[[:alnum:]]##=*}%%=*} + ) +- _store_cache SYS_ALL_PROPRTIES _sys_all_properties ++ _store_cache SYS_ALL_PROPERTIES$_sys_service_mgr _sys_all_properties + fi + _values -s , "${_sys_all_properties[@]}" + } +-- +2.9.3 + diff --git a/0044-bash-completion-add-support-for-now-5155.patch b/0044-bash-completion-add-support-for-now-5155.patch new file mode 100644 index 0000000..8fb3101 --- /dev/null +++ b/0044-bash-completion-add-support-for-now-5155.patch @@ -0,0 +1,26 @@ +From a0a44b26b5438fc45c28b6918542ebd84810fbd2 Mon Sep 17 00:00:00 2001 +From: Jan Synacek +Date: Wed, 25 Jan 2017 13:44:04 +0100 +Subject: [PATCH] bash-completion: add support for --now (#5155) + +(cherry picked from commit 0067c7b29ab996bf99cf1bafe63c118b9b6d5b56) +--- + shell-completion/bash/systemctl.in | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in +index 34589e2888..7765010869 100644 +--- a/shell-completion/bash/systemctl.in ++++ b/shell-completion/bash/systemctl.in +@@ -124,7 +124,7 @@ _systemctl () { + + local -A OPTS=( + [STANDALONE]='--all -a --reverse --after --before --defaults --force -f --full -l --global +- --help -h --no-ask-password --no-block --no-legend --no-pager --no-reload --no-wall ++ --help -h --no-ask-password --no-block --no-legend --no-pager --no-reload --no-wall --now + --quiet -q --privileged -P --system --user --version --runtime --recursive -r --firmware-setup + --show-types -i --ignore-inhibitors --plain' + [ARG]='--host -H --kill-who --property -p --signal -s --type -t --state --job-mode --root +-- +2.9.3 + diff --git a/0045-core-dbus-fix-two-strv-memleaks.patch b/0045-core-dbus-fix-two-strv-memleaks.patch new file mode 100644 index 0000000..4162ef6 --- /dev/null +++ b/0045-core-dbus-fix-two-strv-memleaks.patch @@ -0,0 +1,38 @@ +From 6c179c1c15b7a0289bd77681353475fd40d3f877 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 24 Jan 2017 22:21:16 -0500 +Subject: [PATCH] core/dbus: fix two strv memleaks + +job_dbus_path and unit_dbus_path both allocate new strings, so we should use +strv_free. + +(cherry picked from commit f0c03de85afa93d1df2bb533a46748e7f4264af6) +--- + src/core/dbus.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/core/dbus.c b/src/core/dbus.c +index 070974fe66..c6fcd01138 100644 +--- a/src/core/dbus.c ++++ b/src/core/dbus.c +@@ -477,7 +477,7 @@ static int bus_kill_context_find(sd_bus *bus, const char *path, const char *inte + } + + static int bus_job_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { +- _cleanup_free_ char **l = NULL; ++ _cleanup_strv_free_ char **l = NULL; + Manager *m = userdata; + unsigned k = 0; + Iterator i; +@@ -504,7 +504,7 @@ static int bus_job_enumerate(sd_bus *bus, const char *path, void *userdata, char + } + + static int bus_unit_enumerate(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error) { +- _cleanup_free_ char **l = NULL; ++ _cleanup_strv_free_ char **l = NULL; + Manager *m = userdata; + unsigned k = 0; + Iterator i; +-- +2.9.3 + diff --git a/0046-core-execute-fix-strv-memleak.patch b/0046-core-execute-fix-strv-memleak.patch new file mode 100644 index 0000000..b23471c --- /dev/null +++ b/0046-core-execute-fix-strv-memleak.patch @@ -0,0 +1,40 @@ +From 9fa10892653f7a23939356d018c62beb2044349f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 24 Jan 2017 22:24:07 -0500 +Subject: [PATCH] core/execute: fix strv memleak + +compile_read_write_paths() returns a normal strv from strv_copy(), and +setup_namespace() uses it read-only, so we should use strv_free to deallocate. + +(cherry picked from commit 06ec51d8ef30dce3c7f864633ba3a9888d18fed6) +--- + src/core/execute.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/src/core/execute.c b/src/core/execute.c +index f13ca30395..38cd486d82 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -1984,8 +1984,8 @@ static int compile_read_write_paths( + _cleanup_strv_free_ char **l = NULL; + char **rt; + +- /* Compile the list of writable paths. This is the combination of the explicitly configured paths, plus all +- * runtime directories. */ ++ /* Compile the list of writable paths. This is the combination of ++ * the explicitly configured paths, plus all runtime directories. */ + + if (strv_isempty(context->read_write_paths) && + strv_isempty(context->runtime_directory)) { +@@ -2018,7 +2018,7 @@ static int apply_mount_namespace(Unit *u, const ExecContext *context, + const ExecParameters *params, + ExecRuntime *runtime) { + int r; +- _cleanup_free_ char **rw = NULL; ++ _cleanup_strv_free_ char **rw = NULL; + char *tmp = NULL, *var = NULL; + const char *root_dir = NULL; + NameSpaceInfo ns_info = { +-- +2.9.3 + diff --git a/0047-resolve-fix-strv-memleak.patch b/0047-resolve-fix-strv-memleak.patch new file mode 100644 index 0000000..1698add --- /dev/null +++ b/0047-resolve-fix-strv-memleak.patch @@ -0,0 +1,28 @@ +From 31d6f68def2f824a21190013e17445094f106e2b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Tue, 24 Jan 2017 22:27:21 -0500 +Subject: [PATCH] resolve: fix strv memleak + +sd_bus_message_read_strv() returns a normal strv... + +(cherry picked from commit c6d92582205065e4924b9f0cb1428f4a5f210fd4) +--- + src/resolve/resolved-link-bus.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/resolve/resolved-link-bus.c b/src/resolve/resolved-link-bus.c +index 364812250f..59cd6cf1cb 100644 +--- a/src/resolve/resolved-link-bus.c ++++ b/src/resolve/resolved-link-bus.c +@@ -462,7 +462,7 @@ int bus_link_method_set_dnssec(sd_bus_message *message, void *userdata, sd_bus_e + + int bus_link_method_set_dnssec_negative_trust_anchors(sd_bus_message *message, void *userdata, sd_bus_error *error) { + _cleanup_set_free_free_ Set *ns = NULL; +- _cleanup_free_ char **ntas = NULL; ++ _cleanup_strv_free_ char **ntas = NULL; + Link *l = userdata; + int r; + char **i; +-- +2.9.3 + diff --git a/0048-nspawn-fix-clobbering-of-selinux-context-arg.patch b/0048-nspawn-fix-clobbering-of-selinux-context-arg.patch new file mode 100644 index 0000000..9630b04 --- /dev/null +++ b/0048-nspawn-fix-clobbering-of-selinux-context-arg.patch @@ -0,0 +1,30 @@ +From f9b89affee12bf7383cf2210798489c7197c5765 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Fri, 27 Jan 2017 00:45:38 -0500 +Subject: [PATCH] nspawn: fix clobbering of selinux context arg + +First bug fixed by gcc 7. Yikes. + +(cherry picked from commit 2e8977b198882fb8c99366ab2944572aa8998dc0) +--- + src/nspawn/nspawn.c | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c +index 503265545b..0e2f4d59a5 100644 +--- a/src/nspawn/nspawn.c ++++ b/src/nspawn/nspawn.c +@@ -658,9 +658,8 @@ static int parse_argv(int argc, char *argv[]) { + r = free_and_strdup(&arg_machine, optarg); + if (r < 0) + return log_oom(); +- +- break; + } ++ break; + + case 'Z': + arg_selinux_context = optarg; +-- +2.9.3 + diff --git a/0049-parse_hwdb-fix-to-work-with-pyparsing-2.1.10.patch b/0049-parse_hwdb-fix-to-work-with-pyparsing-2.1.10.patch new file mode 100644 index 0000000..7861fee --- /dev/null +++ b/0049-parse_hwdb-fix-to-work-with-pyparsing-2.1.10.patch @@ -0,0 +1,32 @@ +From bcb9684d0cdb56e4924da6a7dc36d74bd8ef5faf Mon Sep 17 00:00:00 2001 +From: Martin Pitt +Date: Wed, 30 Nov 2016 09:20:15 +0100 +Subject: [PATCH] parse_hwdb: fix to work with pyparsing 2.1.10 + +pyparsing 2.1.10 fixed the handling of LineStart to really just apply to line +starts and not ignore whitespace and comments any more. Adjust EMPTYLINE to +this. + +Many thanks to Paul McGuire for pointing this out! + +(cherry picked from commit f644a6da7a6f11d20116842e2ce1c7e9c0b0ad64) +--- + hwdb/parse_hwdb.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hwdb/parse_hwdb.py b/hwdb/parse_hwdb.py +index 5d4c5ea64d..5856d12af7 100755 +--- a/hwdb/parse_hwdb.py ++++ b/hwdb/parse_hwdb.py +@@ -56,7 +56,7 @@ except ImportError: + lru_cache = lambda: (lambda f: f) + + EOL = LineEnd().suppress() +-EMPTYLINE = LineStart() + LineEnd() ++EMPTYLINE = LineEnd() + COMMENTLINE = pythonStyleComment + EOL + INTEGER = Word(nums) + REAL = Combine((INTEGER + Optional('.' + Optional(INTEGER))) ^ ('.' + INTEGER)) +-- +2.9.3 + diff --git a/0050-journald-don-t-flush-to-var-log-journal-before-we-ge.patch b/0050-journald-don-t-flush-to-var-log-journal-before-we-ge.patch new file mode 100644 index 0000000..50d34f5 --- /dev/null +++ b/0050-journald-don-t-flush-to-var-log-journal-before-we-ge.patch @@ -0,0 +1,136 @@ +From 9a9a2deb4b5c7dcd07ba64e1d01a21d00fbe08f3 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Mon, 12 Dec 2016 20:54:45 +0100 +Subject: [PATCH] journald: don't flush to /var/log/journal before we get asked + to + +This changes journald to not write to /var/log/journal until it received +SIGUSR1 for the first time, thus having been requested to flush the runtime +journal to disk. + +This makes the journal work nicer with systems which have the root file system +writable early, but still need to rearrange /var before journald should start +writing and creating files to it, for example because ACLs need to be applied +first, or because /var is to be mounted from another file system, NFS or tmpfs +(as is the case for systemd.volatile=state). + +Before this change we required setupts with /var split out to mount the root +disk read-only early on, and ship an /etc/fstab that remounted it writable only +after having placed /var at the right place. But even that was racy for various +preparations as journald might end up accessing the file system before it was +entirely set up, as soon as it was writable. + +With this change we make scheduling when to start writing to /var/log/journal +explicit. This means persistent mode now requires +systemd-journal-flush.service in the mix to work, as otherwise journald would +never write to the directory. + +See: #1397 +(cherry picked from commit f78273c8dacf678cc8fd7387f678e6344a99405c) +--- + src/journal/journald-server.c | 21 +++++++++++---------- + src/journal/journald-server.h | 2 +- + src/journal/journald.c | 2 +- + 3 files changed, 13 insertions(+), 12 deletions(-) + +diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c +index 1d2fce8dc7..ced0ad6f21 100644 +--- a/src/journal/journald-server.c ++++ b/src/journal/journald-server.c +@@ -283,17 +283,16 @@ static int open_journal( + } + + static bool flushed_flag_is_set(void) { +- return (access("/run/systemd/journal/flushed", F_OK) >= 0); ++ return access("/run/systemd/journal/flushed", F_OK) >= 0; + } + + static int system_journal_open(Server *s, bool flush_requested) { +- bool flushed = false; + const char *fn; + int r = 0; + + if (!s->system_journal && +- (s->storage == STORAGE_PERSISTENT || s->storage == STORAGE_AUTO) && +- (flush_requested || (flushed = flushed_flag_is_set()))) { ++ IN_SET(s->storage, STORAGE_PERSISTENT, STORAGE_AUTO) && ++ (flush_requested || flushed_flag_is_set())) { + + /* If in auto mode: first try to create the machine + * path, but not the prefix. +@@ -326,8 +325,8 @@ static int system_journal_open(Server *s, bool flush_requested) { + * Perform an implicit flush to var, leaving the runtime + * journal closed, now that the system journal is back. + */ +- if (s->runtime_journal && flushed) +- (void) server_flush_to_var(s); ++ if (!flush_requested) ++ (void) server_flush_to_var(s, true); + } + + if (!s->runtime_journal && +@@ -1183,7 +1182,7 @@ finish: + dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len, unit_id, priority, object_pid); + } + +-int server_flush_to_var(Server *s) { ++int server_flush_to_var(Server *s, bool require_flag_file) { + sd_id128_t machine; + sd_journal *j = NULL; + char ts[FORMAT_TIMESPAN_MAX]; +@@ -1193,13 +1192,15 @@ int server_flush_to_var(Server *s) { + + assert(s); + +- if (s->storage != STORAGE_AUTO && +- s->storage != STORAGE_PERSISTENT) ++ if (!IN_SET(s->storage, STORAGE_AUTO, STORAGE_PERSISTENT)) + return 0; + + if (!s->runtime_journal) + return 0; + ++ if (require_flag_file && !flushed_flag_is_set()) ++ return 0; ++ + (void) system_journal_open(s, true); + + if (!s->system_journal) +@@ -1411,7 +1412,7 @@ static int dispatch_sigusr1(sd_event_source *es, const struct signalfd_siginfo * + + log_info("Received request to flush runtime journal from PID " PID_FMT, si->ssi_pid); + +- (void) server_flush_to_var(s); ++ (void) server_flush_to_var(s, false); + server_sync(s); + server_vacuum(s, false); + +diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h +index 99d91496be..de1c48f805 100644 +--- a/src/journal/journald-server.h ++++ b/src/journal/journald-server.h +@@ -197,7 +197,7 @@ void server_sync(Server *s); + int server_vacuum(Server *s, bool verbose); + void server_rotate(Server *s); + int server_schedule_sync(Server *s, int priority); +-int server_flush_to_var(Server *s); ++int server_flush_to_var(Server *s, bool require_flag_file); + void server_maybe_append_tags(Server *s); + int server_process_datagram(sd_event_source *es, int fd, uint32_t revents, void *userdata); + void server_space_usage_message(Server *s, JournalStorage *storage); +diff --git a/src/journal/journald.c b/src/journal/journald.c +index 7f47ca22dd..9ac21457f6 100644 +--- a/src/journal/journald.c ++++ b/src/journal/journald.c +@@ -52,7 +52,7 @@ int main(int argc, char *argv[]) { + goto finish; + + server_vacuum(&server, false); +- server_flush_to_var(&server); ++ server_flush_to_var(&server, true); + server_flush_dev_kmsg(&server); + + log_debug("systemd-journald running as pid "PID_FMT, getpid()); +-- +2.9.3 + diff --git a/0051-tree-wide-drop-NULL-sentinel-from-strjoin.patch b/0051-tree-wide-drop-NULL-sentinel-from-strjoin.patch new file mode 100644 index 0000000..85a870c --- /dev/null +++ b/0051-tree-wide-drop-NULL-sentinel-from-strjoin.patch @@ -0,0 +1,2121 @@ +From dc5813c259a23678a6521c5d52af50f2825ceacd Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= +Date: Sun, 23 Oct 2016 11:43:27 -0400 +Subject: [PATCH] tree-wide: drop NULL sentinel from strjoin + +This makes strjoin and strjoina more similar and avoids the useless final +argument. + +spatch -I . -I ./src -I ./src/basic -I ./src/basic -I ./src/shared -I ./src/shared -I ./src/network -I ./src/locale -I ./src/login -I ./src/journal -I ./src/journal -I ./src/timedate -I ./src/timesync -I ./src/nspawn -I ./src/resolve -I ./src/resolve -I ./src/systemd -I ./src/core -I ./src/core -I ./src/libudev -I ./src/udev -I ./src/udev/net -I ./src/udev -I ./src/libsystemd/sd-bus -I ./src/libsystemd/sd-event -I ./src/libsystemd/sd-login -I ./src/libsystemd/sd-netlink -I ./src/libsystemd/sd-network -I ./src/libsystemd/sd-hwdb -I ./src/libsystemd/sd-device -I ./src/libsystemd/sd-id128 -I ./src/libsystemd-network --sp-file coccinelle/strjoin.cocci --in-place $(git ls-files src/*.c) + +git grep -e '\bstrjoin\b.*NULL' -l|xargs sed -i -r 's/strjoin\((.*), NULL\)/strjoin(\1)/' + +This might have missed a few cases (spatch has a really hard time dealing +with _cleanup_ macros), but that's no big issue, they can always be fixed +later. + +(cherry picked from commit 605405c6cc934466951b0c6bad5a9553620bcb08) +--- + coccinelle/strjoin.cocci | 16 +++++++++++++++ + src/backlight/backlight.c | 4 ++-- + src/basic/btrfs-util.c | 2 +- + src/basic/cgroup-util.c | 16 +++++++-------- + src/basic/conf-files.c | 2 +- + src/basic/fileio.c | 6 +++--- + src/basic/fs-util.c | 2 +- + src/basic/mount-util.c | 2 +- + src/basic/path-util.c | 12 +++++------- + src/basic/process-util.c | 6 +++--- + src/basic/string-util.c | 2 +- + src/basic/string-util.h | 3 ++- + src/basic/unit-name.c | 4 ++-- + src/basic/util.c | 2 +- + src/cgls/cgls.c | 2 +- + src/cgtop/cgtop.c | 2 +- + src/core/cgroup.c | 7 ++++--- + src/core/dbus-execute.c | 2 +- + src/core/dbus-unit.c | 6 +++--- + src/core/device.c | 2 +- + src/core/execute.c | 10 +++++----- + src/core/locale-setup.c | 2 +- + src/core/manager.c | 2 +- + src/core/namespace.c | 2 +- + src/core/service.c | 2 +- + src/core/timer.c | 4 ++-- + src/core/unit.c | 4 ++-- + src/coredump/coredump.c | 9 +++++++-- + src/coredump/coredumpctl.c | 4 ++-- + src/cryptsetup/cryptsetup-generator.c | 12 ++++++------ + src/debug-generator/debug-generator.c | 4 ++-- + src/delta/delta.c | 8 ++++---- + src/escape/escape.c | 2 +- + src/fstab-generator/fstab-generator.c | 14 ++++++------- + src/gpt-auto-generator/gpt-auto-generator.c | 24 +++++++++++------------ + src/hibernate-resume/hibernate-resume-generator.c | 2 +- + src/hostname/hostnamed.c | 2 +- + src/hwdb/hwdb.c | 2 +- + src/import/import-raw.c | 2 +- + src/import/import-tar.c | 2 +- + src/journal-remote/journal-gatewayd.c | 2 +- + src/journal-remote/journal-upload.c | 4 ++-- + src/journal/journalctl.c | 2 +- + src/journal/journald-server.c | 6 +++--- + src/journal/journald-wall.c | 2 +- + src/journal/sd-journal.c | 6 +++--- + src/libsystemd/sd-bus/bus-kernel.c | 2 +- + src/libsystemd/sd-bus/busctl-introspect.c | 2 +- + src/libsystemd/sd-bus/busctl.c | 4 ++-- + src/libsystemd/sd-bus/sd-bus.c | 6 +++--- + src/libsystemd/sd-bus/test-bus-objects.c | 2 +- + src/libsystemd/sd-device/device-enumerator.c | 2 +- + src/libsystemd/sd-path/sd-path.c | 8 ++++---- + src/locale/keymap-util.c | 6 +++--- + src/login/logind-inhibit.c | 2 +- + src/login/logind-session.c | 2 +- + src/login/pam_systemd.c | 2 +- + src/machine/machine.c | 2 +- + src/mount/mount-tool.c | 4 ++-- + src/nspawn/nspawn-mount.c | 4 ++-- + src/nspawn/nspawn.c | 4 ++-- + src/rc-local-generator/rc-local-generator.c | 4 ++-- + src/resolve/resolved-dns-dnssec.c | 2 +- + src/resolve/resolved-dns-rr.c | 14 ++++++------- + src/resolve/test-dnssec-complex.c | 2 +- + src/rfkill/rfkill.c | 4 ++-- + src/run/run.c | 2 +- + src/shared/base-filesystem.c | 2 +- + src/shared/bus-util.c | 4 ++-- + src/shared/cgroup-show.c | 2 +- + src/shared/conf-parser.c | 2 +- + src/shared/dns-domain.c | 4 ++-- + src/shared/dropin.c | 10 +++++----- + src/shared/fstab-util.c | 2 +- + src/shared/install-printf.c | 2 +- + src/shared/install.c | 10 +++++----- + src/shared/machine-image.c | 2 +- + src/shared/path-lookup.c | 4 ++-- + src/systemctl/systemctl.c | 10 +++++----- + src/sysv-generator/sysv-generator.c | 10 +++++----- + src/test/test-copy.c | 4 ++-- + src/test/test-date.c | 4 ++-- + src/test/test-fileio.c | 2 +- + src/test/test-namespace.c | 8 ++++---- + src/test/test-path.c | 2 +- + src/test/test-replace-var.c | 2 +- + src/tmpfiles/tmpfiles.c | 4 ++-- + src/udev/udev-builtin-hwdb.c | 2 +- + src/udev/udevadm-hwdb.c | 2 +- + 89 files changed, 215 insertions(+), 194 deletions(-) + create mode 100644 coccinelle/strjoin.cocci + +diff --git a/coccinelle/strjoin.cocci b/coccinelle/strjoin.cocci +new file mode 100644 +index 0000000000..675760e37a +--- /dev/null ++++ b/coccinelle/strjoin.cocci +@@ -0,0 +1,16 @@ ++@@ ++expression list args; ++@@ ++- strjoin(args, NULL); +++ strjoin(args); ++@@ ++expression t; ++expression list args; ++@@ ++- t = strjoin(args, NULL); +++ t = strjoin(args); ++@@ ++expression list args; ++@@ ++- return strjoin(args, NULL); +++ return strjoin(args); +diff --git a/src/backlight/backlight.c b/src/backlight/backlight.c +index 7c59f60d5f..c909b5bb51 100644 +--- a/src/backlight/backlight.c ++++ b/src/backlight/backlight.c +@@ -357,9 +357,9 @@ int main(int argc, char *argv[]) { + return EXIT_FAILURE; + } + +- saved = strjoin("/var/lib/systemd/backlight/", escaped_path_id, ":", escaped_ss, ":", escaped_sysname, NULL); ++ saved = strjoin("/var/lib/systemd/backlight/", escaped_path_id, ":", escaped_ss, ":", escaped_sysname); + } else +- saved = strjoin("/var/lib/systemd/backlight/", escaped_ss, ":", escaped_sysname, NULL); ++ saved = strjoin("/var/lib/systemd/backlight/", escaped_ss, ":", escaped_sysname); + + if (!saved) { + log_oom(); +diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c +index 359d85f2e8..656bb13719 100644 +--- a/src/basic/btrfs-util.c ++++ b/src/basic/btrfs-util.c +@@ -1642,7 +1642,7 @@ static int subvol_snapshot_children(int old_fd, int new_fd, const char *subvolum + if (old_child_fd < 0) + return -errno; + +- np = strjoin(subvolume, "/", ino_args.name, NULL); ++ np = strjoin(subvolume, "/", ino_args.name); + if (!np) + return -ENOMEM; + +diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c +index 134e6e3664..63c913f14d 100644 +--- a/src/basic/cgroup-util.c ++++ b/src/basic/cgroup-util.c +@@ -345,7 +345,7 @@ int cg_kill_recursive( + while ((r = cg_read_subgroup(d, &fn)) > 0) { + _cleanup_free_ char *p = NULL; + +- p = strjoin(path, "/", fn, NULL); ++ p = strjoin(path, "/", fn); + free(fn); + if (!p) + return -ENOMEM; +@@ -479,7 +479,7 @@ int cg_migrate_recursive( + while ((r = cg_read_subgroup(d, &fn)) > 0) { + _cleanup_free_ char *p = NULL; + +- p = strjoin(pfrom, "/", fn, NULL); ++ p = strjoin(pfrom, "/", fn); + free(fn); + if (!p) + return -ENOMEM; +@@ -562,11 +562,11 @@ static int join_path_legacy(const char *controller, const char *path, const char + if (isempty(path) && isempty(suffix)) + t = strappend("/sys/fs/cgroup/", dn); + else if (isempty(path)) +- t = strjoin("/sys/fs/cgroup/", dn, "/", suffix, NULL); ++ t = strjoin("/sys/fs/cgroup/", dn, "/", suffix); + else if (isempty(suffix)) +- t = strjoin("/sys/fs/cgroup/", dn, "/", path, NULL); ++ t = strjoin("/sys/fs/cgroup/", dn, "/", path); + else +- t = strjoin("/sys/fs/cgroup/", dn, "/", path, "/", suffix, NULL); ++ t = strjoin("/sys/fs/cgroup/", dn, "/", path, "/", suffix); + if (!t) + return -ENOMEM; + +@@ -586,7 +586,7 @@ static int join_path_unified(const char *path, const char *suffix, char **fs) { + else if (isempty(suffix)) + t = strappend("/sys/fs/cgroup/", path); + else +- t = strjoin("/sys/fs/cgroup/", path, "/", suffix, NULL); ++ t = strjoin("/sys/fs/cgroup/", path, "/", suffix); + if (!t) + return -ENOMEM; + +@@ -613,7 +613,7 @@ int cg_get_path(const char *controller, const char *path, const char *suffix, ch + else if (!path) + t = strdup(suffix); + else +- t = strjoin(path, "/", suffix, NULL); ++ t = strjoin(path, "/", suffix); + if (!t) + return -ENOMEM; + +@@ -1145,7 +1145,7 @@ int cg_is_empty_recursive(const char *controller, const char *path) { + while ((r = cg_read_subgroup(d, &fn)) > 0) { + _cleanup_free_ char *p = NULL; + +- p = strjoin(path, "/", fn, NULL); ++ p = strjoin(path, "/", fn); + free(fn); + if (!p) + return -ENOMEM; +diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c +index c781610e14..c0c22610d7 100644 +--- a/src/basic/conf-files.c ++++ b/src/basic/conf-files.c +@@ -60,7 +60,7 @@ static int files_add(Hashmap *h, const char *root, const char *path, const char + if (!dirent_is_file_with_suffix(de, suffix)) + continue; + +- p = strjoin(dirpath, "/", de->d_name, NULL); ++ p = strjoin(dirpath, "/", de->d_name); + if (!p) + return -ENOMEM; + +diff --git a/src/basic/fileio.c b/src/basic/fileio.c +index 1cfb7a98f5..1615456659 100644 +--- a/src/basic/fileio.c ++++ b/src/basic/fileio.c +@@ -676,7 +676,7 @@ static int load_env_file_push( + return -EINVAL; + } + +- p = strjoin(key, "=", strempty(value), NULL); ++ p = strjoin(key, "=", strempty(value)); + if (!p) + return -ENOMEM; + +@@ -963,9 +963,9 @@ static int search_and_fopen_internal(const char *path, const char *mode, const c + FILE *f; + + if (root) +- p = strjoin(root, *i, "/", path, NULL); ++ p = strjoin(root, *i, "/", path); + else +- p = strjoin(*i, "/", path, NULL); ++ p = strjoin(*i, "/", path); + if (!p) + return -ENOMEM; + +diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c +index 48952a1c26..d2c322a0de 100644 +--- a/src/basic/fs-util.c ++++ b/src/basic/fs-util.c +@@ -742,7 +742,7 @@ int chase_symlinks(const char *path, const char *_root, char **ret) { + /* A relative destination. If so, this is what we'll prefix what's left to do with what + * we just read, and start the loop again, but remain in the current directory. */ + +- joined = strjoin("/", destination, todo, NULL); ++ joined = strjoin("/", destination, todo); + if (!joined) + return -ENOMEM; + +diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c +index c8f8022578..5d37fb48be 100644 +--- a/src/basic/mount-util.c ++++ b/src/basic/mount-util.c +@@ -642,7 +642,7 @@ static char* mount_flags_to_string(long unsigned flags) { + FLAG(MS_I_VERSION), + FLAG(MS_STRICTATIME), + FLAG(MS_LAZYTIME), +- y, NULL); ++ y); + if (!x) + return NULL; + if (!y) +diff --git a/src/basic/path-util.c b/src/basic/path-util.c +index fd38f51c4c..5cdac50c68 100644 +--- a/src/basic/path-util.c ++++ b/src/basic/path-util.c +@@ -83,7 +83,7 @@ char *path_make_absolute(const char *p, const char *prefix) { + if (path_is_absolute(p) || !prefix) + return strdup(p); + +- return strjoin(prefix, "/", p, NULL); ++ return strjoin(prefix, "/", p); + } + + int path_make_absolute_cwd(const char *p, char **ret) { +@@ -104,7 +104,7 @@ int path_make_absolute_cwd(const char *p, char **ret) { + if (!cwd) + return negative_errno(); + +- c = strjoin(cwd, "/", p, NULL); ++ c = strjoin(cwd, "/", p); + } + if (!c) + return -ENOMEM; +@@ -454,13 +454,11 @@ char* path_join(const char *root, const char *path, const char *rest) { + return strjoin(root, endswith(root, "/") ? "" : "/", + path[0] == '/' ? path+1 : path, + rest ? (endswith(path, "/") ? "" : "/") : NULL, +- rest && rest[0] == '/' ? rest+1 : rest, +- NULL); ++ rest && rest[0] == '/' ? rest+1 : rest); + else + return strjoin(path, + rest ? (endswith(path, "/") ? "" : "/") : NULL, +- rest && rest[0] == '/' ? rest+1 : rest, +- NULL); ++ rest && rest[0] == '/' ? rest+1 : rest); + } + + int find_binary(const char *name, char **ret) { +@@ -504,7 +502,7 @@ int find_binary(const char *name, char **ret) { + if (!path_is_absolute(element)) + continue; + +- j = strjoin(element, "/", name, NULL); ++ j = strjoin(element, "/", name); + if (!j) + return -ENOMEM; + +diff --git a/src/basic/process-util.c b/src/basic/process-util.c +index 54b644ad56..48a5c719af 100644 +--- a/src/basic/process-util.c ++++ b/src/basic/process-util.c +@@ -236,14 +236,14 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * + return h; + + if (max_length == 0) +- r = strjoin("[", t, "]", NULL); ++ r = strjoin("[", t, "]"); + else { + size_t l; + + l = strlen(t); + + if (l + 3 <= max_length) +- r = strjoin("[", t, "]", NULL); ++ r = strjoin("[", t, "]"); + else if (max_length <= 6) { + + r = new(char, max_length); +@@ -263,7 +263,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char * + e--; + *e = 0; + +- r = strjoin("[", t, "...]", NULL); ++ r = strjoin("[", t, "...]"); + } + } + if (!r) +diff --git a/src/basic/string-util.c b/src/basic/string-util.c +index 6b06e643c9..2ba3604ba0 100644 +--- a/src/basic/string-util.c ++++ b/src/basic/string-util.c +@@ -218,7 +218,7 @@ char *strappend(const char *s, const char *suffix) { + return strnappend(s, suffix, suffix ? strlen(suffix) : 0); + } + +-char *strjoin(const char *x, ...) { ++char *strjoin_real(const char *x, ...) { + va_list ap; + size_t l; + char *r, *p; +diff --git a/src/basic/string-util.h b/src/basic/string-util.h +index d029d538bd..0175803302 100644 +--- a/src/basic/string-util.h ++++ b/src/basic/string-util.h +@@ -116,7 +116,8 @@ const char* split(const char **state, size_t *l, const char *separator, bool quo + char *strappend(const char *s, const char *suffix); + char *strnappend(const char *s, const char *suffix, size_t length); + +-char *strjoin(const char *x, ...) _sentinel_; ++char *strjoin_real(const char *x, ...) _sentinel_; ++#define strjoin(a, ...) strjoin_real((a), __VA_ARGS__, NULL) + + #define strjoina(a, ...) \ + ({ \ +diff --git a/src/basic/unit-name.c b/src/basic/unit-name.c +index fe883b95c7..0a6efa449a 100644 +--- a/src/basic/unit-name.c ++++ b/src/basic/unit-name.c +@@ -273,7 +273,7 @@ int unit_name_build(const char *prefix, const char *instance, const char *suffix + if (!instance) + s = strappend(prefix, suffix); + else +- s = strjoin(prefix, "@", instance, suffix, NULL); ++ s = strjoin(prefix, "@", instance, suffix); + if (!s) + return -ENOMEM; + +@@ -554,7 +554,7 @@ int unit_name_from_path_instance(const char *prefix, const char *path, const cha + if (r < 0) + return r; + +- s = strjoin(prefix, "@", p, suffix, NULL); ++ s = strjoin(prefix, "@", p, suffix); + if (!s) + return -ENOMEM; + +diff --git a/src/basic/util.c b/src/basic/util.c +index ec7939dc83..0f65e4839c 100644 +--- a/src/basic/util.c ++++ b/src/basic/util.c +@@ -131,7 +131,7 @@ static int do_execute(char **directories, usec_t timeout, char *argv[]) { + if (r < 0) + return log_oom(); + +- path = strjoin(*directory, "/", de->d_name, NULL); ++ path = strjoin(*directory, "/", de->d_name); + if (!path) + return log_oom(); + +diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c +index adf488e8e1..b55aa86a40 100644 +--- a/src/cgls/cgls.c ++++ b/src/cgls/cgls.c +@@ -223,7 +223,7 @@ int main(int argc, char *argv[]) { + + controller = c ?: SYSTEMD_CGROUP_CONTROLLER; + if (p) { +- j = strjoin(root, "/", p, NULL); ++ j = strjoin(root, "/", p); + if (!j) { + r = log_oom(); + goto finish; +diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c +index aba17c9829..af5c3d8695 100644 +--- a/src/cgtop/cgtop.c ++++ b/src/cgtop/cgtop.c +@@ -431,7 +431,7 @@ static int refresh_one( + if (r == 0) + break; + +- p = strjoin(path, "/", fn, NULL); ++ p = strjoin(path, "/", fn); + if (!p) + return -ENOMEM; + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index 23a92f9651..d662b21cb9 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -1201,9 +1201,10 @@ char *unit_default_cgroup_path(Unit *u) { + return NULL; + + if (slice) +- return strjoin(u->manager->cgroup_root, "/", slice, "/", escaped, NULL); ++ return strjoin(u->manager->cgroup_root, "/", slice, "/", ++ escaped); + else +- return strjoin(u->manager->cgroup_root, "/", escaped, NULL); ++ return strjoin(u->manager->cgroup_root, "/", escaped); + } + + int unit_set_cgroup_path(Unit *u, const char *path) { +@@ -1643,7 +1644,7 @@ static int unit_watch_pids_in_path(Unit *u, const char *path) { + while ((r = cg_read_subgroup(d, &fn)) > 0) { + _cleanup_free_ char *p = NULL; + +- p = strjoin(path, "/", fn, NULL); ++ p = strjoin(path, "/", fn); + free(fn); + + if (!p) +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index 1a7f770db1..03f23780c1 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -1368,7 +1368,7 @@ int bus_exec_context_set_transient_property( + if (mode != UNIT_CHECK) { + char *buf = NULL; + +- buf = strjoin(b ? "-" : "", path, NULL); ++ buf = strjoin(b ? "-" : "", path); + if (!buf) + return -ENOMEM; + +diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c +index 69e249c844..b6cb6e1350 100644 +--- a/src/core/dbus-unit.c ++++ b/src/core/dbus-unit.c +@@ -481,7 +481,7 @@ int bus_unit_method_start_generic( + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s invalid", smode); + + if (reload_if_possible) +- verb = strjoin("reload-or-", job_type_to_string(job_type), NULL); ++ verb = strjoin("reload-or-", job_type_to_string(job_type)); + else + verb = strdup(job_type_to_string(job_type)); + if (!verb) +@@ -984,7 +984,7 @@ static int append_cgroup(sd_bus_message *reply, const char *p, Set *pids) { + if (r == 0) + break; + +- j = strjoin(p, "/", g, NULL); ++ j = strjoin(p, "/", g); + if (!j) + return -ENOMEM; + +@@ -1363,7 +1363,7 @@ static int bus_unit_set_transient_property( + if (r < 0) + return r; + +- label = strjoin(name, "-", other, NULL); ++ label = strjoin(name, "-", other); + if (!label) + return -ENOMEM; + +diff --git a/src/core/device.c b/src/core/device.c +index 4b9e84aeb6..425779d74f 100644 +--- a/src/core/device.c ++++ b/src/core/device.c +@@ -239,7 +239,7 @@ static int device_update_description(Unit *u, struct udev_device *dev, const cha + if (label) { + _cleanup_free_ char *j; + +- j = strjoin(model, " ", label, NULL); ++ j = strjoin(model, " ", label); + if (j) + r = unit_set_description(u, j); + else +diff --git a/src/core/execute.c b/src/core/execute.c +index 38cd486d82..224382b581 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -1603,7 +1603,7 @@ static int build_environment( + if (!joined) + return -ENOMEM; + +- x = strjoin("LISTEN_FDNAMES=", joined, NULL); ++ x = strjoin("LISTEN_FDNAMES=", joined); + if (!x) + return -ENOMEM; + our_env[n_env++] = x; +@@ -1710,7 +1710,7 @@ static int build_pass_environment(const ExecContext *c, char ***ret) { + v = getenv(*i); + if (!v) + continue; +- x = strjoin(*i, "=", v, NULL); ++ x = strjoin(*i, "=", v); + if (!x) + return -ENOMEM; + if (!GREEDY_REALLOC(pass_env, n_bufsize, n_env + 2)) +@@ -1924,7 +1924,7 @@ static int setup_runtime_directory( + STRV_FOREACH(rt, context->runtime_directory) { + _cleanup_free_ char *p; + +- p = strjoin(params->runtime_prefix, "/", *rt, NULL); ++ p = strjoin(params->runtime_prefix, "/", *rt); + if (!p) + return -ENOMEM; + +@@ -2000,7 +2000,7 @@ static int compile_read_write_paths( + STRV_FOREACH(rt, context->runtime_directory) { + char *s; + +- s = strjoin(params->runtime_prefix, "/", *rt, NULL); ++ s = strjoin(params->runtime_prefix, "/", *rt); + if (!s) + return -ENOMEM; + +@@ -3004,7 +3004,7 @@ int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_p + STRV_FOREACH(i, c->runtime_directory) { + _cleanup_free_ char *p; + +- p = strjoin(runtime_prefix, "/", *i, NULL); ++ p = strjoin(runtime_prefix, "/", *i); + if (!p) + return -ENOMEM; + +diff --git a/src/core/locale-setup.c b/src/core/locale-setup.c +index ccf61d29fb..fdd847ee8b 100644 +--- a/src/core/locale-setup.c ++++ b/src/core/locale-setup.c +@@ -87,7 +87,7 @@ int locale_setup(char ***environment) { + if (!variables[i]) + continue; + +- s = strjoin(locale_variable_to_string(i), "=", variables[i], NULL); ++ s = strjoin(locale_variable_to_string(i), "=", variables[i]); + if (!s) { + r = -ENOMEM; + goto finish; +diff --git a/src/core/manager.c b/src/core/manager.c +index 322e69a69c..e00e43a199 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -1196,7 +1196,7 @@ static void manager_build_unit_path_cache(Manager *m) { + FOREACH_DIRENT(de, d, r = -errno; goto fail) { + char *p; + +- p = strjoin(streq(*i, "/") ? "" : *i, "/", de->d_name, NULL); ++ p = strjoin(streq(*i, "/") ? "" : *i, "/", de->d_name); + if (!p) { + r = -ENOMEM; + goto fail; +diff --git a/src/core/namespace.c b/src/core/namespace.c +index 49a50c7b61..f5c0d42c05 100644 +--- a/src/core/namespace.c ++++ b/src/core/namespace.c +@@ -940,7 +940,7 @@ static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) { + if (r < 0) + return r; + +- x = strjoin(prefix, "/systemd-private-", sd_id128_to_string(boot_id, bid), "-", id, "-XXXXXX", NULL); ++ x = strjoin(prefix, "/systemd-private-", sd_id128_to_string(boot_id, bid), "-", id, "-XXXXXX"); + if (!x) + return -ENOMEM; + +diff --git a/src/core/service.c b/src/core/service.c +index a7274a758f..f6acc2f129 100644 +--- a/src/core/service.c ++++ b/src/core/service.c +@@ -3273,7 +3273,7 @@ int service_set_socket_fd(Service *s, int fd, Socket *sock, bool selinux_context + if (UNIT(s)->description) { + _cleanup_free_ char *a; + +- a = strjoin(UNIT(s)->description, " (", peer, ")", NULL); ++ a = strjoin(UNIT(s)->description, " (", peer, ")"); + if (!a) + return -ENOMEM; + +diff --git a/src/core/timer.c b/src/core/timer.c +index 2469a517ea..c6b28dd9c5 100644 +--- a/src/core/timer.c ++++ b/src/core/timer.c +@@ -147,7 +147,7 @@ static int timer_setup_persistent(Timer *t) { + + e = getenv("XDG_DATA_HOME"); + if (e) +- t->stamp_path = strjoin(e, "/systemd/timers/stamp-", UNIT(t)->id, NULL); ++ t->stamp_path = strjoin(e, "/systemd/timers/stamp-", UNIT(t)->id); + else { + + _cleanup_free_ char *h = NULL; +@@ -156,7 +156,7 @@ static int timer_setup_persistent(Timer *t) { + if (r < 0) + return log_unit_error_errno(UNIT(t), r, "Failed to determine home directory: %m"); + +- t->stamp_path = strjoin(h, "/.local/share/systemd/timers/stamp-", UNIT(t)->id, NULL); ++ t->stamp_path = strjoin(h, "/.local/share/systemd/timers/stamp-", UNIT(t)->id); + } + } + +diff --git a/src/core/unit.c b/src/core/unit.c +index e664e23892..fa1f3d9d4b 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -2510,7 +2510,7 @@ int unit_set_default_slice(Unit *u) { + return -ENOMEM; + + if (MANAGER_IS_SYSTEM(u->manager)) +- b = strjoin("system-", escaped, ".slice", NULL); ++ b = strjoin("system-", escaped, ".slice"); + else + b = strappend(escaped, ".slice"); + if (!b) +@@ -3670,7 +3670,7 @@ int unit_make_transient(Unit *u) { + if (!UNIT_VTABLE(u)->can_transient) + return -EOPNOTSUPP; + +- path = strjoin(u->manager->lookup_paths.transient, "/", u->id, NULL); ++ path = strjoin(u->manager->lookup_paths.transient, "/", u->id); + if (!path) + return -ENOMEM; + +diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c +index a982c204be..6bd0e925eb 100644 +--- a/src/coredump/coredump.c ++++ b/src/coredump/coredump.c +@@ -729,7 +729,10 @@ static int submit_coredump( + + r = coredump_make_stack_trace(coredump_fd, context[CONTEXT_EXE], &stacktrace); + if (r >= 0) +- core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (", context[CONTEXT_COMM], ") of user ", context[CONTEXT_UID], " dumped core.\n\n", stacktrace, NULL); ++ core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], ++ " (", context[CONTEXT_COMM], ") of user ", ++ context[CONTEXT_UID], " dumped core.\n\n", ++ stacktrace); + else if (r == -EINVAL) + log_warning("Failed to generate stack trace: %s", dwfl_errmsg(dwfl_errno())); + else +@@ -741,7 +744,9 @@ static int submit_coredump( + if (!core_message) + #endif + log: +- core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (", context[CONTEXT_COMM], ") of user ", context[CONTEXT_UID], " dumped core.", NULL); ++ core_message = strjoin("MESSAGE=Process ", context[CONTEXT_PID], " (", ++ context[CONTEXT_COMM], ") of user ", ++ context[CONTEXT_UID], " dumped core."); + if (core_message) + IOVEC_SET_STRING(iovec[n_iovec++], core_message); + +diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c +index 0e5351e621..877bbb34fc 100644 +--- a/src/coredump/coredumpctl.c ++++ b/src/coredump/coredumpctl.c +@@ -108,7 +108,7 @@ static int add_match(Set *set, const char *match) { + else + prefix = "COREDUMP_COMM="; + +- pattern = strjoin(prefix, match, NULL); ++ pattern = strjoin(prefix, match); + if (!pattern) { + r = -ENOMEM; + goto fail; +@@ -667,7 +667,7 @@ static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) + if (r < 0) + return log_error_errno(r, "Failed to acquire temporary directory path: %m"); + +- temp = strjoin(vt, "/coredump-XXXXXX", NULL); ++ temp = strjoin(vt, "/coredump-XXXXXX"); + if (!temp) + return log_oom(); + +diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c +index e2dc4327fe..68029865a0 100644 +--- a/src/cryptsetup/cryptsetup-generator.c ++++ b/src/cryptsetup/cryptsetup-generator.c +@@ -86,7 +86,7 @@ static int create_disk( + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + +- p = strjoin(arg_dest, "/", n, NULL); ++ p = strjoin(arg_dest, "/", n); + if (!p) + return log_oom(); + +@@ -188,7 +188,7 @@ static int create_disk( + + if (!noauto) { + +- to = strjoin(arg_dest, "/", d, ".wants/", n, NULL); ++ to = strjoin(arg_dest, "/", d, ".wants/", n); + if (!to) + return log_oom(); + +@@ -198,9 +198,9 @@ static int create_disk( + + free(to); + if (!nofail) +- to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL); ++ to = strjoin(arg_dest, "/cryptsetup.target.requires/", n); + else +- to = strjoin(arg_dest, "/cryptsetup.target.wants/", n, NULL); ++ to = strjoin(arg_dest, "/cryptsetup.target.wants/", n); + if (!to) + return log_oom(); + +@@ -210,7 +210,7 @@ static int create_disk( + } + + free(to); +- to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL); ++ to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n); + if (!to) + return log_oom(); + +@@ -220,7 +220,7 @@ static int create_disk( + + if (!noauto && !nofail) { + _cleanup_free_ char *dmname; +- dmname = strjoin("dev-mapper-", e, ".device", NULL); ++ dmname = strjoin("dev-mapper-", e, ".device"); + if (!dmname) + return log_oom(); + +diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c +index 7f11ec724d..1ee3471355 100644 +--- a/src/debug-generator/debug-generator.c ++++ b/src/debug-generator/debug-generator.c +@@ -113,7 +113,7 @@ static int generate_mask_symlinks(void) { + STRV_FOREACH(u, arg_mask) { + _cleanup_free_ char *p = NULL; + +- p = strjoin(arg_dest, "/", *u, NULL); ++ p = strjoin(arg_dest, "/", *u); + if (!p) + return log_oom(); + +@@ -136,7 +136,7 @@ static int generate_wants_symlinks(void) { + STRV_FOREACH(u, arg_wants) { + _cleanup_free_ char *p = NULL, *f = NULL; + +- p = strjoin(arg_dest, "/", arg_default_unit, ".wants/", *u, NULL); ++ p = strjoin(arg_dest, "/", arg_default_unit, ".wants/", *u); + if (!p) + return log_oom(); + +diff --git a/src/delta/delta.c b/src/delta/delta.c +index f32744def2..6848662ccb 100644 +--- a/src/delta/delta.c ++++ b/src/delta/delta.c +@@ -214,7 +214,7 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const + + assert(!endswith(drop, "/")); + +- path = strjoin(toppath, "/", drop, NULL); ++ path = strjoin(toppath, "/", drop); + if (!path) + return -ENOMEM; + +@@ -242,7 +242,7 @@ static int enumerate_dir_d(Hashmap *top, Hashmap *bottom, Hashmap *drops, const + if (!endswith(*file, ".conf")) + continue; + +- p = strjoin(path, "/", *file, NULL); ++ p = strjoin(path, "/", *file); + if (!p) + return -ENOMEM; + d = p + strlen(toppath) + 1; +@@ -330,7 +330,7 @@ static int enumerate_dir(Hashmap *top, Hashmap *bottom, Hashmap *drops, const ch + if (!dirent_is_file(de)) + continue; + +- p = strjoin(path, "/", de->d_name, NULL); ++ p = strjoin(path, "/", de->d_name); + if (!p) + return -ENOMEM; + +@@ -383,7 +383,7 @@ static int process_suffix(const char *suffix, const char *onlyprefix) { + NULSTR_FOREACH(p, prefixes) { + _cleanup_free_ char *t = NULL; + +- t = strjoin(p, "/", suffix, NULL); ++ t = strjoin(p, "/", suffix); + if (!t) { + r = -ENOMEM; + goto finish; +diff --git a/src/escape/escape.c b/src/escape/escape.c +index 9f39049577..af98c98e40 100644 +--- a/src/escape/escape.c ++++ b/src/escape/escape.c +@@ -191,7 +191,7 @@ int main(int argc, char *argv[]) { + } else if (arg_suffix) { + char *x; + +- x = strjoin(e, ".", arg_suffix, NULL); ++ x = strjoin(e, ".", arg_suffix); + if (!x) { + r = log_oom(); + goto finish; +diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c +index e77bd71a52..46507de937 100644 +--- a/src/fstab-generator/fstab-generator.c ++++ b/src/fstab-generator/fstab-generator.c +@@ -80,7 +80,7 @@ static int add_swap( + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + +- unit = strjoin(arg_dest, "/", name, NULL); ++ unit = strjoin(arg_dest, "/", name); + if (!unit) + return log_oom(); + +@@ -275,7 +275,7 @@ static int add_mount( + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + +- unit = strjoin(arg_dest, "/", name, NULL); ++ unit = strjoin(arg_dest, "/", name); + if (!unit) + return log_oom(); + +@@ -335,7 +335,7 @@ static int add_mount( + return log_error_errno(r, "Failed to write unit file %s: %m", unit); + + if (!noauto && !automount) { +- lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", name, NULL); ++ lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", name); + if (!lnk) + return log_oom(); + +@@ -349,7 +349,7 @@ static int add_mount( + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + +- automount_unit = strjoin(arg_dest, "/", automount_name, NULL); ++ automount_unit = strjoin(arg_dest, "/", automount_name); + if (!automount_unit) + return log_oom(); + +@@ -391,7 +391,7 @@ static int add_mount( + return log_error_errno(r, "Failed to write unit file %s: %m", automount_unit); + + free(lnk); +- lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name, NULL); ++ lnk = strjoin(arg_dest, "/", post, nofail ? ".wants/" : ".requires/", automount_name); + if (!lnk) + return log_oom(); + +@@ -619,7 +619,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat + char *o; + + o = arg_root_options ? +- strjoin(arg_root_options, ",", value, NULL) : ++ strjoin(arg_root_options, ",", value) : + strdup(value); + if (!o) + return log_oom(); +@@ -641,7 +641,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat + char *o; + + o = arg_usr_options ? +- strjoin(arg_usr_options, ",", value, NULL) : ++ strjoin(arg_usr_options, ",", value) : + strdup(value); + if (!o) + return log_oom(); +diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c +index a098b27a8e..e64d5bb231 100644 +--- a/src/gpt-auto-generator/gpt-auto-generator.c ++++ b/src/gpt-auto-generator/gpt-auto-generator.c +@@ -76,7 +76,7 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + +- p = strjoin(arg_dest, "/", n, NULL); ++ p = strjoin(arg_dest, "/", n); + if (!p) + return log_oom(); + +@@ -111,7 +111,7 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi + + from = strjoina("../", n); + +- to = strjoin(arg_dest, "/", d, ".wants/", n, NULL); ++ to = strjoin(arg_dest, "/", d, ".wants/", n); + if (!to) + return log_oom(); + +@@ -120,7 +120,7 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi + return log_error_errno(errno, "Failed to create symlink %s: %m", to); + + free(to); +- to = strjoin(arg_dest, "/cryptsetup.target.requires/", n, NULL); ++ to = strjoin(arg_dest, "/cryptsetup.target.requires/", n); + if (!to) + return log_oom(); + +@@ -129,7 +129,7 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi + return log_error_errno(errno, "Failed to create symlink %s: %m", to); + + free(to); +- to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n, NULL); ++ to = strjoin(arg_dest, "/dev-mapper-", e, ".device.requires/", n); + if (!to) + return log_oom(); + +@@ -138,7 +138,7 @@ static int add_cryptsetup(const char *id, const char *what, bool rw, char **devi + return log_error_errno(errno, "Failed to create symlink %s: %m", to); + + free(p); +- p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf", NULL); ++ p = strjoin(arg_dest, "/dev-mapper-", e, ".device.d/50-job-timeout-sec-0.conf"); + if (!p) + return log_oom(); + +@@ -194,7 +194,7 @@ static int add_mount( + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + +- p = strjoin(arg_dest, "/", unit, NULL); ++ p = strjoin(arg_dest, "/", unit); + if (!p) + return log_oom(); + +@@ -236,7 +236,7 @@ static int add_mount( + return log_error_errno(r, "Failed to write unit file %s: %m", p); + + if (post) { +- lnk = strjoin(arg_dest, "/", post, ".requires/", unit, NULL); ++ lnk = strjoin(arg_dest, "/", post, ".requires/", unit); + if (!lnk) + return log_oom(); + +@@ -340,7 +340,7 @@ static int add_swap(const char *path) { + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + +- unit = strjoin(arg_dest, "/", name, NULL); ++ unit = strjoin(arg_dest, "/", name); + if (!unit) + return log_oom(); + +@@ -361,7 +361,7 @@ static int add_swap(const char *path) { + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", unit); + +- lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name, NULL); ++ lnk = strjoin(arg_dest, "/" SPECIAL_SWAP_TARGET ".wants/", name); + if (!lnk) + return log_oom(); + +@@ -393,7 +393,7 @@ static int add_automount( + assert(description); + + if (options) +- opt = strjoin(options, ",noauto", NULL); ++ opt = strjoin(options, ",noauto"); + else + opt = strdup("noauto"); + if (!opt) +@@ -414,7 +414,7 @@ static int add_automount( + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + +- p = strjoin(arg_dest, "/", unit, NULL); ++ p = strjoin(arg_dest, "/", unit); + if (!p) + return log_oom(); + +@@ -438,7 +438,7 @@ static int add_automount( + if (r < 0) + return log_error_errno(r, "Failed to write unit file %s: %m", p); + +- lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/", unit, NULL); ++ lnk = strjoin(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/", unit); + if (!lnk) + return log_oom(); + mkdir_parents_label(lnk, 0755); +diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c +index 17e670604e..9f59c04b26 100644 +--- a/src/hibernate-resume/hibernate-resume-generator.c ++++ b/src/hibernate-resume/hibernate-resume-generator.c +@@ -56,7 +56,7 @@ static int process_resume(void) { + if (r < 0) + return log_error_errno(r, "Failed to generate unit name: %m"); + +- lnk = strjoin(arg_dest, "/" SPECIAL_SYSINIT_TARGET ".wants/", name, NULL); ++ lnk = strjoin(arg_dest, "/" SPECIAL_SYSINIT_TARGET ".wants/", name); + if (!lnk) + return log_oom(); + +diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c +index 197f905b7d..74256e4444 100644 +--- a/src/hostname/hostnamed.c ++++ b/src/hostname/hostnamed.c +@@ -335,7 +335,7 @@ static int context_write_data_machine_info(Context *c) { + continue; + } + +- t = strjoin(name[p], "=", c->data[p], NULL); ++ t = strjoin(name[p], "=", c->data[p]); + if (!t) + return -ENOMEM; + +diff --git a/src/hwdb/hwdb.c b/src/hwdb/hwdb.c +index ab1feb435b..1d16d9f8aa 100644 +--- a/src/hwdb/hwdb.c ++++ b/src/hwdb/hwdb.c +@@ -669,7 +669,7 @@ static int hwdb_update(int argc, char *argv[], void *userdata) { + log_debug("strings dedup'ed: %8zu bytes (%8zu)", + trie->strings->dedup_len, trie->strings->dedup_count); + +- hwdb_bin = strjoin(arg_root, "/", arg_hwdb_bin_dir, "/hwdb.bin", NULL); ++ hwdb_bin = strjoin(arg_root, "/", arg_hwdb_bin_dir, "/hwdb.bin"); + if (!hwdb_bin) + return -ENOMEM; + +diff --git a/src/import/import-raw.c b/src/import/import-raw.c +index 29f3f896e5..808eae38f8 100644 +--- a/src/import/import-raw.c ++++ b/src/import/import-raw.c +@@ -267,7 +267,7 @@ static int raw_import_open_disk(RawImport *i) { + assert(!i->temp_path); + assert(i->output_fd < 0); + +- i->final_path = strjoin(i->image_root, "/", i->local, ".raw", NULL); ++ i->final_path = strjoin(i->image_root, "/", i->local, ".raw"); + if (!i->final_path) + return log_oom(); + +diff --git a/src/import/import-tar.c b/src/import/import-tar.c +index 22f9b8c5ea..1c229ec82f 100644 +--- a/src/import/import-tar.c ++++ b/src/import/import-tar.c +@@ -222,7 +222,7 @@ static int tar_import_fork_tar(TarImport *i) { + assert(!i->temp_path); + assert(i->tar_fd < 0); + +- i->final_path = strjoin(i->image_root, "/", i->local, NULL); ++ i->final_path = strjoin(i->image_root, "/", i->local); + if (!i->final_path) + return log_oom(); + +diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c +index 7325adee8f..f75a6f06d2 100644 +--- a/src/journal-remote/journal-gatewayd.c ++++ b/src/journal-remote/journal-gatewayd.c +@@ -434,7 +434,7 @@ static int request_parse_arguments_iterator( + return MHD_YES; + } + +- p = strjoin(key, "=", strempty(value), NULL); ++ p = strjoin(key, "=", strempty(value)); + if (!p) { + m->argument_parse_error = log_oom(); + return MHD_NO; +diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c +index 61190ff83c..42880cca76 100644 +--- a/src/journal-remote/journal-upload.c ++++ b/src/journal-remote/journal-upload.c +@@ -438,7 +438,7 @@ static int setup_uploader(Uploader *u, const char *url, const char *state_file) + } + + if (strchr(host, ':')) +- u->url = strjoin(proto, url, "/upload", NULL); ++ u->url = strjoin(proto, url, "/upload"); + else { + char *t; + size_t x; +@@ -448,7 +448,7 @@ static int setup_uploader(Uploader *u, const char *url, const char *state_file) + while (x > 0 && t[x - 1] == '/') + t[x - 1] = '\0'; + +- u->url = strjoin(proto, t, ":" STRINGIFY(DEFAULT_PORT), "/upload", NULL); ++ u->url = strjoin(proto, t, ":" STRINGIFY(DEFAULT_PORT), "/upload"); + } + if (!u->url) + return log_oom(); +diff --git a/src/journal/journalctl.c b/src/journal/journalctl.c +index 7f997487b4..22cab67824 100644 +--- a/src/journal/journalctl.c ++++ b/src/journal/journalctl.c +@@ -192,7 +192,7 @@ static int add_matches_for_device(sd_journal *j, const char *devpath) { + continue; + } + +- match = strjoin("_KERNEL_DEVICE=+", subsys, ":", sysname, NULL); ++ match = strjoin("_KERNEL_DEVICE=+", subsys, ":", sysname); + if (!match) + return log_oom(); + +diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c +index ced0ad6f21..575070b343 100644 +--- a/src/journal/journald-server.c ++++ b/src/journal/journald-server.c +@@ -730,7 +730,7 @@ static int get_invocation_id(const char *cgroup_root, const char *slice, const c + if (!escaped) + return -ENOMEM; + +- p = strjoin(cgroup_root, "/", slice_path, "/", escaped, NULL); ++ p = strjoin(cgroup_root, "/", slice_path, "/", escaped); + if (!p) + return -ENOMEM; + +@@ -2052,8 +2052,8 @@ int server_init(Server *s) { + s->runtime_storage.name = "Runtime journal"; + s->system_storage.name = "System journal"; + +- s->runtime_storage.path = strjoin("/run/log/journal/", SERVER_MACHINE_ID(s), NULL); +- s->system_storage.path = strjoin("/var/log/journal/", SERVER_MACHINE_ID(s), NULL); ++ s->runtime_storage.path = strjoin("/run/log/journal/", SERVER_MACHINE_ID(s)); ++ s->system_storage.path = strjoin("/var/log/journal/", SERVER_MACHINE_ID(s)); + if (!s->runtime_storage.path || !s->system_storage.path) + return -ENOMEM; + +diff --git a/src/journal/journald-wall.c b/src/journal/journald-wall.c +index 4d91fafffe..d857ff8d39 100644 +--- a/src/journal/journald-wall.c ++++ b/src/journal/journald-wall.c +@@ -57,7 +57,7 @@ void server_forward_wall( + + } else if (identifier) { + +- l = l_buf = strjoin(identifier, ": ", message, NULL); ++ l = l_buf = strjoin(identifier, ": ", message); + if (!l_buf) { + log_oom(); + return; +diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c +index f2f8546086..47c77b1ba9 100644 +--- a/src/journal/sd-journal.c ++++ b/src/journal/sd-journal.c +@@ -405,7 +405,7 @@ static char *match_make_string(Match *m) { + return mfree(p); + + if (p) { +- k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t, NULL); ++ k = strjoin(p, m->type == MATCH_OR_TERM ? " OR " : " AND ", t); + free(p); + free(t); + +@@ -420,7 +420,7 @@ static char *match_make_string(Match *m) { + } + + if (enclose) { +- r = strjoin("(", p, ")", NULL); ++ r = strjoin("(", p, ")"); + free(p); + return r; + } +@@ -1416,7 +1416,7 @@ static int add_directory(sd_journal *j, const char *prefix, const char *dirname) + * and reenumerates directory contents */ + + if (dirname) +- path = strjoin(prefix, "/", dirname, NULL); ++ path = strjoin(prefix, "/", dirname); + else + path = strdup(prefix); + if (!path) { +diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c +index 59398b841d..a3427ca33e 100644 +--- a/src/libsystemd/sd-bus/bus-kernel.c ++++ b/src/libsystemd/sd-bus/bus-kernel.c +@@ -1649,7 +1649,7 @@ int bus_kernel_create_bus(const char *name, bool world, char **s) { + if (s) { + char *p; + +- p = strjoin("/sys/fs/kdbus/", n->str, "/bus", NULL); ++ p = strjoin("/sys/fs/kdbus/", n->str, "/bus"); + if (!p) { + safe_close(fd); + return -ENOMEM; +diff --git a/src/libsystemd/sd-bus/busctl-introspect.c b/src/libsystemd/sd-bus/busctl-introspect.c +index b09509f8e1..09cbd9ab44 100644 +--- a/src/libsystemd/sd-bus/busctl-introspect.c ++++ b/src/libsystemd/sd-bus/busctl-introspect.c +@@ -285,7 +285,7 @@ static int parse_xml_node(Context *context, const char *prefix, unsigned n_depth + if (endswith(prefix, "/")) + node_path = strappend(prefix, name); + else +- node_path = strjoin(prefix, "/", name, NULL); ++ node_path = strjoin(prefix, "/", name); + if (!node_path) + return log_oom(); + } +diff --git a/src/libsystemd/sd-bus/busctl.c b/src/libsystemd/sd-bus/busctl.c +index 2c3f591053..9dd3828364 100644 +--- a/src/libsystemd/sd-bus/busctl.c ++++ b/src/libsystemd/sd-bus/busctl.c +@@ -1102,7 +1102,7 @@ static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FIL + return -EINVAL; + } + +- m = strjoin("sender='", *i, "'", NULL); ++ m = strjoin("sender='", *i, "'"); + if (!m) + return log_oom(); + +@@ -1111,7 +1111,7 @@ static int monitor(sd_bus *bus, char *argv[], int (*dump)(sd_bus_message *m, FIL + return bus_log_create_error(r); + + free(m); +- m = strjoin("destination='", *i, "'", NULL); ++ m = strjoin("destination='", *i, "'"); + if (!m) + return log_oom(); + +diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c +index d746348544..e809942278 100644 +--- a/src/libsystemd/sd-bus/sd-bus.c ++++ b/src/libsystemd/sd-bus/sd-bus.c +@@ -1339,7 +1339,7 @@ int bus_set_address_system_remote(sd_bus *b, const char *host) { + return -ENOMEM; + } + +- b->address = strjoin("unixexec:path=ssh,argv1=-xT,argv2=", e, ",argv3=systemd-stdio-bridge", c, NULL); ++ b->address = strjoin("unixexec:path=ssh,argv1=-xT,argv2=", e, ",argv3=systemd-stdio-bridge", c); + if (!b->address) + return -ENOMEM; + +@@ -1387,7 +1387,7 @@ int bus_set_address_system_machine(sd_bus *b, const char *machine) { + if (!e) + return -ENOMEM; + +- b->address = strjoin("x-machine-kernel:machine=", e, ";x-machine-unix:machine=", e, NULL); ++ b->address = strjoin("x-machine-kernel:machine=", e, ";x-machine-unix:machine=", e); + if (!b->address) + return -ENOMEM; + +@@ -3470,7 +3470,7 @@ _public_ int sd_bus_path_encode(const char *prefix, const char *external_id, cha + if (!e) + return -ENOMEM; + +- ret = strjoin(prefix, "/", e, NULL); ++ ret = strjoin(prefix, "/", e); + if (!ret) + return -ENOMEM; + +diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c +index f11cafd888..233a21a523 100644 +--- a/src/libsystemd/sd-bus/test-bus-objects.c ++++ b/src/libsystemd/sd-bus/test-bus-objects.c +@@ -49,7 +49,7 @@ static int something_handler(sd_bus_message *m, void *userdata, sd_bus_error *er + r = sd_bus_message_read(m, "s", &s); + assert_se(r > 0); + +- n = strjoin("<<<", s, ">>>", NULL); ++ n = strjoin("<<<", s, ">>>"); + assert_se(n); + + free(c->something); +diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c +index 62d03ae00d..86f8935a14 100644 +--- a/src/libsystemd/sd-device/device-enumerator.c ++++ b/src/libsystemd/sd-device/device-enumerator.c +@@ -773,7 +773,7 @@ static int parent_crawl_children(sd_device_enumerator *enumerator, const char *p + if (dent->d_type != DT_DIR) + continue; + +- child = strjoin(path, "/", dent->d_name, NULL); ++ child = strjoin(path, "/", dent->d_name); + if (!child) + return -ENOMEM; + +diff --git a/src/libsystemd/sd-path/sd-path.c b/src/libsystemd/sd-path/sd-path.c +index b7aec1f20a..752c1ba56b 100644 +--- a/src/libsystemd/sd-path/sd-path.c ++++ b/src/libsystemd/sd-path/sd-path.c +@@ -77,7 +77,7 @@ static int from_home_dir(const char *envname, const char *suffix, char **buffer, + if (endswith(h, "/")) + cc = strappend(h, suffix); + else +- cc = strjoin(h, "/", suffix, NULL); ++ cc = strjoin(h, "/", suffix); + if (!cc) + return -ENOMEM; + +@@ -387,7 +387,7 @@ _public_ int sd_path_home(uint64_t type, const char *suffix, char **path) { + if (endswith(ret, "/")) + cc = strappend(ret, suffix); + else +- cc = strjoin(ret, "/", suffix, NULL); ++ cc = strjoin(ret, "/", suffix); + + free(buffer); + +@@ -455,7 +455,7 @@ static int search_from_environment( + if (endswith(e, "/")) + h = strappend(e, home_suffix); + else +- h = strjoin(e, "/", home_suffix, NULL); ++ h = strjoin(e, "/", home_suffix); + + if (!h) { + strv_free(l); +@@ -621,7 +621,7 @@ _public_ int sd_path_search(uint64_t type, const char *suffix, char ***paths) { + if (endswith(*i, "/")) + *j = strappend(*i, suffix); + else +- *j = strjoin(*i, "/", suffix, NULL); ++ *j = strjoin(*i, "/", suffix); + + if (!*j) { + strv_free(l); +diff --git a/src/locale/keymap-util.c b/src/locale/keymap-util.c +index a6bcd1ad54..da72bee4a9 100644 +--- a/src/locale/keymap-util.c ++++ b/src/locale/keymap-util.c +@@ -519,7 +519,7 @@ int find_converted_keymap(const char *x11_layout, const char *x11_variant, char + _cleanup_free_ char *n; + + if (x11_variant) +- n = strjoin(x11_layout, "-", x11_variant, NULL); ++ n = strjoin(x11_layout, "-", x11_variant); + else + n = strdup(x11_layout); + if (!n) +@@ -529,8 +529,8 @@ int find_converted_keymap(const char *x11_layout, const char *x11_variant, char + _cleanup_free_ char *p = NULL, *pz = NULL; + bool uncompressed; + +- p = strjoin(dir, "xkb/", n, ".map", NULL); +- pz = strjoin(dir, "xkb/", n, ".map.gz", NULL); ++ p = strjoin(dir, "xkb/", n, ".map"); ++ pz = strjoin(dir, "xkb/", n, ".map.gz"); + if (!p || !pz) + return -ENOMEM; + +diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c +index c93b24009b..a7e5c01ef3 100644 +--- a/src/login/logind-inhibit.c ++++ b/src/login/logind-inhibit.c +@@ -294,7 +294,7 @@ int inhibitor_create_fifo(Inhibitor *i) { + if (r < 0) + return r; + +- i->fifo_path = strjoin("/run/systemd/inhibit/", i->id, ".ref", NULL); ++ i->fifo_path = strjoin("/run/systemd/inhibit/", i->id, ".ref"); + if (!i->fifo_path) + return -ENOMEM; + +diff --git a/src/login/logind-session.c b/src/login/logind-session.c +index cbf035f706..e83c37a5ac 100644 +--- a/src/login/logind-session.c ++++ b/src/login/logind-session.c +@@ -505,7 +505,7 @@ static int session_start_scope(Session *s) { + char *scope, *job = NULL; + const char *description; + +- scope = strjoin("session-", s->id, ".scope", NULL); ++ scope = strjoin("session-", s->id, ".scope"); + if (!scope) + return log_oom(); + +diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c +index 4f023640f6..b0f75b2a21 100644 +--- a/src/login/pam_systemd.c ++++ b/src/login/pam_systemd.c +@@ -186,7 +186,7 @@ static int export_legacy_dbus_address( + * daemons that spawn dbus-daemon, instead of forcing + * DBUS_SESSION_BUS_ADDRESS= here. */ + +- s = strjoin(runtime, "/bus", NULL); ++ s = strjoin(runtime, "/bus"); + if (!s) + goto error; + +diff --git a/src/machine/machine.c b/src/machine/machine.c +index a02b9d7575..9e7b6cc03d 100644 +--- a/src/machine/machine.c ++++ b/src/machine/machine.c +@@ -355,7 +355,7 @@ static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_er + if (!escaped) + return log_oom(); + +- scope = strjoin("machine-", escaped, ".scope", NULL); ++ scope = strjoin("machine-", escaped, ".scope"); + if (!scope) + return log_oom(); + +diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c +index 80bba086e4..9d79e81918 100644 +--- a/src/mount/mount-tool.c ++++ b/src/mount/mount-tool.c +@@ -697,7 +697,7 @@ static int acquire_mount_where(struct udev_device *d) { + if (!filename_is_valid(escaped)) + return 0; + +- arg_mount_where = strjoin("/run/media/system/", escaped, NULL); ++ arg_mount_where = strjoin("/run/media/system/", escaped); + } else + arg_mount_where = strdup(v); + +@@ -721,7 +721,7 @@ static int acquire_description(struct udev_device *d) { + label = udev_device_get_property_value(d, "ID_PART_ENTRY_NUMBER"); + + if (model && label) +- arg_description = strjoin(model, " ", label, NULL); ++ arg_description = strjoin(model, " ", label); + else if (label) + arg_description = strdup(label); + else if (model) +diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c +index 115de64cf9..52fa0ee907 100644 +--- a/src/nspawn/nspawn-mount.c ++++ b/src/nspawn/nspawn-mount.c +@@ -208,9 +208,9 @@ static int tmpfs_patch_options( + char *t; + + if (options) +- t = strjoin(options, ",context=\"", selinux_apifs_context, "\"", NULL); ++ t = strjoin(options, ",context=\"", selinux_apifs_context, "\""); + else +- t = strjoin("context=\"", selinux_apifs_context, "\"", NULL); ++ t = strjoin("context=\"", selinux_apifs_context, "\""); + if (!t) { + free(buf); + return -ENOMEM; +diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c +index 0e2f4d59a5..d9abd566bb 100644 +--- a/src/nspawn/nspawn.c ++++ b/src/nspawn/nspawn.c +@@ -2567,7 +2567,7 @@ static int determine_names(void) { + * search for a machine, but instead create a new one + * in /var/lib/machine. */ + +- arg_directory = strjoin("/var/lib/machines/", arg_machine, NULL); ++ arg_directory = strjoin("/var/lib/machines/", arg_machine); + if (!arg_directory) + return log_oom(); + } +@@ -3410,7 +3410,7 @@ static int load_settings(void) { + FOREACH_STRING(i, "/etc/systemd/nspawn", "/run/systemd/nspawn") { + _cleanup_free_ char *j = NULL; + +- j = strjoin(i, "/", fn, NULL); ++ j = strjoin(i, "/", fn); + if (!j) + return log_oom(); + +diff --git a/src/rc-local-generator/rc-local-generator.c b/src/rc-local-generator/rc-local-generator.c +index 618bbe428d..b704ca3b4b 100644 +--- a/src/rc-local-generator/rc-local-generator.c ++++ b/src/rc-local-generator/rc-local-generator.c +@@ -45,11 +45,11 @@ static int add_symlink(const char *service, const char *where) { + assert(service); + assert(where); + +- from = strjoin(SYSTEM_DATA_UNIT_PATH, "/", service, NULL); ++ from = strjoin(SYSTEM_DATA_UNIT_PATH, "/", service); + if (!from) + return log_oom(); + +- to = strjoin(arg_dest, "/", where, ".wants/", service, NULL); ++ to = strjoin(arg_dest, "/", where, ".wants/", service); + if (!to) + return log_oom(); + +diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c +index d4a267c89f..51327105d0 100644 +--- a/src/resolve/resolved-dns-dnssec.c ++++ b/src/resolve/resolved-dns-dnssec.c +@@ -1303,7 +1303,7 @@ static int nsec3_hashed_domain_format(const uint8_t *hashed, size_t hashed_size, + if (!l) + return -ENOMEM; + +- j = strjoin(l, ".", zone, NULL); ++ j = strjoin(l, ".", zone); + if (!j) + return -ENOMEM; + +diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c +index 87e4abec6e..209d565033 100644 +--- a/src/resolve/resolved-dns-rr.c ++++ b/src/resolve/resolved-dns-rr.c +@@ -792,7 +792,7 @@ static char *format_types(Bitmap *types) { + if (!str) + return NULL; + +- return strjoin("( ", str, " )", NULL); ++ return strjoin("( ", str, " )"); + } + + static char *format_txt(DnsTxtItem *first) { +@@ -861,14 +861,14 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { + case DNS_TYPE_NS: + case DNS_TYPE_CNAME: + case DNS_TYPE_DNAME: +- s = strjoin(k, " ", rr->ptr.name, NULL); ++ s = strjoin(k, " ", rr->ptr.name); + if (!s) + return NULL; + + break; + + case DNS_TYPE_HINFO: +- s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os, NULL); ++ s = strjoin(k, " ", rr->hinfo.cpu, " ", rr->hinfo.os); + if (!s) + return NULL; + break; +@@ -879,7 +879,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { + if (!t) + return NULL; + +- s = strjoin(k, " ", t, NULL); ++ s = strjoin(k, " ", t); + if (!s) + return NULL; + break; +@@ -891,7 +891,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { + if (r < 0) + return NULL; + +- s = strjoin(k, " ", x, NULL); ++ s = strjoin(k, " ", x); + if (!s) + return NULL; + break; +@@ -902,7 +902,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { + if (r < 0) + return NULL; + +- s = strjoin(k, " ", t, NULL); ++ s = strjoin(k, " ", t); + if (!s) + return NULL; + break; +@@ -942,7 +942,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { + if (!t) + return NULL; + +- s = strjoin(k, " ", t, NULL); ++ s = strjoin(k, " ", t); + if (!s) + return NULL; + break; +diff --git a/src/resolve/test-dnssec-complex.c b/src/resolve/test-dnssec-complex.c +index 58c089eb40..3d7074af11 100644 +--- a/src/resolve/test-dnssec-complex.c ++++ b/src/resolve/test-dnssec-complex.c +@@ -42,7 +42,7 @@ static void prefix_random(const char *name, char **ret) { + char *x; + + assert_se(asprintf(&b, "x%" PRIu64 "x", random_u64())); +- x = strjoin(b, ".", name, NULL); ++ x = strjoin(b, ".", name); + assert_se(x); + + free(m); +diff --git a/src/rfkill/rfkill.c b/src/rfkill/rfkill.c +index 0acdf229ed..c0f138b4f4 100644 +--- a/src/rfkill/rfkill.c ++++ b/src/rfkill/rfkill.c +@@ -184,9 +184,9 @@ static int determine_state_file( + if (!escaped_path_id) + return log_oom(); + +- state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type, NULL); ++ state_file = strjoin("/var/lib/systemd/rfkill/", escaped_path_id, ":", type); + } else +- state_file = strjoin("/var/lib/systemd/rfkill/", type, NULL); ++ state_file = strjoin("/var/lib/systemd/rfkill/", type); + + if (!state_file) + return log_oom(); +diff --git a/src/run/run.c b/src/run/run.c +index 81b53fdfab..8f1cc9e2e3 100644 +--- a/src/run/run.c ++++ b/src/run/run.c +@@ -747,7 +747,7 @@ static int make_unit_name(sd_bus *bus, UnitType t, char **ret) { + return -EINVAL; + } + +- p = strjoin("run-u", id, ".", unit_type_to_string(t), NULL); ++ p = strjoin("run-u", id, ".", unit_type_to_string(t)); + if (!p) + return log_oom(); + +diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c +index 59a34a9d11..f1fbce9dca 100644 +--- a/src/shared/base-filesystem.c ++++ b/src/shared/base-filesystem.c +@@ -82,7 +82,7 @@ int base_filesystem_create(const char *root, uid_t uid, gid_t gid) { + if (table[i].exists) { + _cleanup_free_ char *p = NULL; + +- p = strjoin(s, "/", table[i].exists, NULL); ++ p = strjoin(s, "/", table[i].exists); + if (!p) + return log_oom(); + +diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c +index bb90c89cc2..3b8768b9a7 100644 +--- a/src/shared/bus-util.c ++++ b/src/shared/bus-util.c +@@ -676,7 +676,7 @@ int bus_connect_user_systemd(sd_bus **_bus) { + if (r < 0) + return r; + +- bus->address = strjoin("unix:path=", ee, "/systemd/private", NULL); ++ bus->address = strjoin("unix:path=", ee, "/systemd/private"); + if (!bus->address) + return -ENOMEM; + +@@ -1460,7 +1460,7 @@ int bus_path_encode_unique(sd_bus *b, const char *prefix, const char *sender_id, + if (!external_label) + return -ENOMEM; + +- p = strjoin(prefix, "/", sender_label, "/", external_label, NULL); ++ p = strjoin(prefix, "/", sender_label, "/", external_label); + if (!p) + return -ENOMEM; + +diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c +index 3e451db715..4db6cefb6e 100644 +--- a/src/shared/cgroup-show.c ++++ b/src/shared/cgroup-show.c +@@ -158,7 +158,7 @@ int show_cgroup_by_path( + while ((r = cg_read_subgroup(d, &gn)) > 0) { + _cleanup_free_ char *k = NULL; + +- k = strjoin(fn, "/", gn, NULL); ++ k = strjoin(fn, "/", gn); + free(gn); + if (!k) + return -ENOMEM; +diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c +index 2ec0155b71..19a371c865 100644 +--- a/src/shared/conf-parser.c ++++ b/src/shared/conf-parser.c +@@ -101,7 +101,7 @@ int config_item_perf_lookup( + else { + char *key; + +- key = strjoin(section, ".", lvalue, NULL); ++ key = strjoin(section, ".", lvalue); + if (!key) + return -ENOMEM; + +diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c +index 892f0aadf5..f9a6fd5f03 100644 +--- a/src/shared/dns-domain.c ++++ b/src/shared/dns-domain.c +@@ -1076,7 +1076,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do + if (!name) + return -ENOMEM; + +- type = strjoin(b, ".", c, NULL); ++ type = strjoin(b, ".", c); + if (!type) + return -ENOMEM; + +@@ -1090,7 +1090,7 @@ int dns_service_split(const char *joined, char **_name, char **_type, char **_do + + name = NULL; + +- type = strjoin(a, ".", b, NULL); ++ type = strjoin(a, ".", b); + if (!type) + return -ENOMEM; + +diff --git a/src/shared/dropin.c b/src/shared/dropin.c +index b9cd952ac8..2c1cd84df5 100644 +--- a/src/shared/dropin.c ++++ b/src/shared/dropin.c +@@ -61,11 +61,11 @@ int drop_in_file(const char *dir, const char *unit, unsigned level, + if (!filename_is_valid(b)) + return -EINVAL; + +- p = strjoin(dir, "/", unit, ".d", NULL); ++ p = strjoin(dir, "/", unit, ".d"); + if (!p) + return -ENOMEM; + +- q = strjoin(p, "/", prefix, "-", b, ".conf", NULL); ++ q = strjoin(p, "/", prefix, "-", b, ".conf"); + if (!q) { + free(p); + return -ENOMEM; +@@ -163,7 +163,7 @@ static int iterate_dir( + if (hidden_or_backup_file(de->d_name)) + continue; + +- f = strjoin(path, "/", de->d_name, NULL); ++ f = strjoin(path, "/", de->d_name); + if (!f) + return log_oom(); + +@@ -192,7 +192,7 @@ int unit_file_process_dir( + assert(name); + assert(suffix); + +- path = strjoin(unit_path, "/", name, suffix, NULL); ++ path = strjoin(unit_path, "/", name, suffix); + if (!path) + return log_oom(); + +@@ -207,7 +207,7 @@ int unit_file_process_dir( + if (r < 0) + return log_error_errno(r, "Failed to generate template from unit name: %m"); + +- p = strjoin(unit_path, "/", template, suffix, NULL); ++ p = strjoin(unit_path, "/", template, suffix); + if (!p) + return log_oom(); + +diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c +index a4e0cd3267..f0bfb30bb5 100644 +--- a/src/shared/fstab-util.c ++++ b/src/shared/fstab-util.c +@@ -241,7 +241,7 @@ static char *tag_to_udev_node(const char *tagvalue, const char *by) { + if (encode_devnode_name(u, t, enc_len) < 0) + return NULL; + +- return strjoin("/dev/disk/by-", by, "/", t, NULL); ++ return strjoin("/dev/disk/by-", by, "/", t); + } + + char *fstab_node_to_udev_node(const char *p) { +diff --git a/src/shared/install-printf.c b/src/shared/install-printf.c +index cbdf66827f..007c632f35 100644 +--- a/src/shared/install-printf.c ++++ b/src/shared/install-printf.c +@@ -45,7 +45,7 @@ static int specifier_prefix_and_instance(char specifier, void *data, void *userd + if (endswith(prefix, "@") && i->default_instance) { + char *ans; + +- ans = strjoin(prefix, i->default_instance, NULL); ++ ans = strjoin(prefix, i->default_instance); + if (!ans) + return -ENOMEM; + *ret = ans; +diff --git a/src/shared/install.c b/src/shared/install.c +index 0bb47ac527..379d61f976 100644 +--- a/src/shared/install.c ++++ b/src/shared/install.c +@@ -1307,7 +1307,7 @@ static int unit_file_search( + STRV_FOREACH(p, paths->search_path) { + _cleanup_free_ char *path = NULL; + +- path = strjoin(*p, "/", info->name, NULL); ++ path = strjoin(*p, "/", info->name); + if (!path) + return -ENOMEM; + +@@ -1332,7 +1332,7 @@ static int unit_file_search( + STRV_FOREACH(p, paths->search_path) { + _cleanup_free_ char *path = NULL; + +- path = strjoin(*p, "/", template, NULL); ++ path = strjoin(*p, "/", template); + if (!path) + return -ENOMEM; + +@@ -1612,7 +1612,7 @@ static int install_info_symlink_wants( + continue; + } + +- path = strjoin(config_path, "/", dst, suffix, n, NULL); ++ path = strjoin(config_path, "/", dst, suffix, n); + if (!path) + return -ENOMEM; + +@@ -1646,7 +1646,7 @@ static int install_info_symlink_link( + if (r > 0) + return 0; + +- path = strjoin(config_path, "/", i->name, NULL); ++ path = strjoin(config_path, "/", i->name); + if (!path) + return -ENOMEM; + +@@ -2174,7 +2174,7 @@ int unit_file_revert( + STRV_FOREACH(j, fs) { + _cleanup_free_ char *t = NULL; + +- t = strjoin(*i, "/", *j, NULL); ++ t = strjoin(*i, "/", *j); + if (!t) + return -ENOMEM; + +diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c +index 060f8d50c7..6414ba5246 100644 +--- a/src/shared/machine-image.c ++++ b/src/shared/machine-image.c +@@ -131,7 +131,7 @@ static int image_new( + return -ENOMEM; + + if (path) +- i->path = strjoin(path, "/", filename, NULL); ++ i->path = strjoin(path, "/", filename); + else + i->path = strdup(filename); + +diff --git a/src/shared/path-lookup.c b/src/shared/path-lookup.c +index 862096ae7b..a23d09967e 100644 +--- a/src/shared/path-lookup.c ++++ b/src/shared/path-lookup.c +@@ -70,7 +70,7 @@ static int user_config_dir(char **ret, const char *suffix) { + if (!home) + return -ENXIO; + +- j = strjoin(home, "/.config", suffix, NULL); ++ j = strjoin(home, "/.config", suffix); + } + + if (!j) +@@ -102,7 +102,7 @@ static int user_data_dir(char **ret, const char *suffix) { + return -ENXIO; + + +- j = strjoin(home, "/.local/share", suffix, NULL); ++ j = strjoin(home, "/.local/share", suffix); + } + if (!j) + return -ENOMEM; +diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c +index 1a4a201740..b598a2a19d 100644 +--- a/src/systemctl/systemctl.c ++++ b/src/systemctl/systemctl.c +@@ -529,7 +529,7 @@ static int output_units_list(const UnitInfo *unit_infos, unsigned c) { + } + + if (u->machine) { +- j = strjoin(u->machine, ":", u->id, NULL); ++ j = strjoin(u->machine, ":", u->id); + if (!j) + return log_oom(); + +@@ -941,7 +941,7 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) { + char **a; + + if (s->machine) { +- j = strjoin(s->machine, ":", s->path, NULL); ++ j = strjoin(s->machine, ":", s->path); + if (!j) + return log_oom(); + path = j; +@@ -1225,7 +1225,7 @@ static int output_timers_list(struct timer_info *timer_infos, unsigned n) { + format_timestamp_relative(trel2, sizeof(trel2), t->last_trigger); + + if (t->machine) { +- j = strjoin(t->machine, ":", t->id, NULL); ++ j = strjoin(t->machine, ":", t->id); + if (!j) + return log_oom(); + unit = j; +@@ -6528,12 +6528,12 @@ static int get_file_to_edit( + assert(name); + assert(ret_path); + +- path = strjoin(paths->persistent_config, "/", name, NULL); ++ path = strjoin(paths->persistent_config, "/", name); + if (!path) + return log_oom(); + + if (arg_runtime) { +- run = strjoin(paths->runtime_config, "/", name, NULL); ++ run = strjoin(paths->runtime_config, "/", name); + if (!run) + return log_oom(); + } +diff --git a/src/sysv-generator/sysv-generator.c b/src/sysv-generator/sysv-generator.c +index 212cf7a988..9fde9b1884 100644 +--- a/src/sysv-generator/sysv-generator.c ++++ b/src/sysv-generator/sysv-generator.c +@@ -566,7 +566,7 @@ static int load_sysv(SysvStub *s) { + char *d = NULL; + + if (chkconfig_description) +- d = strjoin(chkconfig_description, " ", j, NULL); ++ d = strjoin(chkconfig_description, " ", j); + else + d = strdup(j); + if (!d) +@@ -628,7 +628,7 @@ static int load_sysv(SysvStub *s) { + char *d = NULL; + + if (long_description) +- d = strjoin(long_description, " ", t, NULL); ++ d = strjoin(long_description, " ", t); + else + d = strdup(j); + if (!d) +@@ -807,7 +807,7 @@ static int enumerate_sysv(const LookupPaths *lp, Hashmap *all_services) { + continue; + } + +- fpath = strjoin(*path, "/", de->d_name, NULL); ++ fpath = strjoin(*path, "/", de->d_name); + if (!fpath) + return log_oom(); + +@@ -853,7 +853,7 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic + _cleanup_free_ char *path = NULL; + struct dirent *de; + +- path = strjoin(*p, "/", rcnd_table[i].path, NULL); ++ path = strjoin(*p, "/", rcnd_table[i].path); + if (!path) { + r = log_oom(); + goto finish; +@@ -883,7 +883,7 @@ static int set_dependencies_from_rcnd(const LookupPaths *lp, Hashmap *all_servic + if (a < 0 || b < 0) + continue; + +- fpath = strjoin(*p, "/", de->d_name, NULL); ++ fpath = strjoin(*p, "/", de->d_name); + if (!fpath) { + r = log_oom(); + goto finish; +diff --git a/src/test/test-copy.c b/src/test/test-copy.c +index ed1ea51dbd..91e4e5b409 100644 +--- a/src/test/test-copy.c ++++ b/src/test/test-copy.c +@@ -141,8 +141,8 @@ static void test_copy_tree(void) { + STRV_FOREACH_PAIR(link, p, links) { + _cleanup_free_ char *target = NULL, *f, *l; + +- assert_se(f = strjoin(original_dir, *p, NULL)); +- assert_se(l = strjoin(copy_dir, *link, NULL)); ++ assert_se(f = strjoin(original_dir, *p)); ++ assert_se(l = strjoin(copy_dir, *link)); + + assert_se(readlink_and_canonicalize(l, &target) == 0); + assert_se(path_equal(f, target)); +diff --git a/src/test/test-date.c b/src/test/test-date.c +index 7f497bb7d5..a8d3f1e083 100644 +--- a/src/test/test-date.c ++++ b/src/test/test-date.c +@@ -60,7 +60,7 @@ static void test_one(const char *p) { + _cleanup_free_ char *with_utc; + + log_info("Test: %s", p); +- with_utc = strjoin(p, " UTC", NULL); ++ with_utc = strjoin(p, " UTC"); + test_should_pass(p); + test_should_pass(with_utc); + } +@@ -69,7 +69,7 @@ static void test_one_noutc(const char *p) { + _cleanup_free_ char *with_utc; + + log_info("Test: %s", p); +- with_utc = strjoin(p, " UTC", NULL); ++ with_utc = strjoin(p, " UTC"); + test_should_pass(p); + test_should_fail(with_utc); + } +diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c +index 92663ef66f..56316904a3 100644 +--- a/src/test/test-fileio.c ++++ b/src/test/test-fileio.c +@@ -367,7 +367,7 @@ static void test_write_string_file_verify(void) { + int r; + + assert_se(read_one_line_file("/proc/cmdline", &buf) >= 0); +- assert_se((buf2 = strjoin(buf, "\n", NULL))); ++ assert_se((buf2 = strjoin(buf, "\n"))); + + r = write_string_file("/proc/cmdline", buf, 0); + assert_se(r == -EACCES || r == -EIO); +diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c +index ff9f35cecd..de7be1f9cc 100644 +--- a/src/test/test-namespace.c ++++ b/src/test/test-namespace.c +@@ -132,14 +132,14 @@ int main(int argc, char *argv[]) { + assert_se(sd_id128_get_boot(&bid) >= 0); + sd_id128_to_string(bid, boot_id); + +- x = strjoin("/tmp/systemd-private-", boot_id, "-abcd.service-", NULL); +- y = strjoin("/var/tmp/systemd-private-", boot_id, "-abcd.service-", NULL); ++ x = strjoin("/tmp/systemd-private-", boot_id, "-abcd.service-"); ++ y = strjoin("/var/tmp/systemd-private-", boot_id, "-abcd.service-"); + assert_se(x && y); + + test_tmpdir("abcd.service", x, y); + +- z = strjoin("/tmp/systemd-private-", boot_id, "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-", NULL); +- zz = strjoin("/var/tmp/systemd-private-", boot_id, "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-", NULL); ++ z = strjoin("/tmp/systemd-private-", boot_id, "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-"); ++ zz = strjoin("/var/tmp/systemd-private-", boot_id, "-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-"); + + assert_se(z && zz); + +diff --git a/src/test/test-path.c b/src/test/test-path.c +index 4d3f0e9948..5e99d478ee 100644 +--- a/src/test/test-path.c ++++ b/src/test/test-path.c +@@ -56,7 +56,7 @@ static int setup_test(Manager **m) { + STRV_FOREACH(test_path, tests_path) { + _cleanup_free_ char *p = NULL; + +- p = strjoin("/tmp/test-path_", *test_path, NULL); ++ p = strjoin("/tmp/test-path_", *test_path); + assert_se(p); + + (void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL); +diff --git a/src/test/test-replace-var.c b/src/test/test-replace-var.c +index 297effce79..60e05d04c9 100644 +--- a/src/test/test-replace-var.c ++++ b/src/test/test-replace-var.c +@@ -25,7 +25,7 @@ + #include "util.h" + + static char *lookup(const char *variable, void *userdata) { +- return strjoin("<<<", variable, ">>>", NULL); ++ return strjoin("<<<", variable, ">>>"); + } + + int main(int argc, char *argv[]) { +diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c +index 954f4aa985..97a564d5a2 100644 +--- a/src/tmpfiles/tmpfiles.c ++++ b/src/tmpfiles/tmpfiles.c +@@ -422,7 +422,7 @@ static int dir_cleanup( + continue; + } + +- sub_path = strjoin(p, "/", dent->d_name, NULL); ++ sub_path = strjoin(p, "/", dent->d_name); + if (!sub_path) { + r = log_oom(); + goto finish; +@@ -1082,7 +1082,7 @@ static int item_do_children(Item *i, const char *path, action_t action) { + if (STR_IN_SET(de->d_name, ".", "..")) + continue; + +- p = strjoin(path, "/", de->d_name, NULL); ++ p = strjoin(path, "/", de->d_name); + if (!p) + return -ENOMEM; + +diff --git a/src/udev/udev-builtin-hwdb.c b/src/udev/udev-builtin-hwdb.c +index f4a065a97d..acd1d1a6de 100644 +--- a/src/udev/udev-builtin-hwdb.c ++++ b/src/udev/udev-builtin-hwdb.c +@@ -43,7 +43,7 @@ int udev_builtin_hwdb_lookup(struct udev_device *dev, + return -ENOENT; + + if (prefix) { +- lookup = strjoin(prefix, modalias, NULL); ++ lookup = strjoin(prefix, modalias); + if (!lookup) + return -ENOMEM; + modalias = lookup; +diff --git a/src/udev/udevadm-hwdb.c b/src/udev/udevadm-hwdb.c +index 1bffe8e8ab..70a5fa4d7a 100644 +--- a/src/udev/udevadm-hwdb.c ++++ b/src/udev/udevadm-hwdb.c +@@ -653,7 +653,7 @@ static int adm_hwdb(struct udev *udev, int argc, char *argv[]) { + log_debug("strings dedup'ed: %8zu bytes (%8zu)", + trie->strings->dedup_len, trie->strings->dedup_count); + +- hwdb_bin = strjoin(root, "/", hwdb_bin_dir, "/hwdb.bin", NULL); ++ hwdb_bin = strjoin(root, "/", hwdb_bin_dir, "/hwdb.bin"); + if (!hwdb_bin) { + rc = EXIT_FAILURE; + goto out; +-- +2.9.3 + diff --git a/0052-core-add-new-RestrictNamespaces-unit-file-setting.patch b/0052-core-add-new-RestrictNamespaces-unit-file-setting.patch new file mode 100644 index 0000000..718b1ed --- /dev/null +++ b/0052-core-add-new-RestrictNamespaces-unit-file-setting.patch @@ -0,0 +1,872 @@ +From 9b51665a3d2cd2678c78e2dc42ec6b40e83232f7 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 1 Nov 2016 20:25:19 -0600 +Subject: [PATCH] core: add new RestrictNamespaces= unit file setting + +This new setting permits restricting whether namespaces may be created and +managed by processes started by a unit. It installs a seccomp filter blocking +certain invocations of unshare(), clone() and setns(). + +RestrictNamespaces=no is the default, and does not restrict namespaces in any +way. RestrictNamespaces=yes takes away the ability to create or manage any kind +of namspace. "RestrictNamespaces=mnt ipc" restricts the creation of namespaces +so that only mount and IPC namespaces may be created/managed, but no other +kind of namespaces. + +This setting should be improve security quite a bit as in particular user +namespacing was a major source of CVEs in the kernel in the past, and is +accessible to unprivileged processes. With this setting the entire attack +surface may be removed for system services that do not make use of namespaces. + +(cherry picked from commit add005357d535681c7075ced8eec2b6e61b43728) +--- + Makefile.am | 4 +- + TODO | 6 -- + man/systemd.exec.xml | 50 +++++++++----- + src/core/dbus-execute.c | 21 ++++++ + src/core/execute.c | 30 ++++++++ + src/core/execute.h | 9 +++ + src/core/load-fragment-gperf.gperf.m4 | 2 + + src/core/load-fragment.c | 49 +++++++++++++ + src/core/load-fragment.h | 1 + + src/shared/bus-unit-util.c | 25 +++++++ + src/shared/nsflags.c | 126 ++++++++++++++++++++++++++++++++++ + src/shared/nsflags.h | 49 +++++++++++++ + src/shared/seccomp-util.c | 89 ++++++++++++++++++++++++ + src/shared/seccomp-util.h | 2 + + src/test/test-seccomp.c | 94 +++++++++++++++++++++++++ + 15 files changed, 534 insertions(+), 23 deletions(-) + create mode 100644 src/shared/nsflags.c + create mode 100644 src/shared/nsflags.h + +diff --git a/Makefile.am b/Makefile.am +index f2d8bf57f7..1031e797b3 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -1046,7 +1046,9 @@ libshared_la_SOURCES = \ + src/shared/tests.h \ + src/shared/tests.c \ + src/shared/fdset.c \ +- src/shared/fdset.h ++ src/shared/fdset.h \ ++ src/shared/nsflags.h \ ++ src/shared/nsflags.c + + if HAVE_UTMP + libshared_la_SOURCES += \ +diff --git a/TODO b/TODO +index c8266a549d..164e33708e 100644 +--- a/TODO ++++ b/TODO +@@ -59,14 +59,10 @@ Features: + + * define gpt header bits to select volatility mode + +-* nspawn: mount loopback filesystems with "discard" +- + * ProtectKernelLogs= (drops CAP_SYSLOG, add seccomp for syslog() syscall, and DeviceAllow to /dev/kmsg) in service files + + * ProtectClock= (drops CAP_SYS_TIMES, adds seecomp filters for settimeofday, adjtimex), sets DeviceAllow o /dev/rtc + +-* ProtectKernelModules= (drops CAP_SYS_MODULE and filters the kmod syscalls) +- + * ProtectTracing= (drops CAP_SYS_PTRACE, blocks ptrace syscall, makes /sys/kernel/tracing go away) + + * ProtectMount= (drop mount/umount/pivot_root from seccomp, disallow fuse via DeviceAllow, imply Mountflags=slave) +@@ -88,8 +84,6 @@ Features: + + * Add RootImage= for mounting a disk image or file as root directory + +-* RestrictNamespaces= or so in services (taking away the ability to create namespaces, with setns, unshare, clone) +- + * make sure the ratelimit object can deal with USEC_INFINITY as way to turn off things + + * journalctl: make sure -f ends when the container indicated by -M terminates +diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml +index 3c350df11f..33bca1bfb0 100644 +--- a/man/systemd.exec.xml ++++ b/man/systemd.exec.xml +@@ -1234,22 +1234,16 @@ + + NoNewPrivileges= + +- Takes a boolean argument. If true, ensures that the service +- process and all its children can never gain new privileges. This option is more +- powerful than the respective secure bits flags (see above), as it also prohibits +- UID changes of any kind. This is the simplest and most effective way to ensure that +- a process and its children can never elevate privileges again. Defaults to false, +- but in the user manager instance certain settings force +- NoNewPrivileges=yes, ignoring the value of this setting. +- Those is the case when SystemCallFilter=, +- SystemCallArchitectures=, +- RestrictAddressFamilies=, +- PrivateDevices=, +- ProtectKernelTunables=, +- ProtectKernelModules=, +- MemoryDenyWriteExecute=, or +- RestrictRealtime= are specified. +- ++ Takes a boolean argument. If true, ensures that the service process and all its children can ++ never gain new privileges through execve() (e.g. via setuid or setgid bits, or filesystem ++ capabilities). This is the simplest and most effective way to ensure that a process and its children can never ++ elevate privileges again. Defaults to false, but in the user manager instance certain settings force ++ NoNewPrivileges=yes, ignoring the value of this setting. This is the case when ++ SystemCallFilter=, SystemCallArchitectures=, ++ RestrictAddressFamilies=, RestrictNamespaces=, ++ PrivateDevices=, ProtectKernelTunables=, ++ ProtectKernelModules=, MemoryDenyWriteExecute=, or ++ RestrictRealtime= are specified. + + + +@@ -1462,6 +1456,30 @@ + logging. This does not affect commands prefixed with +. + + ++ ++ RestrictNamespaces= ++ ++ Restricts access to Linux namespace functionality for the processes of this unit. For details ++ about Linux namespaces, see ++ namespaces7. Either takes a ++ boolean argument, or a space-separated list of namespace type identifiers. If false (the default), no ++ restrictions on namespace creation and switching are made. If true, access to any kind of namespacing is ++ prohibited. Otherwise, a space-separated list of namespace type identifiers must be specified, consisting of ++ any combination of: cgroup, ipc, net, ++ mnt, pid, user and uts. Any ++ namespace type listed is made accessible to the unit's processes, access to namespace types not listed is ++ prohibited (whitelisting). By prepending the list with a single tilda character (~) the ++ effect may be inverted: only the listed namespace types will be made inaccessible, all unlisted ones are ++ permitted (blacklisting). If the empty string is assigned, the default namespace restrictions are applied, ++ which is equivalent to false. Internally, this setting limits access to the ++ unshare2, ++ clone2 and ++ setns2 system calls, taking ++ the specified flags parameters into account. Note that — if this option is used — in addition to restricting ++ creation and switching of the specified types of namespaces (or all of them, if true) access to the ++ setns() system call with a zero flags parameter is prohibited. ++ ++ + + ProtectKernelModules= + +diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c +index 03f23780c1..d7bb0496a0 100644 +--- a/src/core/dbus-execute.c ++++ b/src/core/dbus-execute.c +@@ -781,6 +781,7 @@ const sd_bus_vtable bus_exec_vtable[] = { + SD_BUS_PROPERTY("RuntimeDirectory", "as", NULL, offsetof(ExecContext, runtime_directory), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("MemoryDenyWriteExecute", "b", bus_property_get_bool, offsetof(ExecContext, memory_deny_write_execute), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RestrictRealtime", "b", bus_property_get_bool, offsetof(ExecContext, restrict_realtime), SD_BUS_VTABLE_PROPERTY_CONST), ++ SD_BUS_PROPERTY("RestrictNamespace", "t", bus_property_get_ulong, offsetof(ExecContext, restrict_namespaces), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_VTABLE_END + }; + +@@ -1591,7 +1592,27 @@ int bus_exec_context_set_transient_property( + } + + return 1; ++ } else if (streq(name, "RestrictNamespaces")) { ++ uint64_t flags; + ++ r = sd_bus_message_read(message, "t", &flags); ++ if (r < 0) ++ return r; ++ if ((flags & NAMESPACE_FLAGS_ALL) != flags) ++ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unknown namespace types"); ++ ++ if (mode != UNIT_CHECK) { ++ _cleanup_free_ char *s = NULL; ++ ++ r = namespace_flag_to_string_many(flags, &s); ++ if (r < 0) ++ return r; ++ ++ c->restrict_namespaces = flags; ++ unit_write_drop_in_private_format(u, mode, name, "%s=%s", name, s); ++ } ++ ++ return 1; + } + + ri = rlimit_from_string(name); +diff --git a/src/core/execute.c b/src/core/execute.c +index 224382b581..59ce0774c4 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -1534,6 +1534,18 @@ static int apply_private_devices(const Unit *u, const ExecContext *c) { + return seccomp_load_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_RAW_IO, SCMP_ACT_ERRNO(EPERM)); + } + ++static int apply_restrict_namespaces(Unit *u, const ExecContext *c) { ++ assert(c); ++ ++ if (!exec_context_restrict_namespaces_set(c)) ++ return 0; ++ ++ if (skip_seccomp_unavailable(u, "RestrictNamespaces=")) ++ return 0; ++ ++ return seccomp_restrict_namespaces(c->restrict_namespaces); ++} ++ + #endif + + static void do_idle_pipe_dance(int idle_pipe[4]) { +@@ -2183,6 +2195,7 @@ static bool context_has_no_new_privileges(const ExecContext *c) { + return context_has_address_families(c) || /* we need NNP if we have any form of seccomp and are unprivileged */ + c->memory_deny_write_execute || + c->restrict_realtime || ++ exec_context_restrict_namespaces_set(c) || + c->protect_kernel_tunables || + c->protect_kernel_modules || + c->private_devices || +@@ -2764,6 +2777,12 @@ static int exec_child( + } + } + ++ r = apply_restrict_namespaces(unit, context); ++ if (r < 0) { ++ *exit_status = EXIT_SECCOMP; ++ return r; ++ } ++ + if (context->protect_kernel_tunables) { + r = apply_protect_sysctl(unit, context); + if (r < 0) { +@@ -2947,6 +2966,7 @@ void exec_context_init(ExecContext *c) { + c->personality = PERSONALITY_INVALID; + c->runtime_directory_mode = 0755; + c->capability_bounding_set = CAP_ALL; ++ c->restrict_namespaces = NAMESPACE_FLAGS_ALL; + } + + void exec_context_done(ExecContext *c) { +@@ -3244,6 +3264,7 @@ static void strv_fprintf(FILE *f, char **l) { + void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { + char **e, **d; + unsigned i; ++ int r; + + assert(c); + assert(f); +@@ -3524,6 +3545,15 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { + fputc('\n', f); + } + ++ if (exec_context_restrict_namespaces_set(c)) { ++ _cleanup_free_ char *s = NULL; ++ ++ r = namespace_flag_to_string_many(c->restrict_namespaces, &s); ++ if (r >= 0) ++ fprintf(f, "%sRestrictNamespaces: %s\n", ++ prefix, s); ++ } ++ + if (c->syscall_errno > 0) + fprintf(f, + "%sSystemCallErrorNumber: %s\n", +diff --git a/src/core/execute.h b/src/core/execute.h +index c7d0f7761e..56f880cffe 100644 +--- a/src/core/execute.h ++++ b/src/core/execute.h +@@ -35,6 +35,7 @@ typedef struct ExecParameters ExecParameters; + #include "list.h" + #include "missing.h" + #include "namespace.h" ++#include "nsflags.h" + + typedef enum ExecUtmpMode { + EXEC_UTMP_INIT, +@@ -195,6 +196,8 @@ struct ExecContext { + + unsigned long personality; + ++ unsigned long restrict_namespaces; /* The CLONE_NEWxyz flags permitted to the unit's processes */ ++ + Set *syscall_filter; + Set *syscall_archs; + int syscall_errno; +@@ -216,6 +219,12 @@ struct ExecContext { + bool no_new_privileges_set:1; + }; + ++static inline bool exec_context_restrict_namespaces_set(const ExecContext *c) { ++ assert(c); ++ ++ return (c->restrict_namespaces & NAMESPACE_FLAGS_ALL) != NAMESPACE_FLAGS_ALL; ++} ++ + typedef enum ExecFlags { + EXEC_CONFIRM_SPAWN = 1U << 0, + EXEC_APPLY_PERMISSIONS = 1U << 1, +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index af2f9d960b..cb2f384f47 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -57,12 +57,14 @@ m4_ifdef(`HAVE_SECCOMP', + $1.SystemCallArchitectures, config_parse_syscall_archs, 0, offsetof($1, exec_context.syscall_archs) + $1.SystemCallErrorNumber, config_parse_syscall_errno, 0, offsetof($1, exec_context) + $1.MemoryDenyWriteExecute, config_parse_bool, 0, offsetof($1, exec_context.memory_deny_write_execute) ++$1.RestrictNamespaces, config_parse_restrict_namespaces, 0, offsetof($1, exec_context.restrict_namespaces) + $1.RestrictRealtime, config_parse_bool, 0, offsetof($1, exec_context.restrict_realtime) + $1.RestrictAddressFamilies, config_parse_address_families, 0, offsetof($1, exec_context)', + `$1.SystemCallFilter, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 + $1.SystemCallArchitectures, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 + $1.SystemCallErrorNumber, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 + $1.MemoryDenyWriteExecute, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 ++$1.RestrictNamespaces, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 + $1.RestrictRealtime, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 + $1.RestrictAddressFamilies, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') + $1.LimitCPU, config_parse_limit, RLIMIT_CPU, offsetof($1, exec_context.rlimit) +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index cbc826809e..e0fa484c1e 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -2905,6 +2905,54 @@ int config_parse_address_families( + if (!isempty(state)) + log_syntax(unit, LOG_ERR, filename, line, 0, "Trailing garbage, ignoring."); + ++ return 0; ++} ++ ++int config_parse_restrict_namespaces( ++ const char *unit, ++ const char *filename, ++ unsigned line, ++ const char *section, ++ unsigned section_line, ++ const char *lvalue, ++ int ltype, ++ const char *rvalue, ++ void *data, ++ void *userdata) { ++ ++ ExecContext *c = data; ++ bool invert = false; ++ int r; ++ ++ if (isempty(rvalue)) { ++ /* Reset to the default. */ ++ c->restrict_namespaces = NAMESPACE_FLAGS_ALL; ++ return 0; ++ } ++ ++ if (rvalue[0] == '~') { ++ invert = true; ++ rvalue++; ++ } ++ ++ r = parse_boolean(rvalue); ++ if (r > 0) ++ c->restrict_namespaces = 0; ++ else if (r == 0) ++ c->restrict_namespaces = NAMESPACE_FLAGS_ALL; ++ else { ++ /* Not a boolean argument, in this case it's a list of namespace types. */ ++ ++ r = namespace_flag_from_string_many(rvalue, &c->restrict_namespaces); ++ if (r < 0) { ++ log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse namespace type string, ignoring: %s", rvalue); ++ return 0; ++ } ++ } ++ ++ if (invert) ++ c->restrict_namespaces = (~c->restrict_namespaces) & NAMESPACE_FLAGS_ALL; ++ + return 0; + } + #endif +@@ -4327,6 +4375,7 @@ void unit_dump_config_items(FILE *f) { + { config_parse_syscall_archs, "ARCHS" }, + { config_parse_syscall_errno, "ERRNO" }, + { config_parse_address_families, "FAMILIES" }, ++ { config_parse_restrict_namespaces, "NAMESPACES" }, + #endif + { config_parse_cpu_shares, "SHARES" }, + { config_parse_cpu_weight, "WEIGHT" }, +diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h +index c05f205c37..1cff815a50 100644 +--- a/src/core/load-fragment.h ++++ b/src/core/load-fragment.h +@@ -116,6 +116,7 @@ int config_parse_fdname(const char *unit, const char *filename, unsigned line, c + int config_parse_sec_fix_0(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); + int config_parse_user_group(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); + int config_parse_user_group_strv(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); ++int config_parse_restrict_namespaces(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); + + /* gperf prototypes */ + const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length); +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index f639e0e832..35e2c8f18e 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -27,6 +27,7 @@ + #include "hashmap.h" + #include "list.h" + #include "locale-util.h" ++#include "nsflags.h" + #include "parse-util.h" + #include "path-util.h" + #include "process-util.h" +@@ -553,6 +554,30 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen + + r = sd_bus_message_close_container(m); + ++ } else if (streq(field, "RestrictNamespaces")) { ++ bool invert = false; ++ uint64_t flags = 0; ++ ++ if (eq[0] == '~') { ++ invert = true; ++ eq++; ++ } ++ ++ r = parse_boolean(eq); ++ if (r > 0) ++ flags = 0; ++ else if (r == 0) ++ flags = NAMESPACE_FLAGS_ALL; ++ else { ++ r = namespace_flag_from_string_many(eq, &flags); ++ if (r < 0) ++ return log_error_errno(r, "Failed to parse %s value %s.", field, eq); ++ } ++ ++ if (invert) ++ flags = (~flags) & NAMESPACE_FLAGS_ALL; ++ ++ r = sd_bus_message_append(m, "v", "t", flags); + } else { + log_error("Unknown assignment %s.", assignment); + return -EINVAL; +diff --git a/src/shared/nsflags.c b/src/shared/nsflags.c +new file mode 100644 +index 0000000000..8fcbe97ba7 +--- /dev/null ++++ b/src/shared/nsflags.c +@@ -0,0 +1,126 @@ ++/*** ++ This file is part of systemd. ++ ++ Copyright 2016 Lennart Poettering ++ ++ systemd is free software; you can redistribute it and/or modify it ++ under the terms of the GNU Lesser General Public License as published by ++ the Free Software Foundation; either version 2.1 of the License, or ++ (at your option) any later version. ++ ++ systemd is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with systemd; If not, see . ++***/ ++ ++#include ++ ++#include "alloc-util.h" ++#include "extract-word.h" ++#include "nsflags.h" ++#include "seccomp-util.h" ++#include "string-util.h" ++ ++const struct namespace_flag_map namespace_flag_map[] = { ++ { CLONE_NEWCGROUP, "cgroup" }, ++ { CLONE_NEWIPC, "ipc" }, ++ { CLONE_NEWNET, "net" }, ++ /* So, the mount namespace flag is called CLONE_NEWNS for historical reasons. Let's expose it here under a more ++ * explanatory name: "mnt". This is in-line with how the kernel exposes namespaces in /proc/$PID/ns. */ ++ { CLONE_NEWNS, "mnt" }, ++ { CLONE_NEWPID, "pid" }, ++ { CLONE_NEWUSER, "user" }, ++ { CLONE_NEWUTS, "uts" }, ++ {} ++}; ++ ++const char* namespace_flag_to_string(unsigned long flag) { ++ unsigned i; ++ ++ flag &= NAMESPACE_FLAGS_ALL; ++ ++ for (i = 0; namespace_flag_map[i].name; i++) ++ if (flag == namespace_flag_map[i].flag) ++ return namespace_flag_map[i].name; ++ ++ return NULL; /* either unknown namespace flag, or a combination of many. This call supports neither. */ ++} ++ ++unsigned long namespace_flag_from_string(const char *name) { ++ unsigned i; ++ ++ if (isempty(name)) ++ return 0; ++ ++ for (i = 0; namespace_flag_map[i].name; i++) ++ if (streq(name, namespace_flag_map[i].name)) ++ return namespace_flag_map[i].flag; ++ ++ return 0; ++} ++ ++int namespace_flag_from_string_many(const char *name, unsigned long *ret) { ++ unsigned long flags = 0; ++ int r; ++ ++ assert_se(ret); ++ ++ if (!name) { ++ *ret = 0; ++ return 0; ++ } ++ ++ for (;;) { ++ _cleanup_free_ char *word = NULL; ++ unsigned long f; ++ ++ r = extract_first_word(&name, &word, NULL, 0); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ break; ++ ++ f = namespace_flag_from_string(word); ++ if (f == 0) ++ return -EINVAL; ++ ++ flags |= f; ++ } ++ ++ *ret = flags; ++ return 0; ++} ++ ++int namespace_flag_to_string_many(unsigned long flags, char **ret) { ++ _cleanup_free_ char *s = NULL; ++ unsigned i; ++ ++ for (i = 0; namespace_flag_map[i].name; i++) { ++ if ((flags & namespace_flag_map[i].flag) != namespace_flag_map[i].flag) ++ continue; ++ ++ if (!s) { ++ s = strdup(namespace_flag_map[i].name); ++ if (!s) ++ return -ENOMEM; ++ } else { ++ if (!strextend(&s, " ", namespace_flag_map[i].name, NULL)) ++ return -ENOMEM; ++ } ++ } ++ ++ if (!s) { ++ s = strdup(""); ++ if (!s) ++ return -ENOMEM; ++ } ++ ++ *ret = s; ++ s = NULL; ++ ++ return 0; ++} +diff --git a/src/shared/nsflags.h b/src/shared/nsflags.h +new file mode 100644 +index 0000000000..152ab8b936 +--- /dev/null ++++ b/src/shared/nsflags.h +@@ -0,0 +1,49 @@ ++#pragma once ++ ++/*** ++ This file is part of systemd. ++ ++ Copyright 2016 Lennart Poettering ++ ++ systemd is free software; you can redistribute it and/or modify it ++ under the terms of the GNU Lesser General Public License as published by ++ the Free Software Foundation; either version 2.1 of the License, or ++ (at your option) any later version. ++ ++ systemd is distributed in the hope that it will be useful, but ++ WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public License ++ along with systemd; If not, see . ++***/ ++ ++#include ++ ++#include "missing.h" ++ ++/* The combination of all namespace flags defined by the kernel. The right type for this isn't clear. setns() and ++ * unshare() expect these flags to be passed as (signed) "int", while clone() wants them as "unsigned long". The latter ++ * is definitely more appropriate for a flags parameter, and also the larger type of the two, hence let's stick to that ++ * here. */ ++#define NAMESPACE_FLAGS_ALL \ ++ ((unsigned long) (CLONE_NEWCGROUP| \ ++ CLONE_NEWIPC| \ ++ CLONE_NEWNET| \ ++ CLONE_NEWNS| \ ++ CLONE_NEWPID| \ ++ CLONE_NEWUSER| \ ++ CLONE_NEWUTS)) ++ ++const char* namespace_flag_to_string(unsigned long flag); ++unsigned long namespace_flag_from_string(const char *name); ++int namespace_flag_from_string_many(const char *name, unsigned long *ret); ++int namespace_flag_to_string_many(unsigned long flags, char **ret); ++ ++struct namespace_flag_map { ++ unsigned long flag; ++ const char *name; ++}; ++ ++extern const struct namespace_flag_map namespace_flag_map[]; +diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c +index c9b24f1065..55b97e1efb 100644 +--- a/src/shared/seccomp-util.c ++++ b/src/shared/seccomp-util.c +@@ -23,7 +23,9 @@ + #include + #include + ++#include "alloc-util.h" + #include "macro.h" ++#include "nsflags.h" + #include "seccomp-util.h" + #include "string-util.h" + #include "util.h" +@@ -574,5 +576,92 @@ int seccomp_load_filter_set(uint32_t default_action, const SyscallFilterSet *set + finish: + seccomp_release(seccomp); + return r; ++} ++ ++int seccomp_restrict_namespaces(unsigned long retain) { ++ scmp_filter_ctx seccomp; ++ unsigned i; ++ int r; ++ ++ if (log_get_max_level() >= LOG_DEBUG) { ++ _cleanup_free_ char *s = NULL; ++ ++ (void) namespace_flag_to_string_many(retain, &s); ++ log_debug("Restricting namespace to: %s.", strna(s)); ++ } ++ ++ /* NOOP? */ ++ if ((retain & NAMESPACE_FLAGS_ALL) == NAMESPACE_FLAGS_ALL) ++ return 0; ++ ++ r = seccomp_init_conservative(&seccomp, SCMP_ACT_ALLOW); ++ if (r < 0) ++ return r; ++ ++ if ((retain & NAMESPACE_FLAGS_ALL) == 0) ++ /* If every single kind of namespace shall be prohibited, then let's block the whole setns() syscall ++ * altogether. */ ++ r = seccomp_rule_add( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(setns), ++ 0); ++ else ++ /* Otherwise, block only the invocations with the appropriate flags in the loop below, but also the ++ * special invocation with a zero flags argument, right here. */ ++ r = seccomp_rule_add( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(setns), ++ 1, ++ SCMP_A1(SCMP_CMP_EQ, 0)); ++ if (r < 0) ++ goto finish; ++ ++ for (i = 0; namespace_flag_map[i].name; i++) { ++ unsigned long f; ++ ++ f = namespace_flag_map[i].flag; ++ if ((retain & f) == f) { ++ log_debug("Permitting %s.", namespace_flag_map[i].name); ++ continue; ++ } ++ ++ log_debug("Blocking %s.", namespace_flag_map[i].name); ++ ++ r = seccomp_rule_add( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(unshare), ++ 1, ++ SCMP_A0(SCMP_CMP_MASKED_EQ, f, f)); ++ if (r < 0) ++ goto finish; ++ ++ r = seccomp_rule_add( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(clone), ++ 1, ++ SCMP_A0(SCMP_CMP_MASKED_EQ, f, f)); ++ if (r < 0) ++ goto finish; ++ ++ if ((retain & NAMESPACE_FLAGS_ALL) != 0) { ++ r = seccomp_rule_add( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(setns), ++ 1, ++ SCMP_A1(SCMP_CMP_MASKED_EQ, f, f)); ++ if (r < 0) ++ goto finish; ++ } ++ } ++ ++ r = seccomp_load(seccomp); + ++finish: ++ seccomp_release(seccomp); ++ return r; + } +diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h +index 8e209efef2..e325dab628 100644 +--- a/src/shared/seccomp-util.h ++++ b/src/shared/seccomp-util.h +@@ -64,3 +64,5 @@ const SyscallFilterSet *syscall_filter_set_find(const char *name); + int seccomp_add_syscall_filter_set(scmp_filter_ctx seccomp, const SyscallFilterSet *set, uint32_t action); + + int seccomp_load_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action); ++ ++int seccomp_restrict_namespaces(unsigned long retain); +diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c +index 43d1567288..beb6a7f422 100644 +--- a/src/test/test-seccomp.c ++++ b/src/test/test-seccomp.c +@@ -20,10 +20,15 @@ + #include + #include + #include ++#include + ++#include "alloc-util.h" + #include "fd-util.h" + #include "macro.h" ++#include "missing.h" ++#include "nsflags.h" + #include "process-util.h" ++#include "raw-clone.h" + #include "seccomp-util.h" + #include "string-util.h" + #include "util.h" +@@ -125,12 +130,101 @@ static void test_filter_sets(void) { + } + } + ++static void test_restrict_namespace(void) { ++ _cleanup_free_ char *s = NULL; ++ pid_t pid; ++ unsigned long ul; ++ ++ assert_se(namespace_flag_to_string(0) == NULL); ++ assert_se(streq(namespace_flag_to_string(CLONE_NEWNS), "mnt")); ++ assert_se(namespace_flag_to_string(CLONE_NEWNS|CLONE_NEWIPC) == NULL); ++ assert_se(streq(namespace_flag_to_string(CLONE_NEWCGROUP), "cgroup")); ++ ++ assert_se(namespace_flag_from_string("mnt") == CLONE_NEWNS); ++ assert_se(namespace_flag_from_string(NULL) == 0); ++ assert_se(namespace_flag_from_string("") == 0); ++ assert_se(namespace_flag_from_string("uts") == CLONE_NEWUTS); ++ assert_se(namespace_flag_from_string(namespace_flag_to_string(CLONE_NEWUTS)) == CLONE_NEWUTS); ++ assert_se(streq(namespace_flag_to_string(namespace_flag_from_string("ipc")), "ipc")); ++ ++ assert_se(namespace_flag_from_string_many(NULL, &ul) == 0 && ul == 0); ++ assert_se(namespace_flag_from_string_many("", &ul) == 0 && ul == 0); ++ assert_se(namespace_flag_from_string_many("mnt uts ipc", &ul) == 0 && ul == (CLONE_NEWNS|CLONE_NEWUTS|CLONE_NEWIPC)); ++ ++ assert_se(namespace_flag_to_string_many(NAMESPACE_FLAGS_ALL, &s) == 0); ++ assert_se(streq(s, "cgroup ipc net mnt pid user uts")); ++ assert_se(namespace_flag_from_string_many(s, &ul) == 0 && ul == NAMESPACE_FLAGS_ALL); ++ ++ if (!is_seccomp_available()) ++ return; ++ ++ if (geteuid() != 0) ++ return; ++ ++ pid = fork(); ++ assert_se(pid >= 0); ++ ++ if (pid == 0) { ++ ++ assert_se(seccomp_restrict_namespaces(CLONE_NEWNS|CLONE_NEWNET) >= 0); ++ ++ assert_se(unshare(CLONE_NEWNS) == 0); ++ assert_se(unshare(CLONE_NEWNET) == 0); ++ assert_se(unshare(CLONE_NEWUTS) == -1); ++ assert_se(errno == EPERM); ++ assert_se(unshare(CLONE_NEWIPC) == -1); ++ assert_se(errno == EPERM); ++ assert_se(unshare(CLONE_NEWNET|CLONE_NEWUTS) == -1); ++ assert_se(errno == EPERM); ++ ++ /* We use fd 0 (stdin) here, which of course will fail with EINVAL on setns(). Except of course our ++ * seccomp filter worked, and hits first and makes it return EPERM */ ++ assert_se(setns(0, CLONE_NEWNS) == -1); ++ assert_se(errno == EINVAL); ++ assert_se(setns(0, CLONE_NEWNET) == -1); ++ assert_se(errno == EINVAL); ++ assert_se(setns(0, CLONE_NEWUTS) == -1); ++ assert_se(errno == EPERM); ++ assert_se(setns(0, CLONE_NEWIPC) == -1); ++ assert_se(errno == EPERM); ++ assert_se(setns(0, CLONE_NEWNET|CLONE_NEWUTS) == -1); ++ assert_se(errno == EPERM); ++ assert_se(setns(0, 0) == -1); ++ assert_se(errno == EPERM); ++ ++ pid = raw_clone(CLONE_NEWNS); ++ assert_se(pid >= 0); ++ if (pid == 0) ++ _exit(EXIT_SUCCESS); ++ pid = raw_clone(CLONE_NEWNET); ++ assert_se(pid >= 0); ++ if (pid == 0) ++ _exit(EXIT_SUCCESS); ++ pid = raw_clone(CLONE_NEWUTS); ++ assert_se(pid < 0); ++ assert_se(errno == EPERM); ++ pid = raw_clone(CLONE_NEWIPC); ++ assert_se(pid < 0); ++ assert_se(errno == EPERM); ++ pid = raw_clone(CLONE_NEWNET|CLONE_NEWUTS); ++ assert_se(pid < 0); ++ assert_se(errno == EPERM); ++ ++ _exit(EXIT_SUCCESS); ++ } ++ ++ assert_se(wait_for_terminate_and_warn("nsseccomp", pid, true) == EXIT_SUCCESS); ++} ++ + int main(int argc, char *argv[]) { + ++ log_set_max_level(LOG_DEBUG); ++ + test_seccomp_arch_to_string(); + test_architecture_table(); + test_syscall_filter_set_find(); + test_filter_sets(); ++ test_restrict_namespace(); + + return 0; + } +-- +2.9.3 + diff --git a/0053-seccomp-rework-seccomp-code-to-improve-compat-with-s.patch b/0053-seccomp-rework-seccomp-code-to-improve-compat-with-s.patch new file mode 100644 index 0000000..e49617d --- /dev/null +++ b/0053-seccomp-rework-seccomp-code-to-improve-compat-with-s.patch @@ -0,0 +1,2021 @@ +From 8296437564980199342f4fd9f2c85312570ba060 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 27 Dec 2016 15:28:25 +0100 +Subject: [PATCH] seccomp: rework seccomp code, to improve compat with some + archs + +This substantially reworks the seccomp code, to ensure better +compatibility with some architectures, including i386. + +So far we relied on libseccomp's internal handling of the multiple +syscall ABIs supported on Linux. This is problematic however, as it does +not define clear semantics if an ABI is not able to support specific +seccomp rules we install. + +This rework hence changes a couple of things: + +- We no longer use seccomp_rule_add(), but only + seccomp_rule_add_exact(), and fail the installation of a filter if the + architecture doesn't support it. + +- We no longer rely on adding multiple syscall architectures to a single filter, + but instead install a separate filter for each syscall architecture + supported. This way, we can install a strict filter for x86-64, while + permitting a less strict filter for i386. + +- All high-level filter additions are now moved from execute.c to + seccomp-util.c, so that we can test them independently of the service + execution logic. + +- Tests have been added for all types of our seccomp filters. + +- SystemCallFilters= and SystemCallArchitectures= are now implemented in + independent filters and installation logic, as they semantically are + very much independent of each other. + +Fixes: #4575 +(cherry picked from commit 469830d1426a91e0897c321fdc8ee428f0a750c1) +--- + src/core/execute.c | 466 ++++++++---------------------- + src/core/main.c | 34 +-- + src/nspawn/nspawn-seccomp.c | 117 ++++---- + src/shared/seccomp-util.c | 670 +++++++++++++++++++++++++++++++++++--------- + src/shared/seccomp-util.h | 25 +- + src/test/test-execute.c | 1 + + src/test/test-seccomp.c | 272 +++++++++++++++++- + 7 files changed, 1016 insertions(+), 569 deletions(-) + +diff --git a/src/core/execute.c b/src/core/execute.c +index 59ce0774c4..2dfd43a8f2 100644 +--- a/src/core/execute.c ++++ b/src/core/execute.c +@@ -1184,6 +1184,41 @@ static void rename_process_from_path(const char *path) { + rename_process(process_name); + } + ++static bool context_has_address_families(const ExecContext *c) { ++ assert(c); ++ ++ return c->address_families_whitelist || ++ !set_isempty(c->address_families); ++} ++ ++static bool context_has_syscall_filters(const ExecContext *c) { ++ assert(c); ++ ++ return c->syscall_whitelist || ++ !set_isempty(c->syscall_filter); ++} ++ ++static bool context_has_no_new_privileges(const ExecContext *c) { ++ assert(c); ++ ++ if (c->no_new_privileges) ++ return true; ++ ++ if (have_effective_cap(CAP_SYS_ADMIN)) /* if we are privileged, we don't need NNP */ ++ return false; ++ ++ /* We need NNP if we have any form of seccomp and are unprivileged */ ++ return context_has_address_families(c) || ++ c->memory_deny_write_execute || ++ c->restrict_realtime || ++ exec_context_restrict_namespaces_set(c) || ++ c->protect_kernel_tunables || ++ c->protect_kernel_modules || ++ c->private_devices || ++ context_has_syscall_filters(c) || ++ !set_isempty(c->syscall_archs); ++} ++ + #ifdef HAVE_SECCOMP + + static bool skip_seccomp_unavailable(const Unit* u, const char* msg) { +@@ -1197,344 +1232,131 @@ static bool skip_seccomp_unavailable(const Unit* u, const char* msg) { + return true; + } + +-static int apply_seccomp(const Unit* u, const ExecContext *c) { +- uint32_t negative_action, action; +- scmp_filter_ctx seccomp; +- Iterator i; +- void *id; +- int r; ++static int apply_syscall_filter(const Unit* u, const ExecContext *c) { ++ uint32_t negative_action, default_action, action; + ++ assert(u); + assert(c); + +- if (skip_seccomp_unavailable(u, "syscall filtering")) ++ if (!context_has_syscall_filters(c)) ++ return 0; ++ ++ if (skip_seccomp_unavailable(u, "SystemCallFilter=")) + return 0; + + negative_action = c->syscall_errno == 0 ? SCMP_ACT_KILL : SCMP_ACT_ERRNO(c->syscall_errno); + +- seccomp = seccomp_init(c->syscall_whitelist ? negative_action : SCMP_ACT_ALLOW); +- if (!seccomp) +- return -ENOMEM; +- +- if (c->syscall_archs) { +- +- SET_FOREACH(id, c->syscall_archs, i) { +- r = seccomp_arch_add(seccomp, PTR_TO_UINT32(id) - 1); +- if (r == -EEXIST) +- continue; +- if (r < 0) +- goto finish; +- } +- ++ if (c->syscall_whitelist) { ++ default_action = negative_action; ++ action = SCMP_ACT_ALLOW; + } else { +- r = seccomp_add_secondary_archs(seccomp); +- if (r < 0) +- goto finish; ++ default_action = SCMP_ACT_ALLOW; ++ action = negative_action; + } + +- action = c->syscall_whitelist ? SCMP_ACT_ALLOW : negative_action; +- SET_FOREACH(id, c->syscall_filter, i) { +- r = seccomp_rule_add(seccomp, action, PTR_TO_INT(id) - 1, 0); +- if (r < 0) +- goto finish; +- } ++ return seccomp_load_syscall_filter_set_raw(default_action, c->syscall_filter, action); ++} ++ ++static int apply_syscall_archs(const Unit *u, const ExecContext *c) { ++ assert(u); ++ assert(c); + +- r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); +- if (r < 0) +- goto finish; ++ if (set_isempty(c->syscall_archs)) ++ return 0; + +- r = seccomp_load(seccomp); ++ if (skip_seccomp_unavailable(u, "SystemCallArchitectures=")) ++ return 0; + +-finish: +- seccomp_release(seccomp); +- return r; ++ return seccomp_restrict_archs(c->syscall_archs); + } + + static int apply_address_families(const Unit* u, const ExecContext *c) { +- scmp_filter_ctx seccomp; +- Iterator i; +- int r; +- ++ assert(u); + assert(c); + ++ if (!context_has_address_families(c)) ++ return 0; ++ + if (skip_seccomp_unavailable(u, "RestrictAddressFamilies=")) + return 0; + +- r = seccomp_init_conservative(&seccomp, SCMP_ACT_ALLOW); +- if (r < 0) +- return r; +- +- if (c->address_families_whitelist) { +- int af, first = 0, last = 0; +- void *afp; +- +- /* If this is a whitelist, we first block the address +- * families that are out of range and then everything +- * that is not in the set. First, we find the lowest +- * and highest address family in the set. */ +- +- SET_FOREACH(afp, c->address_families, i) { +- af = PTR_TO_INT(afp); +- +- if (af <= 0 || af >= af_max()) +- continue; +- +- if (first == 0 || af < first) +- first = af; +- +- if (last == 0 || af > last) +- last = af; +- } +- +- assert((first == 0) == (last == 0)); +- +- if (first == 0) { +- +- /* No entries in the valid range, block everything */ +- r = seccomp_rule_add( +- seccomp, +- SCMP_ACT_ERRNO(EPROTONOSUPPORT), +- SCMP_SYS(socket), +- 0); +- if (r < 0) +- goto finish; +- +- } else { +- +- /* Block everything below the first entry */ +- r = seccomp_rule_add( +- seccomp, +- SCMP_ACT_ERRNO(EPROTONOSUPPORT), +- SCMP_SYS(socket), +- 1, +- SCMP_A0(SCMP_CMP_LT, first)); +- if (r < 0) +- goto finish; +- +- /* Block everything above the last entry */ +- r = seccomp_rule_add( +- seccomp, +- SCMP_ACT_ERRNO(EPROTONOSUPPORT), +- SCMP_SYS(socket), +- 1, +- SCMP_A0(SCMP_CMP_GT, last)); +- if (r < 0) +- goto finish; +- +- /* Block everything between the first and last +- * entry */ +- for (af = 1; af < af_max(); af++) { +- +- if (set_contains(c->address_families, INT_TO_PTR(af))) +- continue; +- +- r = seccomp_rule_add( +- seccomp, +- SCMP_ACT_ERRNO(EPROTONOSUPPORT), +- SCMP_SYS(socket), +- 1, +- SCMP_A0(SCMP_CMP_EQ, af)); +- if (r < 0) +- goto finish; +- } +- } +- +- } else { +- void *af; +- +- /* If this is a blacklist, then generate one rule for +- * each address family that are then combined in OR +- * checks. */ +- +- SET_FOREACH(af, c->address_families, i) { +- +- r = seccomp_rule_add( +- seccomp, +- SCMP_ACT_ERRNO(EPROTONOSUPPORT), +- SCMP_SYS(socket), +- 1, +- SCMP_A0(SCMP_CMP_EQ, PTR_TO_INT(af))); +- if (r < 0) +- goto finish; +- } +- } +- +- r = seccomp_load(seccomp); +- +-finish: +- seccomp_release(seccomp); +- return r; ++ return seccomp_restrict_address_families(c->address_families, c->address_families_whitelist); + } + + static int apply_memory_deny_write_execute(const Unit* u, const ExecContext *c) { +- scmp_filter_ctx seccomp; +- int r; +- ++ assert(u); + assert(c); + ++ if (!c->memory_deny_write_execute) ++ return 0; ++ + if (skip_seccomp_unavailable(u, "MemoryDenyWriteExecute=")) + return 0; + +- r = seccomp_init_conservative(&seccomp, SCMP_ACT_ALLOW); +- if (r < 0) +- return r; +- +- r = seccomp_rule_add( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(mmap), +- 1, +- SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC|PROT_WRITE, PROT_EXEC|PROT_WRITE)); +- if (r < 0) +- goto finish; +- +- r = seccomp_rule_add( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(mprotect), +- 1, +- SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC, PROT_EXEC)); +- if (r < 0) +- goto finish; +- +- r = seccomp_rule_add( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(shmat), +- 1, +- SCMP_A2(SCMP_CMP_MASKED_EQ, SHM_EXEC, SHM_EXEC)); +- if (r < 0) +- goto finish; +- +- r = seccomp_load(seccomp); +- +-finish: +- seccomp_release(seccomp); +- return r; ++ return seccomp_memory_deny_write_execute(); + } + + static int apply_restrict_realtime(const Unit* u, const ExecContext *c) { +- static const int permitted_policies[] = { +- SCHED_OTHER, +- SCHED_BATCH, +- SCHED_IDLE, +- }; +- +- scmp_filter_ctx seccomp; +- unsigned i; +- int r, p, max_policy = 0; +- ++ assert(u); + assert(c); + ++ if (!c->restrict_realtime) ++ return 0; ++ + if (skip_seccomp_unavailable(u, "RestrictRealtime=")) + return 0; + +- r = seccomp_init_conservative(&seccomp, SCMP_ACT_ALLOW); +- if (r < 0) +- return r; +- +- /* Determine the highest policy constant we want to allow */ +- for (i = 0; i < ELEMENTSOF(permitted_policies); i++) +- if (permitted_policies[i] > max_policy) +- max_policy = permitted_policies[i]; +- +- /* Go through all policies with lower values than that, and block them -- unless they appear in the +- * whitelist. */ +- for (p = 0; p < max_policy; p++) { +- bool good = false; +- +- /* Check if this is in the whitelist. */ +- for (i = 0; i < ELEMENTSOF(permitted_policies); i++) +- if (permitted_policies[i] == p) { +- good = true; +- break; +- } +- +- if (good) +- continue; +- +- /* Deny this policy */ +- r = seccomp_rule_add( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(sched_setscheduler), +- 1, +- SCMP_A1(SCMP_CMP_EQ, p)); +- if (r < 0) +- goto finish; +- } +- +- /* Blacklist all other policies, i.e. the ones with higher values. Note that all comparisons are unsigned here, +- * hence no need no check for < 0 values. */ +- r = seccomp_rule_add( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(sched_setscheduler), +- 1, +- SCMP_A1(SCMP_CMP_GT, max_policy)); +- if (r < 0) +- goto finish; +- +- r = seccomp_load(seccomp); +- +-finish: +- seccomp_release(seccomp); +- return r; ++ return seccomp_restrict_realtime(); + } + + static int apply_protect_sysctl(const Unit *u, const ExecContext *c) { +- scmp_filter_ctx seccomp; +- int r; +- ++ assert(u); + assert(c); + + /* Turn off the legacy sysctl() system call. Many distributions turn this off while building the kernel, but + * let's protect even those systems where this is left on in the kernel. */ + ++ if (!c->protect_kernel_tunables) ++ return 0; ++ + if (skip_seccomp_unavailable(u, "ProtectKernelTunables=")) + return 0; + +- r = seccomp_init_conservative(&seccomp, SCMP_ACT_ALLOW); +- if (r < 0) +- return r; +- +- r = seccomp_rule_add( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(_sysctl), +- 0); +- if (r < 0) +- goto finish; +- +- r = seccomp_load(seccomp); +- +-finish: +- seccomp_release(seccomp); +- return r; ++ return seccomp_protect_sysctl(); + } + + static int apply_protect_kernel_modules(const Unit *u, const ExecContext *c) { ++ assert(u); + assert(c); + + /* Turn off module syscalls on ProtectKernelModules=yes */ + ++ if (!c->protect_kernel_modules) ++ return 0; ++ + if (skip_seccomp_unavailable(u, "ProtectKernelModules=")) + return 0; + +- return seccomp_load_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_MODULE, SCMP_ACT_ERRNO(EPERM)); ++ return seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_MODULE, SCMP_ACT_ERRNO(EPERM)); + } + + static int apply_private_devices(const Unit *u, const ExecContext *c) { ++ assert(u); + assert(c); + + /* If PrivateDevices= is set, also turn off iopl and all @raw-io syscalls. */ + ++ if (!c->private_devices) ++ return 0; ++ + if (skip_seccomp_unavailable(u, "PrivateDevices=")) + return 0; + +- return seccomp_load_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_RAW_IO, SCMP_ACT_ERRNO(EPERM)); ++ return seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + SYSCALL_FILTER_SET_RAW_IO, SCMP_ACT_ERRNO(EPERM)); + } + + static int apply_restrict_namespaces(Unit *u, const ExecContext *c) { ++ assert(u); + assert(c); + + if (!exec_context_restrict_namespaces_set(c)) +@@ -2168,40 +1990,6 @@ static int close_remaining_fds( + return close_all_fds(dont_close, n_dont_close); + } + +-static bool context_has_address_families(const ExecContext *c) { +- assert(c); +- +- return c->address_families_whitelist || +- !set_isempty(c->address_families); +-} +- +-static bool context_has_syscall_filters(const ExecContext *c) { +- assert(c); +- +- return c->syscall_whitelist || +- !set_isempty(c->syscall_filter) || +- !set_isempty(c->syscall_archs); +-} +- +-static bool context_has_no_new_privileges(const ExecContext *c) { +- assert(c); +- +- if (c->no_new_privileges) +- return true; +- +- if (have_effective_cap(CAP_SYS_ADMIN)) /* if we are privileged, we don't need NNP */ +- return false; +- +- return context_has_address_families(c) || /* we need NNP if we have any form of seccomp and are unprivileged */ +- c->memory_deny_write_execute || +- c->restrict_realtime || +- exec_context_restrict_namespaces_set(c) || +- c->protect_kernel_tunables || +- c->protect_kernel_modules || +- c->private_devices || +- context_has_syscall_filters(c); +-} +- + static int send_user_lookup( + Unit *unit, + int user_lookup_fd, +@@ -2753,28 +2541,22 @@ static int exec_child( + } + + #ifdef HAVE_SECCOMP +- if (context_has_address_families(context)) { +- r = apply_address_families(unit, context); +- if (r < 0) { +- *exit_status = EXIT_ADDRESS_FAMILIES; +- return r; +- } ++ r = apply_address_families(unit, context); ++ if (r < 0) { ++ *exit_status = EXIT_ADDRESS_FAMILIES; ++ return r; + } + +- if (context->memory_deny_write_execute) { +- r = apply_memory_deny_write_execute(unit, context); +- if (r < 0) { +- *exit_status = EXIT_SECCOMP; +- return r; +- } ++ r = apply_memory_deny_write_execute(unit, context); ++ if (r < 0) { ++ *exit_status = EXIT_SECCOMP; ++ return r; + } + +- if (context->restrict_realtime) { +- r = apply_restrict_realtime(unit, context); +- if (r < 0) { +- *exit_status = EXIT_SECCOMP; +- return r; +- } ++ r = apply_restrict_realtime(unit, context); ++ if (r < 0) { ++ *exit_status = EXIT_SECCOMP; ++ return r; + } + + r = apply_restrict_namespaces(unit, context); +@@ -2783,38 +2565,36 @@ static int exec_child( + return r; + } + +- if (context->protect_kernel_tunables) { +- r = apply_protect_sysctl(unit, context); +- if (r < 0) { +- *exit_status = EXIT_SECCOMP; +- return r; +- } ++ r = apply_protect_sysctl(unit, context); ++ if (r < 0) { ++ *exit_status = EXIT_SECCOMP; ++ return r; + } + +- if (context->protect_kernel_modules) { +- r = apply_protect_kernel_modules(unit, context); +- if (r < 0) { +- *exit_status = EXIT_SECCOMP; +- return r; +- } ++ r = apply_protect_kernel_modules(unit, context); ++ if (r < 0) { ++ *exit_status = EXIT_SECCOMP; ++ return r; + } + +- if (context->private_devices) { +- r = apply_private_devices(unit, context); +- if (r < 0) { +- *exit_status = EXIT_SECCOMP; +- return r; +- } ++ r = apply_private_devices(unit, context); ++ if (r < 0) { ++ *exit_status = EXIT_SECCOMP; ++ return r; ++ } ++ ++ r = apply_syscall_archs(unit, context); ++ if (r < 0) { ++ *exit_status = EXIT_SECCOMP; ++ return r; + } + + /* This really should remain the last step before the execve(), to make sure our own code is unaffected + * by the filter as little as possible. */ +- if (context_has_syscall_filters(context)) { +- r = apply_seccomp(unit, context); +- if (r < 0) { +- *exit_status = EXIT_SECCOMP; +- return r; +- } ++ r = apply_syscall_filter(unit, context); ++ if (r < 0) { ++ *exit_status = EXIT_SECCOMP; ++ return r; + } + #endif + } +diff --git a/src/core/main.c b/src/core/main.c +index 94602611a7..fc1ae123a8 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -1185,44 +1185,16 @@ oom: + + static int enforce_syscall_archs(Set *archs) { + #ifdef HAVE_SECCOMP +- scmp_filter_ctx *seccomp; +- Iterator i; +- void *id; + int r; + + if (!is_seccomp_available()) + return 0; + +- seccomp = seccomp_init(SCMP_ACT_ALLOW); +- if (!seccomp) +- return log_oom(); +- +- SET_FOREACH(id, arg_syscall_archs, i) { +- r = seccomp_arch_add(seccomp, PTR_TO_UINT32(id) - 1); +- if (r == -EEXIST) +- continue; +- if (r < 0) { +- log_error_errno(r, "Failed to add architecture to seccomp: %m"); +- goto finish; +- } +- } +- +- r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); +- if (r < 0) { +- log_error_errno(r, "Failed to unset NO_NEW_PRIVS: %m"); +- goto finish; +- } +- +- r = seccomp_load(seccomp); ++ r = seccomp_restrict_archs(arg_syscall_archs); + if (r < 0) +- log_error_errno(r, "Failed to add install architecture seccomp: %m"); +- +-finish: +- seccomp_release(seccomp); +- return r; +-#else +- return 0; ++ return log_error_errno(r, "Failed to enforce system call architecture restrication: %m"); + #endif ++ return 0; + } + + static int status_welcome(void) { +diff --git a/src/nspawn/nspawn-seccomp.c b/src/nspawn/nspawn-seccomp.c +index 03a397d30c..72ecc51b16 100644 +--- a/src/nspawn/nspawn-seccomp.c ++++ b/src/nspawn/nspawn-seccomp.c +@@ -26,20 +26,21 @@ + #include + #endif + ++#include "alloc-util.h" + #include "log.h" +- +-#ifdef HAVE_SECCOMP +-#include "seccomp-util.h" +-#endif +- + #include "nspawn-seccomp.h" ++#ifdef HAVE_SECCOMP ++#include "seccomp-util.h" ++#endif ++#include "string-util.h" + + #ifdef HAVE_SECCOMP + +-static int seccomp_add_default_syscall_filter(scmp_filter_ctx ctx, +- uint64_t cap_list_retain) { +- unsigned i; +- int r; ++static int seccomp_add_default_syscall_filter( ++ scmp_filter_ctx ctx, ++ uint32_t arch, ++ uint64_t cap_list_retain) { ++ + static const struct { + uint64_t capability; + int syscall_num; +@@ -111,23 +112,29 @@ static int seccomp_add_default_syscall_filter(scmp_filter_ctx ctx, + { CAP_SYS_TIME, SCMP_SYS(settimeofday) }, + { CAP_SYS_TIME, SCMP_SYS(stime) }, + }; ++ unsigned i; ++ int r, c = 0; + + for (i = 0; i < ELEMENTSOF(blacklist); i++) { + if (blacklist[i].capability != 0 && (cap_list_retain & (1ULL << blacklist[i].capability))) + continue; + +- r = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), blacklist[i].syscall_num, 0); +- if (r == -EFAULT) +- continue; /* unknown syscall */ +- if (r < 0) +- return log_error_errno(r, "Failed to block syscall: %m"); ++ r = seccomp_rule_add_exact(ctx, SCMP_ACT_ERRNO(EPERM), blacklist[i].syscall_num, 0); ++ if (r < 0) { ++ /* If the system call is not known on this architecture, then that's fine, let's ignore it */ ++ _cleanup_free_ char *n = NULL; ++ ++ n = seccomp_syscall_resolve_num_arch(arch, blacklist[i].syscall_num); ++ log_debug_errno(r, "Failed to add rule for system call %s, ignoring: %m", strna(n)); ++ } else ++ c++; + } + +- return 0; ++ return c; + } + + int setup_seccomp(uint64_t cap_list_retain) { +- scmp_filter_ctx seccomp; ++ uint32_t arch; + int r; + + if (!is_seccomp_available()) { +@@ -135,45 +142,51 @@ int setup_seccomp(uint64_t cap_list_retain) { + return 0; + } + +- r = seccomp_init_conservative(&seccomp, SCMP_ACT_ALLOW); +- if (r < 0) +- return log_error_errno(r, "Failed to allocate seccomp object: %m"); +- +- r = seccomp_add_default_syscall_filter(seccomp, cap_list_retain); +- if (r < 0) +- goto finish; +- +- /* +- Audit is broken in containers, much of the userspace audit +- hookup will fail if running inside a container. We don't +- care and just turn off creation of audit sockets. +- +- This will make socket(AF_NETLINK, *, NETLINK_AUDIT) fail +- with EAFNOSUPPORT which audit userspace uses as indication +- that audit is disabled in the kernel. +- */ +- +- r = seccomp_rule_add( +- seccomp, +- SCMP_ACT_ERRNO(EAFNOSUPPORT), +- SCMP_SYS(socket), +- 2, +- SCMP_A0(SCMP_CMP_EQ, AF_NETLINK), +- SCMP_A2(SCMP_CMP_EQ, NETLINK_AUDIT)); +- if (r < 0) { +- log_error_errno(r, "Failed to add audit seccomp rule: %m"); +- goto finish; +- } ++ SECCOMP_FOREACH_LOCAL_ARCH(arch) { ++ _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; ++ int n; ++ ++ log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); ++ ++ r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW); ++ if (r < 0) ++ return log_error_errno(r, "Failed to allocate seccomp object: %m"); ++ ++ n = seccomp_add_default_syscall_filter(seccomp, arch, cap_list_retain); ++ if (n < 0) ++ return n; ++ ++ /* ++ Audit is broken in containers, much of the userspace audit hookup will fail if running inside a ++ container. We don't care and just turn off creation of audit sockets. ++ ++ This will make socket(AF_NETLINK, *, NETLINK_AUDIT) fail with EAFNOSUPPORT which audit userspace uses ++ as indication that audit is disabled in the kernel. ++ */ ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EAFNOSUPPORT), ++ SCMP_SYS(socket), ++ 2, ++ SCMP_A0(SCMP_CMP_EQ, AF_NETLINK), ++ SCMP_A2(SCMP_CMP_EQ, NETLINK_AUDIT)); ++ if (r < 0) ++ log_debug_errno(r, "Failed to add audit seccomp rule, ignoring: %m"); ++ else ++ n++; ++ ++ if (n <= 0) /* no rule added? then skip this architecture */ ++ continue; + +- r = seccomp_load(seccomp); +- if (r < 0) { +- log_error_errno(r, "Failed to install seccomp audit filter: %m"); +- goto finish; ++ r = seccomp_load(seccomp); ++ if (IN_SET(r, -EPERM, -EACCES)) ++ return log_error_errno(r, "Failed to install seccomp audit filter: %m"); ++ if (r < 0) ++ log_debug_errno(r, "Failed to install filter set for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); + } + +-finish: +- seccomp_release(seccomp); +- return r; ++ return 0; + } + + #else +diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c +index 55b97e1efb..aa37e12db7 100644 +--- a/src/shared/seccomp-util.c ++++ b/src/shared/seccomp-util.c +@@ -18,17 +18,52 @@ + ***/ + + #include ++#include + #include + #include ++#include + #include +-#include ++#include + ++#include "af-list.h" + #include "alloc-util.h" + #include "macro.h" + #include "nsflags.h" + #include "seccomp-util.h" + #include "string-util.h" + #include "util.h" ++#include "errno-list.h" ++ ++const uint32_t seccomp_local_archs[] = { ++ ++#if defined(__i386__) || defined(__x86_64__) ++ SCMP_ARCH_X86, ++ SCMP_ARCH_X86_64, ++ SCMP_ARCH_X32, ++ ++#elif defined(__arm__) || defined(__aarch64__) ++ SCMP_ARCH_ARM, ++ SCMP_ARCH_AARCH64, ++ ++#elif defined(__mips__) || defined(__mips64__) ++ SCMP_ARCH_MIPS, ++ SCMP_ARCH_MIPS64, ++ SCMP_ARCH_MIPS64N32, ++ SCMP_ARCH_MIPSEL, ++ SCMP_ARCH_MIPSEL64, ++ SCMP_ARCH_MIPSEL64N32, ++ ++#elif defined(__powerpc__) || defined(__powerpc64__) ++ SCMP_ARCH_PPC, ++ SCMP_ARCH_PPC64, ++ SCMP_ARCH_PPC64LE, ++ ++#elif defined(__s390__) || defined(__s390x__) ++ SCMP_ARCH_S390, ++ SCMP_ARCH_S390X, ++#endif ++ (uint32_t) -1 ++ }; + + const char* seccomp_arch_to_string(uint32_t c) { + /* Maintain order used in . +@@ -122,18 +157,37 @@ int seccomp_arch_from_string(const char *n, uint32_t *ret) { + return 0; + } + +-int seccomp_init_conservative(scmp_filter_ctx *ret, uint32_t default_action) { ++int seccomp_init_for_arch(scmp_filter_ctx *ret, uint32_t arch, uint32_t default_action) { + scmp_filter_ctx seccomp; + int r; + +- /* Much like seccomp_init(), but tries to be a bit more conservative in its defaults: all secondary archs are +- * added by default, and NNP is turned off. */ ++ /* Much like seccomp_init(), but initializes the filter for one specific architecture only, without affecting ++ * any others. Also, turns off the NNP fiddling. */ + + seccomp = seccomp_init(default_action); + if (!seccomp) + return -ENOMEM; + +- r = seccomp_add_secondary_archs(seccomp); ++ if (arch != SCMP_ARCH_NATIVE && ++ arch != seccomp_arch_native()) { ++ ++ r = seccomp_arch_add(seccomp, arch); ++ if (r < 0) ++ goto finish; ++ ++ r = seccomp_arch_remove(seccomp, seccomp_arch_native()); ++ if (r < 0) ++ goto finish; ++ ++ assert(seccomp_arch_exist(seccomp, arch) >= 0); ++ assert(seccomp_arch_exist(seccomp, SCMP_ARCH_NATIVE) == -EEXIST); ++ assert(seccomp_arch_exist(seccomp, seccomp_arch_native()) == -EEXIST); ++ } else { ++ assert(seccomp_arch_exist(seccomp, SCMP_ARCH_NATIVE) >= 0); ++ assert(seccomp_arch_exist(seccomp, seccomp_arch_native()) >= 0); ++ } ++ ++ r = seccomp_attr_set(seccomp, SCMP_FLTATR_ACT_BADARCH, SCMP_ACT_ALLOW); + if (r < 0) + goto finish; + +@@ -149,56 +203,6 @@ finish: + return r; + } + +-int seccomp_add_secondary_archs(scmp_filter_ctx ctx) { +- +- /* Add in all possible secondary archs we are aware of that +- * this kernel might support. */ +- +- static const int seccomp_arches[] = { +-#if defined(__i386__) || defined(__x86_64__) +- SCMP_ARCH_X86, +- SCMP_ARCH_X86_64, +- SCMP_ARCH_X32, +- +-#elif defined(__arm__) || defined(__aarch64__) +- SCMP_ARCH_ARM, +- SCMP_ARCH_AARCH64, +- +-#elif defined(__arm__) || defined(__aarch64__) +- SCMP_ARCH_ARM, +- SCMP_ARCH_AARCH64, +- +-#elif defined(__mips__) || defined(__mips64__) +- SCMP_ARCH_MIPS, +- SCMP_ARCH_MIPS64, +- SCMP_ARCH_MIPS64N32, +- SCMP_ARCH_MIPSEL, +- SCMP_ARCH_MIPSEL64, +- SCMP_ARCH_MIPSEL64N32, +- +-#elif defined(__powerpc__) || defined(__powerpc64__) +- SCMP_ARCH_PPC, +- SCMP_ARCH_PPC64, +- SCMP_ARCH_PPC64LE, +- +-#elif defined(__s390__) || defined(__s390x__) +- SCMP_ARCH_S390, +- SCMP_ARCH_S390X, +-#endif +- }; +- +- unsigned i; +- int r; +- +- for (i = 0; i < ELEMENTSOF(seccomp_arches); i++) { +- r = seccomp_arch_add(ctx, seccomp_arches[i]); +- if (r < 0 && r != -EEXIST) +- return r; +- } +- +- return 0; +-} +- + static bool is_basic_seccomp_available(void) { + int r; + r = prctl(PR_GET_SECCOMP, 0, 0, 0, 0); +@@ -523,7 +527,12 @@ const SyscallFilterSet *syscall_filter_set_find(const char *name) { + return NULL; + } + +-int seccomp_add_syscall_filter_set(scmp_filter_ctx seccomp, const SyscallFilterSet *set, uint32_t action) { ++static int seccomp_add_syscall_filter_set( ++ scmp_filter_ctx seccomp, ++ uint32_t default_action, ++ const SyscallFilterSet *set, ++ uint32_t action) { ++ + const char *sys; + int r; + +@@ -540,47 +549,102 @@ int seccomp_add_syscall_filter_set(scmp_filter_ctx seccomp, const SyscallFilterS + if (!other) + return -EINVAL; + +- r = seccomp_add_syscall_filter_set(seccomp, other, action); ++ r = seccomp_add_syscall_filter_set(seccomp, default_action, other, action); ++ if (r < 0) ++ return r; + } else { + id = seccomp_syscall_resolve_name(sys); + if (id == __NR_SCMP_ERROR) +- return -EINVAL; ++ return -EINVAL; /* Not known at all? Then that's a real error */ + +- r = seccomp_rule_add(seccomp, action, id, 0); ++ r = seccomp_rule_add_exact(seccomp, action, id, 0); ++ if (r < 0) ++ /* If the system call is not known on this architecture, then that's fine, let's ignore it */ ++ log_debug_errno(r, "Failed to add rule for system call %s, ignoring: %m", sys); + } +- if (r < 0) +- return r; + } + + return 0; + } + +-int seccomp_load_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action) { +- scmp_filter_ctx seccomp; ++int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action) { ++ uint32_t arch; + int r; + + assert(set); + +- /* The one-stop solution: allocate a seccomp object, add a filter to it, and apply it */ ++ /* The one-stop solution: allocate a seccomp object, add the specified filter to it, and apply it. Once for ++ * earch local arch. */ + +- r = seccomp_init_conservative(&seccomp, default_action); +- if (r < 0) +- return r; ++ SECCOMP_FOREACH_LOCAL_ARCH(arch) { ++ _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; + +- r = seccomp_add_syscall_filter_set(seccomp, set, action); +- if (r < 0) +- goto finish; ++ log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); + +- r = seccomp_load(seccomp); ++ r = seccomp_init_for_arch(&seccomp, arch, default_action); ++ if (r < 0) ++ return r; + +-finish: +- seccomp_release(seccomp); +- return r; ++ r = seccomp_add_syscall_filter_set(seccomp, default_action, set, action); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add filter set, ignoring: %m"); ++ continue; ++ } ++ ++ r = seccomp_load(seccomp); ++ if (IN_SET(r, -EPERM, -EACCES)) ++ return r; ++ if (r < 0) ++ log_debug_errno(r, "Failed to install filter set for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ } ++ ++ return 0; ++} ++ ++int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Set* set, uint32_t action) { ++ uint32_t arch; ++ int r; ++ ++ /* Similar to seccomp_load_syscall_filter_set(), but takes a raw Set* of syscalls, instead of a ++ * SyscallFilterSet* table. */ ++ ++ if (set_isempty(set) && default_action == SCMP_ACT_ALLOW) ++ return 0; ++ ++ SECCOMP_FOREACH_LOCAL_ARCH(arch) { ++ _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; ++ Iterator i; ++ void *id; ++ ++ log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); ++ ++ r = seccomp_init_for_arch(&seccomp, arch, default_action); ++ if (r < 0) ++ return r; ++ ++ SET_FOREACH(id, set, i) { ++ r = seccomp_rule_add_exact(seccomp, action, PTR_TO_INT(id) - 1, 0); ++ if (r < 0) { ++ /* If the system call is not known on this architecture, then that's fine, let's ignore it */ ++ _cleanup_free_ char *n = NULL; ++ ++ n = seccomp_syscall_resolve_num_arch(arch, PTR_TO_INT(id) - 1); ++ log_debug_errno(r, "Failed to add rule for system call %s, ignoring: %m", strna(n)); ++ } ++ } ++ ++ r = seccomp_load(seccomp); ++ if (IN_SET(r, -EPERM, -EACCES)) ++ return r; ++ if (r < 0) ++ log_debug_errno(r, "Failed to install filter set for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ } ++ ++ return 0; + } + + int seccomp_restrict_namespaces(unsigned long retain) { +- scmp_filter_ctx seccomp; +- unsigned i; ++ uint32_t arch; + int r; + + if (log_get_max_level() >= LOG_DEBUG) { +@@ -594,74 +658,420 @@ int seccomp_restrict_namespaces(unsigned long retain) { + if ((retain & NAMESPACE_FLAGS_ALL) == NAMESPACE_FLAGS_ALL) + return 0; + +- r = seccomp_init_conservative(&seccomp, SCMP_ACT_ALLOW); +- if (r < 0) +- return r; +- +- if ((retain & NAMESPACE_FLAGS_ALL) == 0) +- /* If every single kind of namespace shall be prohibited, then let's block the whole setns() syscall +- * altogether. */ +- r = seccomp_rule_add( ++ SECCOMP_FOREACH_LOCAL_ARCH(arch) { ++ _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; ++ unsigned i; ++ ++ log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); ++ ++ r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW); ++ if (r < 0) ++ return r; ++ ++ if ((retain & NAMESPACE_FLAGS_ALL) == 0) ++ /* If every single kind of namespace shall be prohibited, then let's block the whole setns() syscall ++ * altogether. */ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(setns), ++ 0); ++ else ++ /* Otherwise, block only the invocations with the appropriate flags in the loop below, but also the ++ * special invocation with a zero flags argument, right here. */ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(setns), ++ 1, ++ SCMP_A1(SCMP_CMP_EQ, 0)); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add setns() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ continue; ++ } ++ ++ for (i = 0; namespace_flag_map[i].name; i++) { ++ unsigned long f; ++ ++ f = namespace_flag_map[i].flag; ++ if ((retain & f) == f) { ++ log_debug("Permitting %s.", namespace_flag_map[i].name); ++ continue; ++ } ++ ++ log_debug("Blocking %s.", namespace_flag_map[i].name); ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(unshare), ++ 1, ++ SCMP_A0(SCMP_CMP_MASKED_EQ, f, f)); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add unshare() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ break; ++ } ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(clone), ++ 1, ++ SCMP_A0(SCMP_CMP_MASKED_EQ, f, f)); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add clone() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ break; ++ } ++ ++ if ((retain & NAMESPACE_FLAGS_ALL) != 0) { ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(setns), ++ 1, ++ SCMP_A1(SCMP_CMP_MASKED_EQ, f, f)); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add setns() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ break; ++ } ++ } ++ } ++ if (r < 0) ++ continue; ++ ++ r = seccomp_load(seccomp); ++ if (IN_SET(r, -EPERM, -EACCES)) ++ return r; ++ if (r < 0) ++ log_debug_errno(r, "Failed to install namespace restriction rules for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ } ++ ++ return 0; ++} ++ ++int seccomp_protect_sysctl(void) { ++ uint32_t arch; ++ int r; ++ ++ SECCOMP_FOREACH_LOCAL_ARCH(arch) { ++ _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; ++ ++ log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); ++ ++ r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW); ++ if (r < 0) ++ return r; ++ ++ r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(setns), ++ SCMP_SYS(_sysctl), + 0); +- else +- /* Otherwise, block only the invocations with the appropriate flags in the loop below, but also the +- * special invocation with a zero flags argument, right here. */ +- r = seccomp_rule_add( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(setns), +- 1, +- SCMP_A1(SCMP_CMP_EQ, 0)); +- if (r < 0) +- goto finish; +- +- for (i = 0; namespace_flag_map[i].name; i++) { +- unsigned long f; +- +- f = namespace_flag_map[i].flag; +- if ((retain & f) == f) { +- log_debug("Permitting %s.", namespace_flag_map[i].name); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add _sysctl() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); + continue; + } + +- log_debug("Blocking %s.", namespace_flag_map[i].name); +- +- r = seccomp_rule_add( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(unshare), +- 1, +- SCMP_A0(SCMP_CMP_MASKED_EQ, f, f)); ++ r = seccomp_load(seccomp); ++ if (IN_SET(r, -EPERM, -EACCES)) ++ return r; + if (r < 0) +- goto finish; +- +- r = seccomp_rule_add( +- seccomp, +- SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(clone), +- 1, +- SCMP_A0(SCMP_CMP_MASKED_EQ, f, f)); ++ log_debug_errno(r, "Failed to install sysctl protection rules for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ } ++ ++ return 0; ++} ++ ++int seccomp_restrict_address_families(Set *address_families, bool whitelist) { ++ uint32_t arch; ++ int r; ++ ++ SECCOMP_FOREACH_LOCAL_ARCH(arch) { ++ _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; ++ Iterator i; ++ ++ log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); ++ ++ r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW); + if (r < 0) +- goto finish; ++ return r; ++ ++ if (whitelist) { ++ int af, first = 0, last = 0; ++ void *afp; ++ ++ /* If this is a whitelist, we first block the address families that are out of range and then ++ * everything that is not in the set. First, we find the lowest and highest address family in ++ * the set. */ ++ ++ SET_FOREACH(afp, address_families, i) { ++ af = PTR_TO_INT(afp); ++ ++ if (af <= 0 || af >= af_max()) ++ continue; ++ ++ if (first == 0 || af < first) ++ first = af; ++ ++ if (last == 0 || af > last) ++ last = af; ++ } ++ ++ assert((first == 0) == (last == 0)); ++ ++ if (first == 0) { ++ ++ /* No entries in the valid range, block everything */ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EAFNOSUPPORT), ++ SCMP_SYS(socket), ++ 0); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add socket() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ continue; ++ } ++ ++ } else { ++ ++ /* Block everything below the first entry */ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EAFNOSUPPORT), ++ SCMP_SYS(socket), ++ 1, ++ SCMP_A0(SCMP_CMP_LT, first)); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add socket() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ continue; ++ } ++ ++ /* Block everything above the last entry */ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EAFNOSUPPORT), ++ SCMP_SYS(socket), ++ 1, ++ SCMP_A0(SCMP_CMP_GT, last)); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add socket() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ continue; ++ } ++ ++ /* Block everything between the first and last entry */ ++ for (af = 1; af < af_max(); af++) { ++ ++ if (set_contains(address_families, INT_TO_PTR(af))) ++ continue; ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EAFNOSUPPORT), ++ SCMP_SYS(socket), ++ 1, ++ SCMP_A0(SCMP_CMP_EQ, af)); ++ if (r < 0) ++ break; ++ } ++ ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add socket() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ continue; ++ } ++ } ++ ++ } else { ++ void *af; ++ ++ /* If this is a blacklist, then generate one rule for ++ * each address family that are then combined in OR ++ * checks. */ ++ ++ SET_FOREACH(af, address_families, i) { ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EAFNOSUPPORT), ++ SCMP_SYS(socket), ++ 1, ++ SCMP_A0(SCMP_CMP_EQ, PTR_TO_INT(af))); ++ if (r < 0) ++ break; ++ } ++ ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add socket() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ continue; ++ } ++ } ++ ++ r = seccomp_load(seccomp); ++ if (IN_SET(r, -EPERM, -EACCES)) ++ return r; ++ if (r < 0) ++ log_debug_errno(r, "Failed to install socket family rules for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ } ++ ++ return 0; ++} ++ ++int seccomp_restrict_realtime(void) { ++ static const int permitted_policies[] = { ++ SCHED_OTHER, ++ SCHED_BATCH, ++ SCHED_IDLE, ++ }; ++ ++ int r, max_policy = 0; ++ uint32_t arch; ++ unsigned i; ++ ++ /* Determine the highest policy constant we want to allow */ ++ for (i = 0; i < ELEMENTSOF(permitted_policies); i++) ++ if (permitted_policies[i] > max_policy) ++ max_policy = permitted_policies[i]; ++ ++ SECCOMP_FOREACH_LOCAL_ARCH(arch) { ++ _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; ++ int p; ++ ++ log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); ++ ++ r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW); ++ if (r < 0) ++ return r; ++ ++ /* Go through all policies with lower values than that, and block them -- unless they appear in the ++ * whitelist. */ ++ for (p = 0; p < max_policy; p++) { ++ bool good = false; ++ ++ /* Check if this is in the whitelist. */ ++ for (i = 0; i < ELEMENTSOF(permitted_policies); i++) ++ if (permitted_policies[i] == p) { ++ good = true; ++ break; ++ } ++ ++ if (good) ++ continue; + +- if ((retain & NAMESPACE_FLAGS_ALL) != 0) { +- r = seccomp_rule_add( ++ /* Deny this policy */ ++ r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), +- SCMP_SYS(setns), ++ SCMP_SYS(sched_setscheduler), + 1, +- SCMP_A1(SCMP_CMP_MASKED_EQ, f, f)); +- if (r < 0) +- goto finish; ++ SCMP_A1(SCMP_CMP_EQ, p)); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add scheduler rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ continue; ++ } + } ++ ++ /* Blacklist all other policies, i.e. the ones with higher values. Note that all comparisons are ++ * unsigned here, hence no need no check for < 0 values. */ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(sched_setscheduler), ++ 1, ++ SCMP_A1(SCMP_CMP_GT, max_policy)); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add scheduler rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ continue; ++ } ++ ++ r = seccomp_load(seccomp); ++ if (IN_SET(r, -EPERM, -EACCES)) ++ return r; ++ if (r < 0) ++ log_debug_errno(r, "Failed to install realtime protection rules for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ } ++ ++ return 0; ++} ++ ++int seccomp_memory_deny_write_execute(void) { ++ uint32_t arch; ++ int r; ++ ++ SECCOMP_FOREACH_LOCAL_ARCH(arch) { ++ _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; ++ ++ log_debug("Operating on architecture: %s", seccomp_arch_to_string(arch)); ++ ++ r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW); ++ if (r < 0) ++ return r; ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(mmap), ++ 1, ++ SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC|PROT_WRITE, PROT_EXEC|PROT_WRITE)); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add mmap() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ continue; ++ } ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(mprotect), ++ 1, ++ SCMP_A2(SCMP_CMP_MASKED_EQ, PROT_EXEC, PROT_EXEC)); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add mprotect() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ continue; ++ } ++ ++ r = seccomp_rule_add_exact( ++ seccomp, ++ SCMP_ACT_ERRNO(EPERM), ++ SCMP_SYS(shmat), ++ 1, ++ SCMP_A2(SCMP_CMP_MASKED_EQ, SHM_EXEC, SHM_EXEC)); ++ if (r < 0) { ++ log_debug_errno(r, "Failed to add shmat() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ continue; ++ } ++ ++ r = seccomp_load(seccomp); ++ if (IN_SET(r, -EPERM, -EACCES)) ++ return r; ++ if (r < 0) ++ log_debug_errno(r, "Failed to install MemoryDenyWriteExecute= rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); ++ } ++ ++ return 0; ++} ++ ++int seccomp_restrict_archs(Set *archs) { ++ _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; ++ Iterator i; ++ void *id; ++ int r; ++ ++ /* This installs a filter with no rules, but that restricts the system call architectures to the specified ++ * list. */ ++ ++ seccomp = seccomp_init(SCMP_ACT_ALLOW); ++ if (!seccomp) ++ return -ENOMEM; ++ ++ SET_FOREACH(id, archs, i) { ++ r = seccomp_arch_add(seccomp, PTR_TO_UINT32(id) - 1); ++ if (r == -EEXIST) ++ continue; ++ if (r < 0) ++ return r; + } + +- r = seccomp_load(seccomp); ++ r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0); ++ if (r < 0) ++ return r; + +-finish: +- seccomp_release(seccomp); +- return r; ++ return seccomp_load(seccomp); + } +diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h +index e325dab628..50e4f43c43 100644 +--- a/src/shared/seccomp-util.h ++++ b/src/shared/seccomp-util.h +@@ -23,12 +23,12 @@ + #include + #include + ++#include "set.h" ++ + const char* seccomp_arch_to_string(uint32_t c); + int seccomp_arch_from_string(const char *n, uint32_t *ret); + +-int seccomp_init_conservative(scmp_filter_ctx *ret, uint32_t default_action); +- +-int seccomp_add_secondary_archs(scmp_filter_ctx c); ++int seccomp_init_for_arch(scmp_filter_ctx *ret, uint32_t arch, uint32_t default_action); + + bool is_seccomp_available(void); + +@@ -61,8 +61,21 @@ extern const SyscallFilterSet syscall_filter_sets[]; + + const SyscallFilterSet *syscall_filter_set_find(const char *name); + +-int seccomp_add_syscall_filter_set(scmp_filter_ctx seccomp, const SyscallFilterSet *set, uint32_t action); +- +-int seccomp_load_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action); ++int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action); ++int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Set* set, uint32_t action); + ++int seccomp_restrict_archs(Set *archs); + int seccomp_restrict_namespaces(unsigned long retain); ++int seccomp_protect_sysctl(void); ++int seccomp_restrict_address_families(Set *address_families, bool whitelist); ++int seccomp_restrict_realtime(void); ++int seccomp_memory_deny_write_execute(void); ++ ++extern const uint32_t seccomp_local_archs[]; ++ ++#define SECCOMP_FOREACH_LOCAL_ARCH(arch) \ ++ for (unsigned _i = ({ (arch) = seccomp_local_archs[0]; 0; }); \ ++ seccomp_local_archs[_i] != (uint32_t) -1; \ ++ (arch) = seccomp_local_archs[++_i]) ++ ++DEFINE_TRIVIAL_CLEANUP_FUNC(scmp_filter_ctx, seccomp_release); +diff --git a/src/test/test-execute.c b/src/test/test-execute.c +index 6029853e3e..7d7790cf1e 100644 +--- a/src/test/test-execute.c ++++ b/src/test/test-execute.c +@@ -457,6 +457,7 @@ int main(int argc, char *argv[]) { + }; + int r; + ++ log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + +diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c +index beb6a7f422..6f15879c45 100644 +--- a/src/test/test-seccomp.c ++++ b/src/test/test-seccomp.c +@@ -17,10 +17,12 @@ + along with systemd; If not, see . + ***/ + ++#include + #include + #include ++#include + #include +-#include ++#include + + #include "alloc-util.h" + #include "fd-util.h" +@@ -30,8 +32,10 @@ + #include "process-util.h" + #include "raw-clone.h" + #include "seccomp-util.h" ++#include "set.h" + #include "string-util.h" + #include "util.h" ++#include "virt.h" + + static void test_seccomp_arch_to_string(void) { + uint32_t a, b; +@@ -92,7 +96,6 @@ static void test_filter_sets(void) { + + if (!is_seccomp_available()) + return; +- + if (geteuid() != 0) + return; + +@@ -108,16 +111,16 @@ static void test_filter_sets(void) { + int fd; + + if (i == SYSCALL_FILTER_SET_DEFAULT) /* if we look at the default set, whitelist instead of blacklist */ +- r = seccomp_load_filter_set(SCMP_ACT_ERRNO(EPERM), syscall_filter_sets + i, SCMP_ACT_ALLOW); ++ r = seccomp_load_syscall_filter_set(SCMP_ACT_ERRNO(EUCLEAN), syscall_filter_sets + i, SCMP_ACT_ALLOW); + else +- r = seccomp_load_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + i, SCMP_ACT_ERRNO(EPERM)); ++ r = seccomp_load_syscall_filter_set(SCMP_ACT_ALLOW, syscall_filter_sets + i, SCMP_ACT_ERRNO(EUCLEAN)); + if (r < 0) + _exit(EXIT_FAILURE); + + /* Test the sycall filter with one random system call */ + fd = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC); + if (IN_SET(i, SYSCALL_FILTER_SET_IO_EVENT, SYSCALL_FILTER_SET_DEFAULT)) +- assert_se(fd < 0 && errno == EPERM); ++ assert_se(fd < 0 && errno == EUCLEAN); + else { + assert_se(fd >= 0); + safe_close(fd); +@@ -132,8 +135,8 @@ static void test_filter_sets(void) { + + static void test_restrict_namespace(void) { + _cleanup_free_ char *s = NULL; +- pid_t pid; + unsigned long ul; ++ pid_t pid; + + assert_se(namespace_flag_to_string(0) == NULL); + assert_se(streq(namespace_flag_to_string(CLONE_NEWNS), "mnt")); +@@ -157,7 +160,6 @@ static void test_restrict_namespace(void) { + + if (!is_seccomp_available()) + return; +- + if (geteuid() != 0) + return; + +@@ -216,6 +218,256 @@ static void test_restrict_namespace(void) { + assert_se(wait_for_terminate_and_warn("nsseccomp", pid, true) == EXIT_SUCCESS); + } + ++static void test_protect_sysctl(void) { ++ pid_t pid; ++ ++ if (!is_seccomp_available()) ++ return; ++ if (geteuid() != 0) ++ return; ++ ++ if (detect_container() > 0) /* in containers _sysctl() is likely missing anyway */ ++ return; ++ ++ pid = fork(); ++ assert_se(pid >= 0); ++ ++ if (pid == 0) { ++ assert_se(syscall(__NR__sysctl, NULL) < 0); ++ assert_se(errno == EFAULT); ++ ++ assert_se(seccomp_protect_sysctl() >= 0); ++ ++ assert_se(syscall(__NR__sysctl, 0, 0, 0) < 0); ++ assert_se(errno == EPERM); ++ ++ _exit(EXIT_SUCCESS); ++ } ++ ++ assert_se(wait_for_terminate_and_warn("sysctlseccomp", pid, true) == EXIT_SUCCESS); ++} ++ ++static void test_restrict_address_families(void) { ++ pid_t pid; ++ ++ if (!is_seccomp_available()) ++ return; ++ if (geteuid() != 0) ++ return; ++ ++ pid = fork(); ++ assert_se(pid >= 0); ++ ++ if (pid == 0) { ++ int fd; ++ Set *s; ++ ++ fd = socket(AF_INET, SOCK_DGRAM, 0); ++ assert_se(fd >= 0); ++ safe_close(fd); ++ ++ fd = socket(AF_UNIX, SOCK_DGRAM, 0); ++ assert_se(fd >= 0); ++ safe_close(fd); ++ ++ fd = socket(AF_NETLINK, SOCK_DGRAM, 0); ++ assert_se(fd >= 0); ++ safe_close(fd); ++ ++ assert_se(s = set_new(NULL)); ++ assert_se(set_put(s, INT_TO_PTR(AF_UNIX)) >= 0); ++ ++ assert_se(seccomp_restrict_address_families(s, false) >= 0); ++ ++ fd = socket(AF_INET, SOCK_DGRAM, 0); ++ assert_se(fd >= 0); ++ safe_close(fd); ++ ++ assert_se(socket(AF_UNIX, SOCK_DGRAM, 0) < 0); ++ assert_se(errno == EAFNOSUPPORT); ++ ++ fd = socket(AF_NETLINK, SOCK_DGRAM, 0); ++ assert_se(fd >= 0); ++ safe_close(fd); ++ ++ set_clear(s); ++ ++ assert_se(set_put(s, INT_TO_PTR(AF_INET)) >= 0); ++ ++ assert_se(seccomp_restrict_address_families(s, true) >= 0); ++ ++ fd = socket(AF_INET, SOCK_DGRAM, 0); ++ assert_se(fd >= 0); ++ safe_close(fd); ++ ++ assert_se(socket(AF_UNIX, SOCK_DGRAM, 0) < 0); ++ assert_se(errno == EAFNOSUPPORT); ++ ++ assert_se(socket(AF_NETLINK, SOCK_DGRAM, 0) < 0); ++ assert_se(errno == EAFNOSUPPORT); ++ ++ _exit(EXIT_SUCCESS); ++ } ++ ++ assert_se(wait_for_terminate_and_warn("socketseccomp", pid, true) == EXIT_SUCCESS); ++} ++ ++static void test_restrict_realtime(void) { ++ pid_t pid; ++ ++ if (!is_seccomp_available()) ++ return; ++ if (geteuid() != 0) ++ return; ++ ++ if (detect_container() > 0) /* in containers RT privs are likely missing anyway */ ++ return; ++ ++ pid = fork(); ++ assert_se(pid >= 0); ++ ++ if (pid == 0) { ++ assert_se(sched_setscheduler(0, SCHED_FIFO, &(struct sched_param) { .sched_priority = 1 }) >= 0); ++ assert_se(sched_setscheduler(0, SCHED_RR, &(struct sched_param) { .sched_priority = 1 }) >= 0); ++ assert_se(sched_setscheduler(0, SCHED_IDLE, &(struct sched_param) { .sched_priority = 0 }) >= 0); ++ assert_se(sched_setscheduler(0, SCHED_BATCH, &(struct sched_param) { .sched_priority = 0 }) >= 0); ++ assert_se(sched_setscheduler(0, SCHED_OTHER, &(struct sched_param) {}) >= 0); ++ ++ assert_se(seccomp_restrict_realtime() >= 0); ++ ++ assert_se(sched_setscheduler(0, SCHED_IDLE, &(struct sched_param) { .sched_priority = 0 }) >= 0); ++ assert_se(sched_setscheduler(0, SCHED_BATCH, &(struct sched_param) { .sched_priority = 0 }) >= 0); ++ assert_se(sched_setscheduler(0, SCHED_OTHER, &(struct sched_param) {}) >= 0); ++ ++ assert_se(sched_setscheduler(0, SCHED_FIFO, &(struct sched_param) { .sched_priority = 1 }) < 0); ++ assert_se(errno == EPERM); ++ assert_se(sched_setscheduler(0, SCHED_RR, &(struct sched_param) { .sched_priority = 1 }) < 0); ++ assert_se(errno == EPERM); ++ ++ _exit(EXIT_SUCCESS); ++ } ++ ++ assert_se(wait_for_terminate_and_warn("realtimeseccomp", pid, true) == EXIT_SUCCESS); ++} ++ ++static void test_memory_deny_write_execute(void) { ++ pid_t pid; ++ ++ if (!is_seccomp_available()) ++ return; ++ if (geteuid() != 0) ++ return; ++ ++ pid = fork(); ++ assert_se(pid >= 0); ++ ++ if (pid == 0) { ++ void *p; ++ ++ p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1,0); ++ assert_se(p != MAP_FAILED); ++ assert_se(munmap(p, page_size()) >= 0); ++ ++ seccomp_memory_deny_write_execute(); ++ ++ p = mmap(NULL, page_size(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1,0); ++ assert_se(p == MAP_FAILED); ++ assert_se(errno == EPERM); ++ ++ p = mmap(NULL, page_size(), PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1,0); ++ assert_se(p != MAP_FAILED); ++ assert_se(munmap(p, page_size()) >= 0); ++ ++ _exit(EXIT_SUCCESS); ++ } ++ ++ assert_se(wait_for_terminate_and_warn("memoryseccomp", pid, true) == EXIT_SUCCESS); ++} ++ ++static void test_restrict_archs(void) { ++ pid_t pid; ++ ++ if (!is_seccomp_available()) ++ return; ++ if (geteuid() != 0) ++ return; ++ ++ pid = fork(); ++ assert_se(pid >= 0); ++ ++ if (pid == 0) { ++ _cleanup_set_free_ Set *s = NULL; ++ ++ assert_se(access("/", F_OK) >= 0); ++ ++ assert_se(s = set_new(NULL)); ++ ++#ifdef __x86_64__ ++ assert_se(set_put(s, UINT32_TO_PTR(SCMP_ARCH_X86+1)) >= 0); ++#endif ++ assert_se(seccomp_restrict_archs(s) >= 0); ++ ++ assert_se(access("/", F_OK) >= 0); ++ assert_se(seccomp_restrict_archs(NULL) >= 0); ++ ++ assert_se(access("/", F_OK) >= 0); ++ ++ _exit(EXIT_SUCCESS); ++ } ++ ++ assert_se(wait_for_terminate_and_warn("archseccomp", pid, true) == EXIT_SUCCESS); ++} ++ ++static void test_load_syscall_filter_set_raw(void) { ++ pid_t pid; ++ ++ if (!is_seccomp_available()) ++ return; ++ if (geteuid() != 0) ++ return; ++ ++ pid = fork(); ++ assert_se(pid >= 0); ++ ++ if (pid == 0) { ++ _cleanup_set_free_ Set *s = NULL; ++ ++ assert_se(access("/", F_OK) >= 0); ++ assert_se(poll(NULL, 0, 0) == 0); ++ ++ assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, NULL, SCMP_ACT_KILL) >= 0); ++ assert_se(access("/", F_OK) >= 0); ++ assert_se(poll(NULL, 0, 0) == 0); ++ ++ assert_se(s = set_new(NULL)); ++ assert_se(set_put(s, UINT32_TO_PTR(__NR_access + 1)) >= 0); ++ ++ assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUCLEAN)) >= 0); ++ ++ assert_se(access("/", F_OK) < 0); ++ assert_se(errno == EUCLEAN); ++ ++ assert_se(poll(NULL, 0, 0) == 0); ++ ++ s = set_free(s); ++ ++ assert_se(s = set_new(NULL)); ++ assert_se(set_put(s, UINT32_TO_PTR(__NR_poll + 1)) >= 0); ++ ++ assert_se(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, s, SCMP_ACT_ERRNO(EUNATCH)) >= 0); ++ ++ assert_se(access("/", F_OK) < 0); ++ assert_se(errno == EUCLEAN); ++ ++ assert_se(poll(NULL, 0, 0) < 0); ++ assert_se(errno == EUNATCH); ++ ++ _exit(EXIT_SUCCESS); ++ } ++ ++ assert_se(wait_for_terminate_and_warn("syscallrawseccomp", pid, true) == EXIT_SUCCESS); ++} ++ + int main(int argc, char *argv[]) { + + log_set_max_level(LOG_DEBUG); +@@ -225,6 +477,12 @@ int main(int argc, char *argv[]) { + test_syscall_filter_set_find(); + test_filter_sets(); + test_restrict_namespace(); ++ test_protect_sysctl(); ++ test_restrict_address_families(); ++ test_restrict_realtime(); ++ test_memory_deny_write_execute(); ++ test_restrict_archs(); ++ test_load_syscall_filter_set_raw(); + + return 0; + } +-- +2.9.3 + diff --git a/systemd.spec b/systemd.spec index ba4fd40..b8a4317 100644 --- a/systemd.spec +++ b/systemd.spec @@ -12,7 +12,7 @@ Name: systemd Url: http://www.freedesktop.org/wiki/Software/systemd Version: 232 -Release: 10%{?gitcommit:.git%{gitcommitshort}}%{?dist} +Release: 11%{?gitcommit:.git%{gitcommitshort}}%{?dist} # For a breakdown of the licensing, see README License: LGPLv2+ and MIT and GPLv2+ Summary: System and Service Manager @@ -39,14 +39,62 @@ Source10: systemd-udev-trigger-no-reload.conf Source11: 20-grubby.install Source12: https://raw.githubusercontent.com/systemd/systemd/1000522a60ceade446773c67031b47a566d4a70d/src/login/systemd-user.m4 +# GIT_DIR=../../src/systemd/.git git format-patch-ab -M -N v232..v232-stable +# i=1; for j in 00*patch; do printf "Patch%04d: %s\n" $i $j; i=$((i+1));done|xclip + Patch0001: 0001-build-sys-link-test-seccomp-against-seccomp-libs-456.patch Patch0002: 0002-kernel-install-use-exit-instead-of-return-4565.patch -Patch0003: 0003-kernel-install-avoid-process-substitution.patch -Patch0004: 0004-build-sys-check-for-lz4-in-the-old-and-new-numbering.patch -# Fix periodic boot fail in initrd-switch-root.service -# https://github.com/systemd/systemd/commit/acc28e2e3037d689d6481e4664925cf31d4d087b -# re-diffed on v232 -Patch0005: 0005-core-make-sure-initrd-switch-root-command-survives-P.patch +Patch0003: 0003-nspawn-fix-exit-code-for-help-and-version-4609.patch +Patch0004: 0004-core-don-t-use-the-unified-hierarchy-for-the-systemd.patch +Patch0005: 0005-core-make-RootDirectory-and-ProtectKernelModules-wor.patch +Patch0006: 0006-nspawn-avoid-one-strdup-by-using-free_and_replace.patch +Patch0007: 0007-nspawn-slight-simplification.patch +Patch0008: 0008-core-namespace-count-and-free-failed-paths-inside-ch.patch +Patch0009: 0009-basic-virt-fix-userns-check-on-CONFIG_USER_NS-n-kern.patch +Patch0010: 0010-timesyncd-clear-ADJ_MAXERROR-to-keep-STA_UNSYNC-clea.patch +Patch0011: 0011-link-fix-offload-features-initialization-4639.patch +Patch0012: 0012-sd-event-fix-sd_event_source_get_priority-4712.patch +Patch0013: 0013-build-sys-check-for-lz4-in-the-old-and-new-numbering.patch +Patch0014: 0014-networkd-fix-size-of-MTUBytes-so-that-it-does-not-ov.patch +Patch0015: 0015-core-consider-SIGTERM-as-a-clean-exit-status-for-ini.patch +Patch0016: 0016-core-make-sure-initrd-switch-root-command-survives-P.patch +Patch0017: 0017-fix-journald-startup-problem-when-code-is-compiled-w.patch +Patch0018: 0018-device-Avoid-calling-unit_free-NULL-in-device-setup-.patch +Patch0019: 0019-udevd-check-correct-return-value-of-fcntl-4758.patch +Patch0020: 0020-systemctl-fix-is-enabled-exit-status-on-failure-when.patch +Patch0021: 0021-journal-make-sure-to-initially-populate-the-space-in.patch +Patch0022: 0022-networkd-link_enter_configured-remove-assert-4800.patch +Patch0023: 0023-rules-consider-MMC-device-partitions-with-partition-.patch +Patch0024: 0024-nspawn-add-missing-E-to-getopt_long-4860.patch +Patch0025: 0025-build-sys-define-arm-as-secondary-architecture-for-a.patch +Patch0026: 0026-nspawn-when-getting-SIGCHLD-make-sure-it-s-from-the-.patch +Patch0027: 0027-machinectl-make-machinectl-E-shell-work.patch +Patch0028: 0028-sysv-generator-properly-translate-sysv-facilities.patch +Patch0029: 0029-core-downgrade-Time-has-been-changed-to-debug-4906.patch +Patch0030: 0030-machinectl-handle-EOPNOTSUPP-from-print_addresses-49.patch +Patch0031: 0031-units-fix-condition-for-systemd-journal-catalog-upda.patch +Patch0032: 0032-core-fix-sockaddr-length-calculation-for-sockaddr_pr.patch +Patch0033: 0033-shared-fix-double-free-in-unmask-5005.patch +Patch0034: 0034-shared-fix-double-free-in-link.patch +Patch0035: 0035-shared-check-strdup-NULL.patch +Patch0036: 0036-rpm-triggers-do-nothing-if-systemd-is-not-running-50.patch +Patch0037: 0037-kernel-install-avoid-process-substitution.patch +Patch0038: 0038-shell-completion-redirect-all-errors-from-systemctl-.patch +Patch0039: 0039-cryptsetup-fix-unitialized-variable.patch +Patch0040: 0040-systemctl-uninitalized-variable.patch +Patch0041: 0041-bash_completion-journalctl-add-missing-options.patch +Patch0042: 0042-bash_completion-journalctl-Complete-t-option-values.patch +Patch0043: 0043-Fixi-caching-in-zsh-completion-5122.patch +Patch0044: 0044-bash-completion-add-support-for-now-5155.patch +Patch0045: 0045-core-dbus-fix-two-strv-memleaks.patch +Patch0046: 0046-core-execute-fix-strv-memleak.patch +Patch0047: 0047-resolve-fix-strv-memleak.patch +Patch0048: 0048-nspawn-fix-clobbering-of-selinux-context-arg.patch +Patch0049: 0049-parse_hwdb-fix-to-work-with-pyparsing-2.1.10.patch +Patch0050: 0050-journald-don-t-flush-to-var-log-journal-before-we-ge.patch +Patch0051: 0051-tree-wide-drop-NULL-sentinel-from-strjoin.patch +Patch0052: 0052-core-add-new-RestrictNamespaces-unit-file-setting.patch +Patch0053: 0053-seccomp-rework-seccomp-code-to-improve-compat-with-s.patch Patch0998: 0998-resolved-create-etc-resolv.conf-symlink-at-runtime.patch @@ -966,6 +1014,12 @@ getent passwd systemd-journal-upload &>/dev/null || useradd -r -l -g systemd-jou %{_mandir}/man[1578]/systemd-journal-gateway* %changelog +* Sun Jan 29 2017 zbyszek - 232-11 +- Backport a number of patches (#1411299, #1413075, #1415745, + ##1415358, #1416588, #1408884) +- Fix various memleaks and unitialized variable access +- Shell completion enhancements + * Thu Jan 19 2017 Adam Williamson - 232-10 - Backport fix for boot failure in initrd-switch-root (#1414904)