diff --git a/.systemd.metadata b/.systemd.metadata index aef1460..f755dad 100644 --- a/.systemd.metadata +++ b/.systemd.metadata @@ -1 +1 @@ -9bad8622d0198406e6570ca7c54de0eac47e468e SOURCES/systemd-247.3.tar.gz +c7bd26b72db63a5e7eff060173b11807845c7dcb SOURCES/systemd-248.2.tar.gz diff --git a/SOURCES/13496-fb.patch b/SOURCES/13496-fb.patch new file mode 100644 index 0000000..2f16d58 --- /dev/null +++ b/SOURCES/13496-fb.patch @@ -0,0 +1,1738 @@ +From 6999572c06303057dbdc16c04c523f407aec08bd Mon Sep 17 00:00:00 2001 +From: Julia Kartseva +Date: Wed, 16 Sep 2020 15:58:04 -0700 +Subject: [PATCH 1/9] shared: add bpf-program helpers + +Add helpers to: +- Create new BPFProgram instance from a path in bpf +filesystem and bpf attach type; +- Pin a program to bpf fs; +- Get BPF program ID by BPF program FD. +--- + src/shared/bpf-program.c | 80 ++++++++++++++++++++++++++++++++++++++++ + src/shared/bpf-program.h | 5 ++- + 2 files changed, 84 insertions(+), 1 deletion(-) + +diff --git a/src/shared/bpf-program.c b/src/shared/bpf-program.c +index 10239142af..d67ada23b0 100644 +--- a/src/shared/bpf-program.c ++++ b/src/shared/bpf-program.c +@@ -12,6 +12,26 @@ + #include "missing_syscall.h" + #include "path-util.h" + ++ /* struct bpf_prog_info info must be initialized since its value is both input and output ++ * for BPF_OBJ_GET_INFO_BY_FD syscall. */ ++static int bpf_program_get_info_by_fd(int prog_fd, struct bpf_prog_info *info, uint32_t info_len) { ++ union bpf_attr attr; ++ ++ /* Explicitly memset to zero since some compilers may produce non-zero-initialized padding when ++ * structured initialization is used. ++ * Refer to https://github.com/systemd/systemd/issues/18164 ++ */ ++ zero(attr); ++ attr.info.bpf_fd = prog_fd; ++ attr.info.info_len = info_len; ++ attr.info.info = PTR_TO_UINT64(info); ++ ++ if (bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr)) < 0) ++ return -errno; ++ ++ return 0; ++} ++ + int bpf_program_new(uint32_t prog_type, BPFProgram **ret) { + _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL; + +@@ -28,6 +48,38 @@ int bpf_program_new(uint32_t prog_type, BPFProgram **ret) { + return 0; + } + ++int bpf_program_new_from_bpffs_path(const char *path, BPFProgram **ret) { ++ _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL; ++ struct bpf_prog_info info = {}; ++ int r; ++ ++ assert(path); ++ assert(ret); ++ ++ p = new(BPFProgram, 1); ++ if (!p) ++ return -ENOMEM; ++ ++ *p = (BPFProgram) { ++ .prog_type = BPF_PROG_TYPE_UNSPEC, ++ .n_ref = 1, ++ .kernel_fd = -1, ++ }; ++ ++ r = bpf_program_load_from_bpf_fs(p, path); ++ if (r < 0) ++ return r; ++ ++ r = bpf_program_get_info_by_fd(p->kernel_fd, &info, sizeof(info)); ++ if (r < 0) ++ return r; ++ ++ p->prog_type = info.type; ++ *ret = TAKE_PTR(p); ++ ++ return 0; ++} ++ + static BPFProgram *bpf_program_free(BPFProgram *p) { + assert(p); + +@@ -254,3 +306,31 @@ int bpf_map_lookup_element(int fd, const void *key, void *value) { + + return 0; + } ++ ++int bpf_program_pin(int prog_fd, const char *bpffs_path) { ++ union bpf_attr attr; ++ ++ zero(attr); ++ attr.pathname = PTR_TO_UINT64((void *) bpffs_path); ++ attr.bpf_fd = prog_fd; ++ ++ if (bpf(BPF_OBJ_PIN, &attr, sizeof(attr)) < 0) ++ return -errno; ++ ++ return 0; ++} ++ ++int bpf_program_get_id_by_fd(int prog_fd, uint32_t *ret_id) { ++ struct bpf_prog_info info = {}; ++ int r; ++ ++ assert(ret_id); ++ ++ r = bpf_program_get_info_by_fd(prog_fd, &info, sizeof(info)); ++ if (r < 0) ++ return r; ++ ++ *ret_id = info.id; ++ ++ return 0; ++}; +diff --git a/src/shared/bpf-program.h b/src/shared/bpf-program.h +index eef77f9d8e..243cef923f 100644 +--- a/src/shared/bpf-program.h ++++ b/src/shared/bpf-program.h +@@ -26,8 +26,9 @@ struct BPFProgram { + }; + + int bpf_program_new(uint32_t prog_type, BPFProgram **ret); +-BPFProgram *bpf_program_unref(BPFProgram *p); ++int bpf_program_new_from_bpffs_path(const char *path, BPFProgram **ret); + BPFProgram *bpf_program_ref(BPFProgram *p); ++BPFProgram *bpf_program_unref(BPFProgram *p); + + int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *insn, size_t count); + int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size); +@@ -35,6 +36,8 @@ int bpf_program_load_from_bpf_fs(BPFProgram *p, const char *path); + + int bpf_program_cgroup_attach(BPFProgram *p, int type, const char *path, uint32_t flags); + int bpf_program_cgroup_detach(BPFProgram *p); ++int bpf_program_pin(int prog_fd, const char *bpffs_path); ++int bpf_program_get_id_by_fd(int prog_fd, uint32_t *ret_id); + + int bpf_map_new(enum bpf_map_type type, size_t key_size, size_t value_size, size_t max_entries, uint32_t flags); + int bpf_map_update_element(int fd, const void *key, void *value); +-- +2.30.2 + + +From b04b09640c624df8f64944dead8d59faa0521d2e Mon Sep 17 00:00:00 2001 +From: Julia Kartseva +Date: Thu, 4 Feb 2021 00:02:07 -0800 +Subject: [PATCH 2/9] shared: bpf_attach_type {from,to} string + +Introduce bpf_cgroup_attach_type_table with accustomed attached type +names also used in bpftool. +Add bpf_cgroup_attach_type_{from|to}_string helpers to convert from|to +string representation of pinned bpf program, e.g. +"egress:/sys/fs/bpf/egress-hook" for +/sys/fs/bpf/egress-hook path and BPF_CGROUP_INET_EGRESS attach type. +--- + src/shared/bpf-program.c | 24 ++++++++++++++++++++++++ + src/shared/bpf-program.h | 3 +++ + 2 files changed, 27 insertions(+) + +diff --git a/src/shared/bpf-program.c b/src/shared/bpf-program.c +index d67ada23b0..a8a34521fd 100644 +--- a/src/shared/bpf-program.c ++++ b/src/shared/bpf-program.c +@@ -11,6 +11,30 @@ + #include "memory-util.h" + #include "missing_syscall.h" + #include "path-util.h" ++#include "string-table.h" ++ ++static const char *const bpf_cgroup_attach_type_table[__MAX_BPF_ATTACH_TYPE] = { ++ [BPF_CGROUP_INET_INGRESS] = "ingress", ++ [BPF_CGROUP_INET_EGRESS] = "egress", ++ [BPF_CGROUP_INET_SOCK_CREATE] = "sock_create", ++ [BPF_CGROUP_SOCK_OPS] = "sock_ops", ++ [BPF_CGROUP_DEVICE] = "device", ++ [BPF_CGROUP_INET4_BIND] = "bind4", ++ [BPF_CGROUP_INET6_BIND] = "bind6", ++ [BPF_CGROUP_INET4_CONNECT] = "connect4", ++ [BPF_CGROUP_INET6_CONNECT] = "connect6", ++ [BPF_CGROUP_INET4_POST_BIND] = "post_bind4", ++ [BPF_CGROUP_INET6_POST_BIND] = "post_bind6", ++ [BPF_CGROUP_UDP4_SENDMSG] = "sendmsg4", ++ [BPF_CGROUP_UDP6_SENDMSG] = "sendmsg6", ++ [BPF_CGROUP_SYSCTL] = "sysctl", ++ [BPF_CGROUP_UDP4_RECVMSG] = "recvmsg4", ++ [BPF_CGROUP_UDP6_RECVMSG] = "recvmsg6", ++ [BPF_CGROUP_GETSOCKOPT] = "getsockopt", ++ [BPF_CGROUP_SETSOCKOPT] = "setsockopt", ++}; ++ ++DEFINE_STRING_TABLE_LOOKUP(bpf_cgroup_attach_type, int); + + /* struct bpf_prog_info info must be initialized since its value is both input and output + * for BPF_OBJ_GET_INFO_BY_FD syscall. */ +diff --git a/src/shared/bpf-program.h b/src/shared/bpf-program.h +index 243cef923f..86fd338c93 100644 +--- a/src/shared/bpf-program.h ++++ b/src/shared/bpf-program.h +@@ -43,4 +43,7 @@ int bpf_map_new(enum bpf_map_type type, size_t key_size, size_t value_size, size + int bpf_map_update_element(int fd, const void *key, void *value); + int bpf_map_lookup_element(int fd, const void *key, void *value); + ++int bpf_cgroup_attach_type_from_string(const char *str) _pure_; ++const char *bpf_cgroup_attach_type_to_string(int attach_type) _const_; ++ + DEFINE_TRIVIAL_CLEANUP_FUNC(BPFProgram*, bpf_program_unref); +-- +2.30.2 + + +From 2ab40fe0dc0f5ec83fe73bc750e9b3f5aabf1fb9 Mon Sep 17 00:00:00 2001 +From: Julia Kartseva +Date: Mon, 1 Mar 2021 16:56:04 -0800 +Subject: [PATCH 3/9] cgroup: add foreign program to cgroup context + +- Store foreign bpf programs in cgroup context. A program is considered +foreign if it was loaded to a kernel by an entity external to systemd, +so systemd is responsible only for attach and detach paths. +- Support the case of pinned bpf programs: pinning to bpffs so a program +is kept loaded to the kernel even when program fd is closed by a user +application is a common way to extend program's lifetime. +- Aadd linked list node struct with attach type and bpffs path +fields. +--- + src/core/cgroup.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ + src/core/cgroup.h | 10 ++++++++++ + 2 files changed, 55 insertions(+) + +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index 3ed6ac09ff..a0af50518d 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -190,6 +190,15 @@ void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockI + free(b); + } + ++void cgroup_context_remove_bpf_foreign_program(CGroupContext *c, CGroupBPFForeignProgram *p) { ++ assert(c); ++ assert(p); ++ ++ LIST_REMOVE(programs, c->bpf_foreign_programs, p); ++ free(p->bpffs_path); ++ free(p); ++} ++ + void cgroup_context_done(CGroupContext *c) { + assert(c); + +@@ -217,6 +226,9 @@ void cgroup_context_done(CGroupContext *c) { + c->ip_filters_ingress = strv_free(c->ip_filters_ingress); + c->ip_filters_egress = strv_free(c->ip_filters_egress); + ++ while (c->bpf_foreign_programs) ++ cgroup_context_remove_bpf_foreign_program(c, c->bpf_foreign_programs); ++ + cpu_set_reset(&c->cpuset_cpus); + cpu_set_reset(&c->cpuset_mems); + } +@@ -360,6 +372,7 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) { + CGroupIODeviceLatency *l; + CGroupBlockIODeviceBandwidth *b; + CGroupBlockIODeviceWeight *w; ++ CGroupBPFForeignProgram *p; + CGroupDeviceAllow *a; + CGroupContext *c; + IPAddressAccessItem *iaai; +@@ -544,6 +557,10 @@ void cgroup_context_dump(Unit *u, FILE* f, const char *prefix) { + + STRV_FOREACH(path, c->ip_filters_egress) + fprintf(f, "%sIPEgressFilterPath: %s\n", prefix, *path); ++ ++ LIST_FOREACH(programs, p, c->bpf_foreign_programs) ++ fprintf(f, "%sBPFProgram: %s:%s", ++ prefix, bpf_cgroup_attach_type_to_string(p->attach_type), p->bpffs_path); + } + + int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode) { +@@ -575,6 +592,34 @@ int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode) + return 0; + } + ++int cgroup_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const char *bpffs_path) { ++ CGroupBPFForeignProgram *p; ++ _cleanup_free_ char *d = NULL; ++ ++ assert(c); ++ assert(bpffs_path); ++ ++ if (!path_is_normalized(bpffs_path) || !path_is_absolute(bpffs_path)) ++ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path is not normalized: %m"); ++ ++ d = strdup(bpffs_path); ++ if (!d) ++ return log_oom(); ++ ++ p = new(CGroupBPFForeignProgram, 1); ++ if (!p) ++ return log_oom(); ++ ++ *p = (CGroupBPFForeignProgram) { ++ .attach_type = attach_type, ++ .bpffs_path = TAKE_PTR(d), ++ }; ++ ++ LIST_PREPEND(programs, c->bpf_foreign_programs, TAKE_PTR(p)); ++ ++ return 0; ++} ++ + #define UNIT_DEFINE_ANCESTOR_MEMORY_LOOKUP(entry) \ + uint64_t unit_get_ancestor_##entry(Unit *u) { \ + CGroupContext *c; \ +diff --git a/src/core/cgroup.h b/src/core/cgroup.h +index fa79ba1523..be3060eba7 100644 +--- a/src/core/cgroup.h ++++ b/src/core/cgroup.h +@@ -31,6 +31,7 @@ typedef struct CGroupIODeviceLimit CGroupIODeviceLimit; + typedef struct CGroupIODeviceLatency CGroupIODeviceLatency; + typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight; + typedef struct CGroupBlockIODeviceBandwidth CGroupBlockIODeviceBandwidth; ++typedef struct CGroupBPFForeignProgram CGroupBPFForeignProgram; + + typedef enum CGroupDevicePolicy { + /* When devices listed, will allow those, plus built-in ones, if none are listed will allow +@@ -94,6 +95,12 @@ struct CGroupBlockIODeviceBandwidth { + uint64_t wbps; + }; + ++struct CGroupBPFForeignProgram { ++ LIST_FIELDS(CGroupBPFForeignProgram, programs); ++ uint32_t attach_type; ++ char *bpffs_path; ++}; ++ + struct CGroupContext { + bool cpu_accounting; + bool io_accounting; +@@ -142,6 +149,7 @@ struct CGroupContext { + + char **ip_filters_ingress; + char **ip_filters_egress; ++ LIST_HEAD(CGroupBPFForeignProgram, bpf_foreign_programs); + + /* For legacy hierarchies */ + uint64_t cpu_shares; +@@ -202,8 +210,10 @@ void cgroup_context_free_io_device_limit(CGroupContext *c, CGroupIODeviceLimit * + void cgroup_context_free_io_device_latency(CGroupContext *c, CGroupIODeviceLatency *l); + void cgroup_context_free_blockio_device_weight(CGroupContext *c, CGroupBlockIODeviceWeight *w); + void cgroup_context_free_blockio_device_bandwidth(CGroupContext *c, CGroupBlockIODeviceBandwidth *b); ++void cgroup_context_remove_bpf_foreign_program(CGroupContext *c, CGroupBPFForeignProgram *p); + + int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode); ++int cgroup_add_bpf_foreign_program(CGroupContext *c, uint32_t attach_type, const char *path); + + void cgroup_oomd_xattr_apply(Unit *u, const char *cgroup_path); + +-- +2.30.2 + + +From 42b3c33e312d70fec24b5daa455ab4abdcab81b4 Mon Sep 17 00:00:00 2001 +From: Julia Kartseva +Date: Wed, 16 Sep 2020 15:58:04 -0700 +Subject: [PATCH 4/9] core: add bpf-foreign unit helpers + +- Introduce support of cgroup-bpf programs managed (i.e. compiled, +loaded to and unloaded from kernel) externally. Systemd is only +responsible for attaching programs to unit cgroup hence the name +'foreign'. + +Foreign BPF programs are identified by bpf program ID and attach type. + +systemd: +- Gets kernel FD of BPF program; +- Makes a unique identifier of BPF program from BPF attach type and +program ID. Same program IDs mean the same program, i.e the same +chunk of kernel memory. Even if the same program is passed multiple +times, identical (program_id, attach_type) instances are collapsed +into one; +- Attaches programs to unit cgroup. +--- + src/core/bpf-foreign.c | 151 +++++++++++++++++++++++++++++++++++++++++ + src/core/bpf-foreign.h | 12 ++++ + src/core/meson.build | 2 + + src/core/unit.c | 3 + + src/core/unit.h | 4 ++ + 5 files changed, 172 insertions(+) + create mode 100644 src/core/bpf-foreign.c + create mode 100644 src/core/bpf-foreign.h + +diff --git a/src/core/bpf-foreign.c b/src/core/bpf-foreign.c +new file mode 100644 +index 0000000000..98655bda3c +--- /dev/null ++++ b/src/core/bpf-foreign.c +@@ -0,0 +1,151 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include "bpf-foreign.h" ++#include "bpf-program.h" ++#include "cgroup.h" ++#include "memory-util.h" ++#include "mountpoint-util.h" ++#include "set.h" ++ ++typedef struct BPFForeignKey BPFForeignKey; ++struct BPFForeignKey { ++ uint32_t prog_id; ++ uint32_t attach_type; ++}; ++ ++static int bpf_foreign_key_new(uint32_t prog_id, ++ enum bpf_attach_type attach_type, ++ BPFForeignKey **ret) { ++ _cleanup_free_ BPFForeignKey *p = NULL; ++ ++ assert(ret); ++ ++ p = new(BPFForeignKey, 1); ++ if (!p) ++ return log_oom(); ++ ++ *p = (BPFForeignKey) { ++ .prog_id = prog_id, ++ .attach_type = attach_type, ++ }; ++ ++ *ret = TAKE_PTR(p); ++ ++ return 0; ++} ++ ++static int bpf_foreign_key_compare_func(const BPFForeignKey *a, const BPFForeignKey *b) { ++ int r = CMP(a->prog_id, b->prog_id); ++ if (r != 0) ++ return r; ++ ++ return CMP(a->attach_type, b->attach_type); ++} ++ ++static void bpf_foreign_key_hash_func(const BPFForeignKey *p, struct siphash *h) { ++ siphash24_compress(&p->prog_id, sizeof(p->prog_id), h); ++ siphash24_compress(&p->attach_type, sizeof(p->attach_type), h); ++} ++ ++DEFINE_PRIVATE_HASH_OPS_FULL(bpf_foreign_by_key_hash_ops, ++ BPFForeignKey, bpf_foreign_key_hash_func, bpf_foreign_key_compare_func, free, ++ BPFProgram, bpf_program_unref); ++ ++static int attach_programs(Unit *u, const char *path, Hashmap* foreign_by_key, uint32_t attach_flags) { ++ const BPFForeignKey *key; ++ BPFProgram *prog; ++ int r; ++ ++ assert(u); ++ ++ HASHMAP_FOREACH_KEY(prog, key, foreign_by_key) { ++ r = bpf_program_cgroup_attach(prog, key->attach_type, path, attach_flags); ++ if (r < 0) ++ return log_unit_error_errno(u, r, "Attaching foreign BPF program to cgroup %s failed: %m", path); ++ } ++ ++ return 0; ++} ++ ++/* ++ * Prepare foreign BPF program for installation: ++ * - Load the program from BPF filesystem to the kernel; ++ * - Store program FD identified by program ID and attach type in the unit. ++ */ ++static int bpf_foreign_prepare( ++ Unit *u, ++ enum bpf_attach_type attach_type, ++ const char *bpffs_path) { ++ _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL; ++ _cleanup_free_ BPFForeignKey *key = NULL; ++ uint32_t prog_id; ++ int r; ++ ++ assert(u); ++ assert(bpffs_path); ++ ++ r = bpf_program_new_from_bpffs_path(bpffs_path, &prog); ++ if (r < 0) ++ return log_unit_error_errno(u, r, "Failed to create foreign BPFProgram: %m"); ++ ++ r = bpf_program_get_id_by_fd(prog->kernel_fd, &prog_id); ++ if (r < 0) ++ return log_unit_error_errno(u, r, "Failed to get BPF program id by fd: %m"); ++ ++ r = bpf_foreign_key_new(prog_id, attach_type, &key); ++ if (r < 0) ++ return log_unit_error_errno(u, r, ++ "Failed to create foreign BPF program key from path '%s': %m", bpffs_path); ++ ++ r = hashmap_ensure_put(&u->bpf_foreign_by_key, &bpf_foreign_by_key_hash_ops, key, prog); ++ if (r == -EEXIST) { ++ log_unit_warning_errno(u, r, "Foreign BPF program already exists, ignoring: %m"); ++ return 0; ++ } ++ if (r < 0) ++ return log_unit_error_errno(u, r, "Failed to put foreign BPFProgram into map: %m"); ++ ++ TAKE_PTR(key); ++ TAKE_PTR(prog); ++ ++ return 0; ++} ++ ++int bpf_foreign_supported(void) { ++ int r; ++ ++ r = cg_all_unified(); ++ if (r <= 0) ++ return r; ++ ++ return path_is_mount_point("/sys/fs/bpf", NULL, 0); ++} ++ ++int bpf_foreign_install(Unit *u) { ++ _cleanup_free_ char *cgroup_path = NULL; ++ CGroupBPFForeignProgram *p; ++ CGroupContext *cc; ++ int r; ++ ++ assert(u); ++ ++ cc = unit_get_cgroup_context(u); ++ if (!cc) ++ return 0; ++ ++ r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path); ++ if (r < 0) ++ return log_unit_error_errno(u, r, "Failed to get cgroup path: %m"); ++ ++ LIST_FOREACH(programs, p, cc->bpf_foreign_programs) { ++ r = bpf_foreign_prepare(u, p->attach_type, p->bpffs_path); ++ if (r < 0) ++ return log_unit_error_errno(u, r, "Failed to prepare foreign BPF hashmap: %m"); ++ } ++ ++ r = attach_programs(u, cgroup_path, u->bpf_foreign_by_key, BPF_F_ALLOW_MULTI); ++ if (r < 0) ++ return log_unit_error_errno(u, r, "Failed to install foreign BPF programs: %m"); ++ ++ return 0; ++} +diff --git a/src/core/bpf-foreign.h b/src/core/bpf-foreign.h +new file mode 100644 +index 0000000000..7704986e3e +--- /dev/null ++++ b/src/core/bpf-foreign.h +@@ -0,0 +1,12 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#pragma once ++ ++#include "unit.h" ++ ++int bpf_foreign_supported(void); ++/* ++ * Attach cgroup-bpf programs foreign to systemd, i.e. loaded to the kernel by an entity ++ * external to systemd. ++ */ ++int bpf_foreign_install(Unit *u); +diff --git a/src/core/meson.build b/src/core/meson.build +index a389c906b3..a1294f3a72 100644 +--- a/src/core/meson.build ++++ b/src/core/meson.build +@@ -11,6 +11,8 @@ libcore_sources = ''' + bpf-devices.h + bpf-firewall.c + bpf-firewall.h ++ bpf-foreign.c ++ bpf-foreign.h + cgroup.c + cgroup.h + core-varlink.c +diff --git a/src/core/unit.c b/src/core/unit.c +index c212f1043d..b7141b29af 100644 +--- a/src/core/unit.c ++++ b/src/core/unit.c +@@ -11,6 +11,7 @@ + #include "all-units.h" + #include "alloc-util.h" + #include "bpf-firewall.h" ++#include "bpf-foreign.h" + #include "bus-common-errors.h" + #include "bus-util.h" + #include "cgroup-setup.h" +@@ -723,6 +724,8 @@ Unit* unit_free(Unit *u) { + set_free(u->ip_bpf_custom_ingress_installed); + set_free(u->ip_bpf_custom_egress_installed); + ++ hashmap_free(u->bpf_foreign_by_key); ++ + bpf_program_unref(u->bpf_device_control_installed); + + condition_free_list(u->conditions); +diff --git a/src/core/unit.h b/src/core/unit.h +index 264431d04d..6de529af92 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -305,6 +305,10 @@ typedef struct Unit { + Set *ip_bpf_custom_egress; + Set *ip_bpf_custom_egress_installed; + ++ /* BPF programs managed (e.g. loaded to kernel) by an entity external to systemd, ++ * attached to unit cgroup by provided program fd and attach type. */ ++ Hashmap *bpf_foreign_by_key; ++ + uint64_t ip_accounting_extra[_CGROUP_IP_ACCOUNTING_METRIC_MAX]; + + /* Low-priority event source which is used to remove watched PIDs that have gone away, and subscribe to any new +-- +2.30.2 + + +From 4d84c1af52683f7272e021fb4c6e68148d64e33b Mon Sep 17 00:00:00 2001 +From: Julia Kartseva +Date: Thu, 4 Feb 2021 00:03:08 -0800 +Subject: [PATCH 5/9] core: add bpf-foreign cgroup mask and harness + +Add CGROUP_MASK_BPF_FOREIGN to CGROUP_MASK_BPF and standard cgroup +context harness. +--- + src/basic/cgroup-util.c | 1 + + src/basic/cgroup-util.h | 4 +++- + src/core/cgroup.c | 29 +++++++++++++++++++++++++++++ + src/test/test-cgroup-mask.c | 2 +- + 4 files changed, 34 insertions(+), 2 deletions(-) + +diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c +index 8dd3f8cd95..743fb0afe1 100644 +--- a/src/basic/cgroup-util.c ++++ b/src/basic/cgroup-util.c +@@ -2162,6 +2162,7 @@ static const char *const cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = { + [CGROUP_CONTROLLER_PIDS] = "pids", + [CGROUP_CONTROLLER_BPF_FIREWALL] = "bpf-firewall", + [CGROUP_CONTROLLER_BPF_DEVICES] = "bpf-devices", ++ [CGROUP_CONTROLLER_BPF_FOREIGN] = "bpf-foreign", + }; + + DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController); +diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h +index f79e384147..8894fd9b0a 100644 +--- a/src/basic/cgroup-util.h ++++ b/src/basic/cgroup-util.h +@@ -30,6 +30,7 @@ typedef enum CGroupController { + /* BPF-based pseudo-controllers, v2 only */ + CGROUP_CONTROLLER_BPF_FIREWALL, + CGROUP_CONTROLLER_BPF_DEVICES, ++ CGROUP_CONTROLLER_BPF_FOREIGN, + + _CGROUP_CONTROLLER_MAX, + _CGROUP_CONTROLLER_INVALID = -EINVAL, +@@ -49,6 +50,7 @@ typedef enum CGroupMask { + CGROUP_MASK_PIDS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_PIDS), + CGROUP_MASK_BPF_FIREWALL = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FIREWALL), + CGROUP_MASK_BPF_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_DEVICES), ++ CGROUP_MASK_BPF_FOREIGN = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BPF_FOREIGN), + + /* All real cgroup v1 controllers */ + CGROUP_MASK_V1 = CGROUP_MASK_CPU|CGROUP_MASK_CPUACCT|CGROUP_MASK_BLKIO|CGROUP_MASK_MEMORY|CGROUP_MASK_DEVICES|CGROUP_MASK_PIDS, +@@ -57,7 +59,7 @@ typedef enum CGroupMask { + CGROUP_MASK_V2 = CGROUP_MASK_CPU|CGROUP_MASK_CPUSET|CGROUP_MASK_IO|CGROUP_MASK_MEMORY|CGROUP_MASK_PIDS, + + /* All cgroup v2 BPF pseudo-controllers */ +- CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES, ++ CGROUP_MASK_BPF = CGROUP_MASK_BPF_FIREWALL|CGROUP_MASK_BPF_DEVICES|CGROUP_MASK_BPF_FOREIGN, + + _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1 + } CGroupMask; +diff --git a/src/core/cgroup.c b/src/core/cgroup.c +index a0af50518d..f3d5d89339 100644 +--- a/src/core/cgroup.c ++++ b/src/core/cgroup.c +@@ -8,6 +8,7 @@ + #include "blockdev-util.h" + #include "bpf-devices.h" + #include "bpf-firewall.h" ++#include "bpf-foreign.h" + #include "btrfs-util.h" + #include "bus-error.h" + #include "cgroup-setup.h" +@@ -1160,6 +1161,12 @@ static void set_io_weight(Unit *u, const char *controller, uint64_t weight) { + (void) set_attribute_and_warn(u, controller, p, buf); + } + ++static void cgroup_apply_bpf_foreign_program(Unit *u) { ++ assert(u); ++ ++ (void) bpf_foreign_install(u); ++} ++ + static void cgroup_context_apply( + Unit *u, + CGroupMask apply_mask, +@@ -1473,6 +1480,9 @@ static void cgroup_context_apply( + + if (apply_mask & CGROUP_MASK_BPF_FIREWALL) + cgroup_apply_firewall(u); ++ ++ if (apply_mask & CGROUP_MASK_BPF_FOREIGN) ++ cgroup_apply_bpf_foreign_program(u); + } + + static bool unit_get_needs_bpf_firewall(Unit *u) { +@@ -1505,6 +1515,17 @@ static bool unit_get_needs_bpf_firewall(Unit *u) { + return false; + } + ++static bool unit_get_needs_bpf_foreign_program(Unit *u) { ++ CGroupContext *c; ++ assert(u); ++ ++ c = unit_get_cgroup_context(u); ++ if (!c) ++ return false; ++ ++ return !LIST_IS_EMPTY(c->bpf_foreign_programs); ++} ++ + static CGroupMask unit_get_cgroup_mask(Unit *u) { + CGroupMask mask = 0; + CGroupContext *c; +@@ -1556,6 +1577,9 @@ static CGroupMask unit_get_bpf_mask(Unit *u) { + if (unit_get_needs_bpf_firewall(u)) + mask |= CGROUP_MASK_BPF_FIREWALL; + ++ if (unit_get_needs_bpf_foreign_program(u)) ++ mask |= CGROUP_MASK_BPF_FOREIGN; ++ + return mask; + } + +@@ -3032,6 +3056,11 @@ static int cg_bpf_mask_supported(CGroupMask *ret) { + if (r > 0) + mask |= CGROUP_MASK_BPF_DEVICES; + ++ /* BPF pinned prog */ ++ r = bpf_foreign_supported(); ++ if (r > 0) ++ mask |= CGROUP_MASK_BPF_FOREIGN; ++ + *ret = mask; + return 0; + } +diff --git a/src/test/test-cgroup-mask.c b/src/test/test-cgroup-mask.c +index b53e327c63..d721946f71 100644 +--- a/src/test/test-cgroup-mask.c ++++ b/src/test/test-cgroup-mask.c +@@ -140,7 +140,7 @@ static void test_cg_mask_to_string_one(CGroupMask mask, const char *t) { + + static void test_cg_mask_to_string(void) { + test_cg_mask_to_string_one(0, NULL); +- test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct cpuset io blkio memory devices pids bpf-firewall bpf-devices"); ++ test_cg_mask_to_string_one(_CGROUP_MASK_ALL, "cpu cpuacct cpuset io blkio memory devices pids bpf-firewall bpf-devices bpf-foreign"); + test_cg_mask_to_string_one(CGROUP_MASK_CPU, "cpu"); + test_cg_mask_to_string_one(CGROUP_MASK_CPUACCT, "cpuacct"); + test_cg_mask_to_string_one(CGROUP_MASK_CPUSET, "cpuset"); +-- +2.30.2 + + +From d1f0204f1ca7bf7b562a2fe1c90decbb5e04cf31 Mon Sep 17 00:00:00 2001 +From: Julia Kartseva +Date: Thu, 4 Feb 2021 00:04:19 -0800 +Subject: [PATCH 6/9] core: add bpf-foreign to fragment parser + +- Parse a string for bpf attach type +- Simplify bpffs path +- Add foreign bpf program to cgroup context +--- + src/core/load-fragment-gperf.gperf.m4 | 3 +- + src/core/load-fragment.c | 59 +++++++++++++++++++++++++++ + src/core/load-fragment.h | 1 + + 3 files changed, 62 insertions(+), 1 deletion(-) + +diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 +index 21bbcffe41..bbb79a12ca 100644 +--- a/src/core/load-fragment-gperf.gperf.m4 ++++ b/src/core/load-fragment-gperf.gperf.m4 +@@ -234,7 +234,8 @@ $1.ManagedOOMSwap, config_parse_managed_oom_mode, + $1.ManagedOOMMemoryPressure, config_parse_managed_oom_mode, 0, offsetof($1, cgroup_context.moom_mem_pressure) + $1.ManagedOOMMemoryPressureLimit, config_parse_managed_oom_mem_pressure_limit, 0, offsetof($1, cgroup_context.moom_mem_pressure_limit) + $1.ManagedOOMPreference, config_parse_managed_oom_preference, 0, offsetof($1, cgroup_context.moom_preference) +-$1.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0' ++$1.NetClass, config_parse_warn_compat, DISABLED_LEGACY, 0 ++$1.BPFProgram, config_parse_bpf_foreign_program, 0, offsetof($1, cgroup_context)' + )m4_dnl + Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description) + Unit.Documentation, config_parse_documentation, 0, offsetof(Unit, documentation) +diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c +index 6da623dbda..fb80acbc02 100644 +--- a/src/core/load-fragment.c ++++ b/src/core/load-fragment.c +@@ -19,6 +19,7 @@ + #include "alloc-util.h" + #include "all-units.h" + #include "bpf-firewall.h" ++#include "bpf-program.h" + #include "bus-error.h" + #include "bus-internal.h" + #include "bus-util.h" +@@ -5580,6 +5581,64 @@ int config_parse_ip_filter_bpf_progs( + return 0; + } + ++int config_parse_bpf_foreign_program( ++ 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) { ++ _cleanup_free_ char *resolved = NULL, *word = NULL; ++ CGroupContext *c = data; ++ Unit *u = userdata; ++ int attach_type, r; ++ ++ assert(filename); ++ assert(lvalue); ++ assert(rvalue); ++ ++ if (isempty(rvalue)) { ++ while (c->bpf_foreign_programs) ++ cgroup_context_remove_bpf_foreign_program(c, c->bpf_foreign_programs); ++ ++ return 0; ++ } ++ ++ r = extract_first_word(&rvalue, &word, ":", 0); ++ if (r == -ENOMEM) ++ return log_oom(); ++ if (r < 0) { ++ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse foreign BPF program, ignoring: %s", rvalue); ++ return 0; ++ } ++ ++ attach_type = bpf_cgroup_attach_type_from_string(word); ++ if (attach_type < 0) { ++ log_syntax(unit, LOG_WARNING, filename, line, 0, "Unknown BPF attach type=%s, ignoring: %s", word, rvalue); ++ return 0; ++ } ++ ++ r = unit_full_printf(u, rvalue, &resolved); ++ if (r < 0) { ++ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue); ++ return 0; ++ } ++ ++ r = path_simplify_and_warn(resolved, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue); ++ if (r < 0) ++ return 0; ++ ++ r = cgroup_add_bpf_foreign_program(c, attach_type, resolved); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add foreign BPF program to cgroup context: %m"); ++ ++ return 0; ++} ++ + static int merge_by_names(Unit **u, Set *names, const char *id) { + char *k; + int r; +diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h +index b8a6d5fead..eebeda5747 100644 +--- a/src/core/load-fragment.h ++++ b/src/core/load-fragment.h +@@ -139,6 +139,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_swap_priority); + CONFIG_PARSER_PROTOTYPE(config_parse_mount_images); + CONFIG_PARSER_PROTOTYPE(config_parse_socket_timestamping); + CONFIG_PARSER_PROTOTYPE(config_parse_extension_images); ++CONFIG_PARSER_PROTOTYPE(config_parse_bpf_foreign_program); + + /* gperf prototypes */ + const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length); +-- +2.30.2 + + +From 75d16dfd0b2fc813534081d3bdf1a957360078e5 Mon Sep 17 00:00:00 2001 +From: Julia Kartseva +Date: Tue, 3 Sep 2019 19:08:13 -0700 +Subject: [PATCH 7/9] tests: add unit file tests for BPFProgram= + +- Pin trivial bpf programs to bpf filesystem, compose BPFProgram= option +string and pass it to a unit. Programs store `0` in r0 BPF register for +denying action, e.g. drop a packet. +- Load trivial BPF programs +- Test is skipped if not run under root or if can not lock enough +memory. +- For egress and ingress hooks, test BPFProgram= option along with +with IP{Egress|Ingress}FilterPath=, expected result should not depend on +which rule is executed first. +Expected results for BPF_CGROUP_INET_INGRESS: +5 packets transmitted, 0 received, 100% packet loss, time 89ms + +For BPF_CGROUP_INET_SOCK_CREATE: +ping: socket: Operation not permitted +--- + src/test/meson.build | 6 + + src/test/test-bpf-foreign-programs.c | 332 +++++++++++++++++++++++++++ + 2 files changed, 338 insertions(+) + create mode 100644 src/test/test-bpf-foreign-programs.c + +diff --git a/src/test/meson.build b/src/test/meson.build +index c752e995f6..6349034aeb 100644 +--- a/src/test/meson.build ++++ b/src/test/meson.build +@@ -335,6 +335,12 @@ tests += [ + libblkid], + core_includes], + ++ [['src/test/test-bpf-foreign-programs.c'], ++ [libcore, ++ libshared], ++ [], ++ core_includes], ++ + [['src/test/test-watch-pid.c'], + [libcore, + libshared], +diff --git a/src/test/test-bpf-foreign-programs.c b/src/test/test-bpf-foreign-programs.c +new file mode 100644 +index 0000000000..e703924077 +--- /dev/null ++++ b/src/test/test-bpf-foreign-programs.c +@@ -0,0 +1,332 @@ ++/* SPDX-License-Identifier: LGPL-2.1+ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "bpf-foreign.h" ++#include "load-fragment.h" ++#include "manager.h" ++#include "process-util.h" ++#include "rlimit-util.h" ++#include "rm-rf.h" ++#include "service.h" ++#include "tests.h" ++#include "unit.h" ++#include "virt.h" ++ ++struct Test { ++ const char *option_name; ++ enum bpf_prog_type prog_type; ++ enum bpf_attach_type attach_type; ++ const char *bpffs_path; ++}; ++ ++typedef struct Test Test; ++ ++#define BPFFS_PATH(prog_suffix) ("/sys/fs/bpf/test-bpf-foreing-" # prog_suffix) ++static const Test single_prog[] = { ++ { ++ .option_name = "BPFProgram", ++ .prog_type = BPF_PROG_TYPE_CGROUP_SKB, ++ .attach_type = BPF_CGROUP_INET_INGRESS, ++ .bpffs_path = BPFFS_PATH("trivial-skb"), ++ }, ++}; ++static const Test path_split_test[] = { ++ { ++ .option_name = "BPFProgram", ++ .prog_type = BPF_PROG_TYPE_CGROUP_SKB, ++ .attach_type = BPF_CGROUP_INET_INGRESS, ++ .bpffs_path = BPFFS_PATH("path:split:test"), ++ }, ++}; ++ ++static const Test same_prog_same_hook[] = { ++ { ++ .option_name = "BPFProgram", ++ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK, ++ .attach_type = BPF_CGROUP_INET_SOCK_CREATE, ++ .bpffs_path = BPFFS_PATH("trivial-sock"), ++ }, ++ { ++ .option_name = "BPFProgram", ++ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK, ++ .attach_type = BPF_CGROUP_INET_SOCK_CREATE, ++ .bpffs_path = BPFFS_PATH("trivial-sock"), ++ } ++}; ++ ++static const Test multi_prog_same_hook[] = { ++ { ++ .option_name = "BPFProgram", ++ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK, ++ .attach_type = BPF_CGROUP_INET_SOCK_CREATE, ++ .bpffs_path = BPFFS_PATH("trivial-sock-0"), ++ }, ++ { ++ .option_name = "BPFProgram", ++ .prog_type = BPF_PROG_TYPE_CGROUP_SOCK, ++ .attach_type = BPF_CGROUP_INET_SOCK_CREATE, ++ .bpffs_path = BPFFS_PATH("trivial-sock-1"), ++ } ++}; ++ ++static const Test same_prog_multi_hook[] = { ++ { ++ .option_name = "BPFProgram", ++ .prog_type = BPF_PROG_TYPE_CGROUP_SKB, ++ .attach_type = BPF_CGROUP_INET_INGRESS, ++ .bpffs_path = BPFFS_PATH("trivial-skb"), ++ }, ++ { ++ .option_name = "BPFProgram", ++ .prog_type = BPF_PROG_TYPE_CGROUP_SKB, ++ .attach_type = BPF_CGROUP_INET_EGRESS, ++ .bpffs_path = BPFFS_PATH("trivial-skb"), ++ } ++}; ++ ++static const Test same_prog_multi_option_0[] = { ++ { ++ .option_name = "BPFProgram", ++ .prog_type = BPF_PROG_TYPE_CGROUP_SKB, ++ .attach_type = BPF_CGROUP_INET_INGRESS, ++ .bpffs_path = BPFFS_PATH("trivial-skb"), ++ }, ++ { ++ .option_name = "IPIngressFilterPath", ++ .prog_type = BPF_PROG_TYPE_CGROUP_SKB, ++ .attach_type = BPF_CGROUP_INET_INGRESS, ++ .bpffs_path = BPFFS_PATH("trivial-skb"), ++ } ++}; ++ ++static const Test same_prog_multi_option_1[] = { ++ { ++ .option_name = "IPEgressFilterPath", ++ .prog_type = BPF_PROG_TYPE_CGROUP_SKB, ++ .attach_type = BPF_CGROUP_INET_EGRESS, ++ .bpffs_path = BPFFS_PATH("trivial-skb"), ++ }, ++ { ++ .option_name = "BPFProgram", ++ .prog_type = BPF_PROG_TYPE_CGROUP_SKB, ++ .attach_type = BPF_CGROUP_INET_EGRESS, ++ .bpffs_path = BPFFS_PATH("trivial-skb"), ++ } ++}; ++#undef BPFFS_PATH ++ ++static int bpf_foreign_test_to_string(enum bpf_attach_type attach_type, const char *bpffs_path, char **ret_str) { ++ const char *s = NULL; ++ ++ assert_se(bpffs_path); ++ assert_se(ret_str); ++ ++ assert_se(s = bpf_cgroup_attach_type_to_string(attach_type)); ++ assert_se(*ret_str = strjoin(s, ":", bpffs_path)); ++ ++ return 0; ++} ++ ++static char **unlink_paths_and_free(char **paths) { ++ char **i; ++ ++ STRV_FOREACH(i, paths) ++ (void) unlink(*i); ++ ++ return strv_free(paths); ++} ++ ++DEFINE_TRIVIAL_CLEANUP_FUNC(char **, unlink_paths_and_free); ++ ++static int pin_programs(Unit *u, CGroupContext *cc, const Test *test_suite, size_t test_suite_size, char ***paths_ret) { ++ _cleanup_(unlink_paths_and_freep) char **bpffs_paths = NULL; ++ static const struct bpf_insn trivial[] = { ++ BPF_MOV64_IMM(BPF_REG_0, 0), ++ BPF_EXIT_INSN() ++ }; ++ char log_buf[0xffff]; ++ int r; ++ ++ assert_se(paths_ret); ++ ++ for (size_t i = 0; i < test_suite_size; i++) { ++ _cleanup_(bpf_program_unrefp) BPFProgram *prog = NULL; ++ _cleanup_free_ char *str = NULL; ++ ++ r = bpf_foreign_test_to_string(test_suite[i].attach_type, test_suite[i].bpffs_path, &str); ++ if (r < 0) ++ return log_error_errno(r, "Failed to convert program to string"); ++ ++ r = bpf_program_new(test_suite[i].prog_type, &prog); ++ if (r < 0) ++ return log_error_errno(r, "Failed to create program '%s'", str); ++ ++ r = bpf_program_add_instructions(prog, trivial, ELEMENTSOF(trivial)); ++ if (r < 0) ++ return log_error_errno(r, "Failed to add trivial instructions for '%s'", str); ++ ++ r = bpf_program_load_kernel(prog, log_buf, ELEMENTSOF(log_buf)); ++ if (r < 0) ++ return log_error_errno(r, "Failed to load BPF program '%s'", str); ++ ++ if (strv_contains(bpffs_paths, test_suite[i].bpffs_path)) ++ continue; ++ ++ r = strv_extend(&bpffs_paths, test_suite[i].bpffs_path); ++ if (r < 0) ++ return log_error_errno(r, "Failed to put path into a vector: %m"); ++ ++ r = bpf_program_pin(prog->kernel_fd, test_suite[i].bpffs_path); ++ if (r < 0) ++ return log_error_errno(r, "Failed to pin BPF program '%s'", str); ++ } ++ ++ *paths_ret = TAKE_PTR(bpffs_paths); ++ return 0; ++} ++ ++static int test_bpf_cgroup_programs(Manager *m, const char *unit_name, const Test *test_suite, size_t test_suite_size) { ++ _cleanup_(unlink_paths_and_freep) char **bpffs_paths = NULL; ++ _cleanup_(unit_freep) Unit *u = NULL; ++ CGroupContext *cc = NULL; ++ int cld_code, r; ++ ++ assert_se(u = unit_new(m, sizeof(Service))); ++ assert_se(unit_add_name(u, unit_name) == 0); ++ assert_se(cc = unit_get_cgroup_context(u)); ++ ++ r = pin_programs(u, cc, test_suite, test_suite_size, &bpffs_paths); ++ if (r < 0) ++ return log_error_errno(r, "Failed to pin programs: %m"); ++ ++ for (size_t i = 0; i < test_suite_size; i++) { ++ if (streq(test_suite[i].option_name, "BPFProgram")) { ++ _cleanup_free_ char *option = NULL; ++ r = bpf_foreign_test_to_string(test_suite[i].attach_type, test_suite[i].bpffs_path, &option); ++ if (r < 0) ++ return log_error_errno(r, "Failed to compose option string: %m"); ++ r = config_parse_bpf_foreign_program( ++ u->id, "filename", 1, "Service", 1, test_suite[i].option_name, 0, option, cc, u); ++ ++ if (r < 0) ++ return log_error_errno(r, "Failed to parse option string '%s': %m", option); ++ } else if (STR_IN_SET(test_suite[i].option_name, "IPIngressFilterPath", "IPEgressFilterPath")) { ++ const char *option = test_suite[i].bpffs_path; ++ void *paths = NULL; ++ ++ if (streq(test_suite[i].option_name, "IPIngressFilterPath")) ++ paths = &cc->ip_filters_ingress; ++ else ++ paths = &cc->ip_filters_egress; ++ ++ r = config_parse_ip_filter_bpf_progs( ++ u->id, "filename", 1, "Service", 1, test_suite[i].option_name, 0, option, paths, u); ++ if (r < 0) ++ return log_error_errno(r, "Failed to parse option string '%s': %m", option); ++ } ++ } ++ ++ r = config_parse_exec( ++ u->id, ++ "filename", ++ 1, ++ "Service", ++ 1, ++ "ExecStart", ++ SERVICE_EXEC_START, ++ "-/bin/ping -c 5 127.0.0.1 -W 1", ++ SERVICE(u)->exec_command, ++ u); ++ if (r < 0) ++ return log_error_errno(r, "Failed to parse ExecStart"); ++ ++ SERVICE(u)->type = SERVICE_ONESHOT; ++ u->load_state = UNIT_LOADED; ++ ++ r = unit_start(u); ++ if (r < 0) ++ return log_error_errno(r, "Unit start failed %m"); ++ ++ while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED)) { ++ r = sd_event_run(m->event, UINT64_MAX); ++ if (r < 0) ++ return log_error_errno(errno, "Event run failed %m"); ++ } ++ ++ cld_code = SERVICE(u)->exec_command[SERVICE_EXEC_START]->exec_status.code; ++ if (cld_code != CLD_EXITED) ++ return log_error_errno(SYNTHETIC_ERRNO(EBUSY), ++ "ExecStart didn't exited, code='%s'", sigchld_code_to_string(cld_code)); ++ ++ if (SERVICE(u)->state != SERVICE_DEAD) ++ return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Service is not dead"); ++ ++ return r; ++} ++ ++int main(int argc, char *argv[]) { ++ _cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL; ++ _cleanup_(manager_freep) Manager *m = NULL; ++ _cleanup_free_ char *unit_dir = NULL; ++ struct rlimit rl; ++ int r; ++ ++ test_setup_logging(LOG_DEBUG); ++ ++ if (detect_container() > 0) ++ return log_tests_skipped("test-bpf fails inside LXC and Docker containers: https://github.com/systemd/systemd/issues/9666"); ++ ++ if (getuid() != 0) ++ return log_tests_skipped("not running as root"); ++ ++ assert_se(getrlimit(RLIMIT_MEMLOCK, &rl) >= 0); ++ rl.rlim_cur = rl.rlim_max = MAX(rl.rlim_max, CAN_MEMLOCK_SIZE); ++ (void) setrlimit_closest(RLIMIT_MEMLOCK, &rl); ++ ++ if (!can_memlock()) ++ return log_tests_skipped("Can't use mlock(), skipping."); ++ ++ r = cg_all_unified(); ++ if (r <= 0) ++ return log_tests_skipped_errno(r, "Unified hierarchy is required, skipping."); ++ ++ r = enter_cgroup_subroot(NULL); ++ if (r == -ENOMEDIUM) ++ return log_tests_skipped("cgroupfs not available"); ++ ++ assert_se(get_testdata_dir("units", &unit_dir) >= 0); ++ assert_se(set_unit_path(unit_dir) >= 0); ++ assert_se(runtime_dir = setup_fake_runtime_dir()); ++ ++ assert_se(manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m) >= 0); ++ assert_se(manager_startup(m, NULL, NULL) >= 0); ++ ++ assert_se(test_bpf_cgroup_programs(m, ++ "single_prog.service", single_prog, ELEMENTSOF(single_prog)) >= 0); ++ assert_se(test_bpf_cgroup_programs(m, ++ "multi_prog_same_hook.service", ++ multi_prog_same_hook, ELEMENTSOF(multi_prog_same_hook)) >= 0); ++ assert_se(test_bpf_cgroup_programs(m, ++ "same_prog_multi_hook.service", ++ same_prog_multi_hook, ELEMENTSOF(same_prog_multi_hook)) >= 0); ++ assert_se(test_bpf_cgroup_programs(m, ++ "same_prog_multi_option_0.service", ++ same_prog_multi_option_0, ELEMENTSOF(same_prog_multi_option_0)) >= 0); ++ assert_se(test_bpf_cgroup_programs(m, ++ "same_prog_multi_option_1.service", ++ same_prog_multi_option_1, ELEMENTSOF(same_prog_multi_option_1)) >= 0); ++ assert_se(test_bpf_cgroup_programs(m, ++ "same_prog_same_hook.service", ++ same_prog_same_hook, ++ ELEMENTSOF(same_prog_same_hook)) >= 0); ++ assert_se(test_bpf_cgroup_programs(m, ++ "path_split_test.service", ++ path_split_test, ++ ELEMENTSOF(path_split_test)) >= 0); ++ return 0; ++} +-- +2.30.2 + + +From faebec942b34c62d8dce512839c3a43be6844394 Mon Sep 17 00:00:00 2001 +From: Julia Kartseva +Date: Tue, 8 Dec 2020 22:06:56 -0800 +Subject: [PATCH 8/9] man: add BPFProgram= documentation + +--- + man/systemd.resource-control.xml | 52 ++++++++++++++++++++++++++++++++ + 1 file changed, 52 insertions(+) + +diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml +index 1bc45a9f00..a2d01f7afb 100644 +--- a/man/systemd.resource-control.xml ++++ b/man/systemd.resource-control.xml +@@ -696,6 +696,12 @@ + If these settings are used multiple times in the same unit all the specified programs are attached. If an + empty string is assigned to these settings the program list is reset and all previous specified programs ignored. + ++ If the path BPF_FS_PROGRAM_PATH in IPIngressFilterPath= assignment ++ is already being handled by BPFProgram= ingress hook, e.g. ++ BPFProgram=ingress:BPF_FS_PROGRAM_PATH, ++ the assignment will be still considered valid and the program will be attached to a cgroup. Same for ++ IPEgressFilterPath= path and egress hook. ++ + Note that for socket-activated services, the IP filter programs configured on the socket unit apply to + all sockets associated with it directly, but not to any sockets created by the ultimately activated services + for it. Conversely, the IP filter programs configured for the service are not applied to any sockets passed into +@@ -710,6 +716,52 @@ + + + ++ ++ BPFProgram=type:program-path ++ ++ Add a custom cgroup BPF program. ++ ++ BPFProgram= allows attaching BPF hooks to the cgroup of a systemd unit. ++ (This generalizes the functionality exposed via IPEgressFilterPath= for egress and ++ IPIngressFilterPath= for ingress.) ++ Cgroup-bpf hooks in the form of BPF programs loaded to the BPF filesystem are attached with cgroup-bpf attach ++ flags determined by the unit. For details about attachment types and flags see . ++ For general BPF documentation please refer to . ++ ++ The specification of BPF program consists of a type followed by a ++ program-path with : as the separator: ++ type:program-path. ++ ++ type is the string name of BPF attach type also used in ++ bpftool. type can be one of egress, ++ ingress, sock_create, sock_ops, ++ device, bind4, bind6, ++ connect4, connect6, post_bind4, ++ post_bind6, sendmsg4, sendmsg6, ++ sysctl, recvmsg4, recvmsg6, ++ getsockopt, setsockopt. ++ ++ Setting BPFProgram= to an empty value makes previous assignments ineffective. ++ Multiple assignments of the same type:program-path ++ value have the same effect as a single assignment: the program with the path program-path ++ will be attached to cgroup hook type just once. ++ If BPF egress pinned to program-path path is already being ++ handled by IPEgressFilterPath=, BPFProgram= ++ assignment will be considered valid and BPFProgram= will be attached to a cgroup. ++ Similarly for ingress hook and IPIngressFilterPath= assignment. ++ ++ BPF programs passed with BPFProgram= are attached to the cgroup of a unit with BPF ++ attach flag multi, that allows further attachments of the same ++ type within cgroup hierarchy topped by the unit cgroup. ++ ++ Examples: ++BPFProgram=egress:/sys/fs/bpf/egress-hook ++BPFProgram=bind6:/sys/fs/bpf/sock-addr-hook ++ ++ ++ ++ + + DeviceAllow= + +-- +2.30.2 + + +From 3e1bfa33f198feb47431d19f4d48383e070dbdca Mon Sep 17 00:00:00 2001 +From: Julia Kartseva +Date: Tue, 8 Dec 2020 22:07:30 -0800 +Subject: [PATCH 9/9] dbus-cgroup: add BPFProgram= dbus support + +- Handle BPFProgram= property in string format +":", e.g. egress:/sys/fs/bpf/egress-hook. +- Add dbus getter to list foreign bpf programs attached to a cgroup. +--- + man/org.freedesktop.systemd1.xml | 36 +++++++++++ + src/core/dbus-cgroup.c | 108 +++++++++++++++++++++++++++++++ + src/shared/bus-unit-util.c | 20 ++++++ + src/systemctl/systemctl-show.c | 17 +++++ + 4 files changed, 181 insertions(+) + +diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml +index aff43217e1..d233d96d24 100644 +--- a/man/org.freedesktop.systemd1.xml ++++ b/man/org.freedesktop.systemd1.xml +@@ -2472,6 +2472,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + readonly u ManagedOOMMemoryPressureLimit = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s ManagedOOMPreference = '...'; ++ @org.freedesktop.DBus.Property.EmitsChangedSignal("false") ++ readonly a(ss) BPFProgram = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Environment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") +@@ -3004,6 +3006,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + + ++ ++ + + + +@@ -3560,6 +3564,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice { + + + ++ ++ + + + +@@ -4245,6 +4251,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + readonly u ManagedOOMMemoryPressureLimit = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s ManagedOOMPreference = '...'; ++ @org.freedesktop.DBus.Property.EmitsChangedSignal("false") ++ readonly a(ss) BPFProgram = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Environment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") +@@ -4805,6 +4813,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + + + ++ ++ + + + +@@ -5359,6 +5369,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket { + + + ++ ++ + + + +@@ -5946,6 +5958,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { + readonly u ManagedOOMMemoryPressureLimit = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s ManagedOOMPreference = '...'; ++ @org.freedesktop.DBus.Property.EmitsChangedSignal("false") ++ readonly a(ss) BPFProgram = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Environment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") +@@ -6434,6 +6448,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { + + + ++ ++ + + + +@@ -6906,6 +6922,8 @@ node /org/freedesktop/systemd1/unit/home_2emount { + + + ++ ++ + + + +@@ -7614,6 +7632,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { + readonly u ManagedOOMMemoryPressureLimit = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s ManagedOOMPreference = '...'; ++ @org.freedesktop.DBus.Property.EmitsChangedSignal("false") ++ readonly a(ss) BPFProgram = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly as Environment = ['...', ...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") +@@ -8088,6 +8108,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { + + + ++ ++ + + + +@@ -8546,6 +8568,8 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap { + + + ++ ++ + + + +@@ -9107,6 +9131,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice { + readonly u ManagedOOMMemoryPressureLimit = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s ManagedOOMPreference = '...'; ++ @org.freedesktop.DBus.Property.EmitsChangedSignal("false") ++ readonly a(ss) BPFProgram = [...]; + }; + interface org.freedesktop.DBus.Peer { ... }; + interface org.freedesktop.DBus.Introspectable { ... }; +@@ -9245,6 +9271,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice { + + + ++ ++ + + + +@@ -9387,6 +9415,8 @@ node /org/freedesktop/systemd1/unit/system_2eslice { + + + ++ ++ + + + +@@ -9548,6 +9578,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope { + readonly u ManagedOOMMemoryPressureLimit = ...; + @org.freedesktop.DBus.Property.EmitsChangedSignal("false") + readonly s ManagedOOMPreference = '...'; ++ @org.freedesktop.DBus.Property.EmitsChangedSignal("false") ++ readonly a(ss) BPFProgram = [...]; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") + readonly s KillMode = '...'; + @org.freedesktop.DBus.Property.EmitsChangedSignal("const") +@@ -9702,6 +9734,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope { + + + ++ ++ + + + +@@ -9870,6 +9904,8 @@ node /org/freedesktop/systemd1/unit/session_2d1_2escope { + + + ++ ++ + + + +diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c +index 04d2ba34f3..604cecf84c 100644 +--- a/src/core/dbus-cgroup.c ++++ b/src/core/dbus-cgroup.c +@@ -5,6 +5,7 @@ + #include "af-list.h" + #include "alloc-util.h" + #include "bpf-firewall.h" ++#include "bpf-foreign.h" + #include "bus-get-properties.h" + #include "cgroup-util.h" + #include "cgroup.h" +@@ -347,6 +348,33 @@ static int property_get_ip_address_access( + return sd_bus_message_close_container(reply); + } + ++static int property_get_bpf_foreign_program( ++ sd_bus *bus, ++ const char *path, ++ const char *interface, ++ const char *property, ++ sd_bus_message *reply, ++ void *userdata, ++ sd_bus_error *error) { ++ CGroupContext *c = userdata; ++ CGroupBPFForeignProgram *p; ++ int r; ++ ++ r = sd_bus_message_open_container(reply, 'a', "(ss)"); ++ if (r < 0) ++ return r; ++ ++ LIST_FOREACH(programs, p, c->bpf_foreign_programs) { ++ const char *attach_type = bpf_cgroup_attach_type_to_string(p->attach_type); ++ ++ r = sd_bus_message_append(reply, "(ss)", attach_type, p->bpffs_path); ++ if (r < 0) ++ return r; ++ } ++ ++ return sd_bus_message_close_container(reply); ++} ++ + const sd_bus_vtable bus_cgroup_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Delegate", "b", bus_property_get_bool, offsetof(CGroupContext, delegate), 0), +@@ -398,6 +426,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = { + SD_BUS_PROPERTY("ManagedOOMMemoryPressure", "s", property_get_managed_oom_mode, offsetof(CGroupContext, moom_mem_pressure), 0), + SD_BUS_PROPERTY("ManagedOOMMemoryPressureLimit", "u", NULL, offsetof(CGroupContext, moom_mem_pressure_limit), 0), + SD_BUS_PROPERTY("ManagedOOMPreference", "s", property_get_managed_oom_preference, offsetof(CGroupContext, moom_preference), 0), ++ SD_BUS_PROPERTY("BPFProgram", "a(ss)", property_get_bpf_foreign_program, 0, 0), + SD_BUS_VTABLE_END + }; + +@@ -570,6 +599,85 @@ static int bus_cgroup_set_transient_property( + } + } + ++ return 1; ++ } else if (streq(name, "BPFProgram")) { ++ const char *a, *p; ++ size_t n = 0; ++ ++ r = sd_bus_message_enter_container(message, 'a', "(ss)"); ++ if (r < 0) ++ return r; ++ ++ while ((r = sd_bus_message_read(message, "(ss)", &a, &p)) > 0) { ++ int attach_type = bpf_cgroup_attach_type_from_string(a); ++ if (attach_type < 0) ++ return sd_bus_error_setf( ++ error, ++ SD_BUS_ERROR_INVALID_ARGS, ++ "%s expects a valid BPF attach type, got '%s'.", ++ name, a); ++ ++ if (!path_is_normalized(p) || !path_is_absolute(p)) ++ return sd_bus_error_setf( ++ error, ++ SD_BUS_ERROR_INVALID_ARGS, ++ "%s= expects a normalized absolute path.", ++ name); ++ ++ if (!UNIT_WRITE_FLAGS_NOOP(flags)) { ++ r = cgroup_add_bpf_foreign_program(c, attach_type, p); ++ if (r < 0) ++ return r; ++ } ++ n++; ++ } ++ if (r < 0) ++ return r; ++ ++ r = sd_bus_message_exit_container(message); ++ if (r < 0) ++ return r; ++ ++ if (!UNIT_WRITE_FLAGS_NOOP(flags)) { ++ _cleanup_free_ char *buf = NULL; ++ _cleanup_fclose_ FILE *f = NULL; ++ CGroupBPFForeignProgram *fp; ++ size_t size = 0; ++ ++ if (n == 0) ++ while (c->bpf_foreign_programs) ++ cgroup_context_remove_bpf_foreign_program(c, c->bpf_foreign_programs); ++ ++ f = open_memstream_unlocked(&buf, &size); ++ if (!f) ++ return -ENOMEM; ++ ++ fputs(name, f); ++ fputs("=\n", f); ++ ++ LIST_FOREACH(programs, fp, c->bpf_foreign_programs) ++ fprintf(f, "%s=%s:%s\n", name, ++ bpf_cgroup_attach_type_to_string(fp->attach_type), ++ fp->bpffs_path); ++ ++ r = fflush_and_check(f); ++ if (r < 0) ++ return r; ++ ++ unit_write_setting(u, flags, name, buf); ++ ++ if (!LIST_IS_EMPTY(c->bpf_foreign_programs)) { ++ r = bpf_foreign_supported(); ++ if (r < 0) ++ return r; ++ if (r == 0) ++ log_full(LOG_DEBUG, ++ "Transient unit %s configures a BPF program pinned to BPF " ++ "filesystem, but the local system does not support that.\n" ++ "Starting this unit will fail!", u->id); ++ } ++ } ++ + return 1; + } + +diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c +index a75178068b..bcb4087e6d 100644 +--- a/src/shared/bus-unit-util.c ++++ b/src/shared/bus-unit-util.c +@@ -842,6 +842,26 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons + return 1; + } + ++ if (streq(field, "BPFProgram")) { ++ if (isempty(eq)) ++ r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 0); ++ else { ++ _cleanup_free_ char *word = NULL; ++ ++ r = extract_first_word(&eq, &word, ":", 0); ++ if (r == -ENOMEM) ++ return log_oom(); ++ if (r < 0) ++ return log_error_errno(r, "Failed to parse %s: %m", field); ++ ++ r = sd_bus_message_append(m, "(sv)", field, "a(ss)", 1, word, eq); ++ } ++ if (r < 0) ++ return bus_log_create_error(r); ++ ++ return 1; ++ } ++ + return 0; + } + +diff --git a/src/systemctl/systemctl-show.c b/src/systemctl/systemctl-show.c +index 2402a59d31..dc45de4b3d 100644 +--- a/src/systemctl/systemctl-show.c ++++ b/src/systemctl/systemctl-show.c +@@ -1696,6 +1696,23 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m + + return 1; + ++ } else if (streq(name, "BPFProgram")) { ++ const char *a, *p; ++ ++ r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)"); ++ if (r < 0) ++ return bus_log_parse_error(r); ++ ++ while ((r = sd_bus_message_read(m, "(ss)", &a, &p)) > 0) ++ bus_print_property_valuef(name, expected_value, value, "%s:%s", a, p); ++ if (r < 0) ++ return bus_log_parse_error(r); ++ ++ r = sd_bus_message_exit_container(m); ++ if (r < 0) ++ return bus_log_parse_error(r); ++ ++ return 1; + } + + break; +-- +2.30.2 + diff --git a/SOURCES/17495-rebased.patch b/SOURCES/17495-rebased.patch new file mode 100644 index 0000000..0a94fe5 --- /dev/null +++ b/SOURCES/17495-rebased.patch @@ -0,0 +1,410 @@ +From 945960a5e4fa339e3d747d695c0e0996c2336e02 Mon Sep 17 00:00:00 2001 +From: Chris Down +Date: Thu, 29 Oct 2020 12:03:52 +0000 +Subject: [PATCH] bpf: pid1: Pin reference to BPF programs for post-coldplug +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +During `daemon-reload` and `daemon-reexec`, we detach and reattach all +BPF programs attached to cgroups. This, however, poses a real practical +problem for DevicePolicy (and some other settings using BPF): it +presents a period of time where the old device filtering BPF program has +been unloaded, but the new one has not been loaded yet. + +Since the filtering is at open() time, it has become apparent that that +there's a non-trivial period where applications inside that ostensibly +filtered cgroup can grab any device -- and often do so -- and then +retain access to that device even after the reload is over. Due to the +file continuing to be available after the initial open(), this issue is +particularly visible for DevicePolicy={strict,closed}, however it also +applies to other BPF programs we install. + +In particular, for BPF ingress/egress filtering this may have more +concerning implications: network traffic which is supposed to be +filtered will -- for a very brief period of time -- not be filtered or +subject to any restrictions imposed by BPF. + +These BPF programs are fundamentally attached to a cgroup lifetime, not +our unit lifetime, so it's enough to pin these programs by taking a +reference to affected BPF programs before reload/reexec. We can then +serialise the program's kernel-facing FD and cgroup attachment FD for +the new daemon, and have the daemon on the other side unpin the programs +after it's finished with coldplug. + +That means that, for example, the BPF program lifecycle during +daemon-reload or daemon-reexec changes from this: + + manager_clear_jobs_and_units + │ + ╔══════╪═════════╤═══════╗ + ║ prog │ no prog │ prog' ║ + ╚══════╧═════════╪═══════╝ + │ + manager_coldplug + +to this: + + manager_clear_jobs_and_units manager_dispatch_cgroup_realize_queue + │ │ + ╔══════╪═══════════════╤═══════════════════════╪═══════╗ + ║ prog │ prog (orphan) │ prog (orphan) + prog' │ prog' ║ + ╚══════╧═══════════════╪═══════════════════════╧═══════╝ + │ + manager_coldplug + +For daemon-reexec the semantics are mostly the same, but the point at +which the program becomes orphan is tied to the process lifecycle +instead. + +None of the BPF programs we install require exclusive access, so having +multiple instances of them running at the same time is fine. Custom +programs, of course, are unknown, but it's hard to imagine legitimate +cases which should be affected, whereas the benefits of this "overlap" +approach with reference pinning is immediately tangible. + +[keszybz: use _cleanup_ for unpin, use FOREACH_POINTER] +--- + src/core/main.c | 9 +++ + src/core/manager.c | 163 ++++++++++++++++++++++++++++++++++++++- + src/core/manager.h | 5 ++ + src/core/unit.h | 8 +- + src/shared/bpf-program.c | 10 +++ + src/shared/bpf-program.h | 1 + + 6 files changed, 191 insertions(+), 5 deletions(-) + +diff --git a/src/core/main.c b/src/core/main.c +index 3ee8d0a869..cb065dbc6e 100644 +--- a/src/core/main.c ++++ b/src/core/main.c +@@ -1164,6 +1164,14 @@ static int prepare_reexecute( + if (!fds) + return log_oom(); + ++ /* We need existing BPF programs to survive reload, otherwise there will be a period where no BPF ++ * program is active during task execution within a cgroup. This would be bad since this may have ++ * security or reliability implications: devices we should filter won't be filtered, network activity ++ * we should filter won't be filtered, etc. We pin all the existing devices by bumping their ++ * refcount, and then storing them to later have it decremented. */ ++ _cleanup_(manager_unpin_all_cgroup_bpf_programs) Manager *m_unpin = ++ manager_pin_all_cgroup_bpf_programs(m); ++ + r = manager_serialize(m, f, fds, switching_root); + if (r < 0) + return r; +@@ -1179,6 +1187,7 @@ static int prepare_reexecute( + if (r < 0) + return log_error_errno(r, "Failed to disable O_CLOEXEC for serialization fds: %m"); + ++ TAKE_PTR(m_unpin); + *ret_f = TAKE_PTR(f); + *ret_fds = TAKE_PTR(fds); + +diff --git a/src/core/manager.c b/src/core/manager.c +index 688e6881c3..44a92e7577 100644 +--- a/src/core/manager.c ++++ b/src/core/manager.c +@@ -65,6 +65,7 @@ + #include "rm-rf.h" + #include "selinux-util.h" + #include "serialize.h" ++#include "set.h" + #include "signal-util.h" + #include "socket-util.h" + #include "special.h" +@@ -3203,6 +3204,79 @@ static void manager_serialize_gid_refs(Manager *m, FILE *f) { + manager_serialize_uid_refs_internal(f, m->gid_refs, "destroy-ipc-gid"); + } + ++static int serialize_limbo_bpf_program(FILE *f, FDSet *fds, BPFProgram *p) { ++ int copy; ++ _cleanup_free_ char *ap = NULL; ++ ++ /* We don't actually need the instructions or other data, since this is only used on the other side ++ * for BPF limbo, which just requires the program type, cgroup path, and kernel-facing BPF file ++ * descriptor. We don't even need to know what unit or directive it's attached to, since we're just ++ * going to expire it after coldplug. */ ++ ++ assert(f); ++ assert(p); ++ ++ /* If the program isn't attached to the kernel yet, there's no reason to serialise it for limbo. Just ++ * let it be skeletonized and then coldplug can do the work on the other side if it's still ++ * necessary. */ ++ if (p->kernel_fd < 0 || !p->attached_path) ++ return -ENOTCONN; ++ ++ copy = fdset_put_dup(fds, p->kernel_fd); ++ if (copy < 0) ++ return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m"); ++ ++ /* Otherwise, on daemon-reload, we'd remain pinned. */ ++ safe_close(p->kernel_fd); ++ ++ ap = cescape(p->attached_path); ++ if (!ap) ++ return log_oom(); ++ ++ return serialize_item_format(f, "bpf-limbo", "%i %i %i \"%s\"", ++ copy, p->prog_type, p->attached_type, ap); ++} ++ ++static void deserialize_limbo_bpf_program(Manager *m, FDSet *fds, const char *value) { ++ _cleanup_free_ char *raw_fd = NULL, *raw_pt = NULL, *raw_at = NULL, *cgpath = NULL; ++ int fd, r, prog_type, attached_type; ++ ++ assert(m); ++ assert(value); ++ ++ r = extract_first_word(&value, &raw_fd, NULL, 0); ++ if (r <= 0 || safe_atoi(raw_fd, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) ++ return (void) log_error("Failed to parse bpf-limbo FD: %s", value); ++ ++ r = extract_first_word(&value, &raw_pt, NULL, 0); ++ if (r <= 0 || safe_atoi(raw_pt, &prog_type) < 0) ++ return (void) log_error("Failed to parse bpf-limbo program type: %s", value); ++ ++ r = extract_first_word(&value, &raw_at, NULL, 0); ++ if (r <= 0 || safe_atoi(raw_at, &attached_type) < 0) ++ return (void) log_error("Failed to parse bpf-limbo attached type: %s", value); ++ ++ r = extract_first_word(&value, &cgpath, NULL, EXTRACT_CUNESCAPE | EXTRACT_UNQUOTE); ++ if (r <= 0) ++ return (void) log_error("Failed to parse attached path for BPF limbo FD %s", value); ++ ++ _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL; ++ r = bpf_program_new(prog_type, &p); ++ if (r < 0) ++ return (void) log_error_errno(r, "Failed to create BPF limbo program: %m"); ++ ++ /* Just enough to free it when the time is right, this does not have enough information be used as a ++ * real BPFProgram. */ ++ p->attached_type = attached_type; ++ p->kernel_fd = fdset_remove(fds, fd); ++ p->attached_path = TAKE_PTR(cgpath); ++ ++ r = set_ensure_put(&m->bpf_limbo_progs, NULL, p); ++ if (r < 0) ++ return (void) log_error_errno(r, "Failed to register BPF limbo program for FD %s: %m", value); ++ TAKE_PTR(p); ++} ++ + int manager_serialize( + Manager *m, + FILE *f, +@@ -3212,6 +3286,7 @@ int manager_serialize( + const char *t; + Unit *u; + int r; ++ BPFProgram *p; + + assert(m); + assert(f); +@@ -3256,6 +3331,9 @@ int manager_serialize( + (void) serialize_dual_timestamp(f, joined, m->timestamps + q); + } + ++ SET_FOREACH(p, m->bpf_limbo_progs) ++ (void) serialize_limbo_bpf_program(f, fds, p); ++ + if (!switching_root) + (void) serialize_strv(f, "env", m->client_environment); + +@@ -3571,7 +3649,10 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { + else + m->n_failed_jobs += n; + +- } else if ((val = startswith(l, "taint-usr="))) { ++ } else if ((val = startswith(l, "bpf-limbo="))) ++ deserialize_limbo_bpf_program(m, fds, val); ++ ++ else if ((val = startswith(l, "taint-usr="))) { + int b; + + b = parse_boolean(val); +@@ -3747,6 +3828,67 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { + return manager_deserialize_units(m, f, fds); + } + ++Manager* manager_pin_all_cgroup_bpf_programs(Manager *m) { ++ int r; ++ Unit *u; ++ ++ assert(m); ++ ++ HASHMAP_FOREACH(u, m->units) { ++ BPFProgram *p; ++ ++ FOREACH_POINTER(p, ++ u->bpf_device_control_installed, ++ u->ip_bpf_ingress, ++ u->ip_bpf_ingress_installed, ++ u->ip_bpf_egress, ++ u->ip_bpf_egress_installed) ++ if (p) { ++ r = set_ensure_put(&m->bpf_limbo_progs, NULL, p); ++ if (r < 0) { ++ log_unit_error_errno(u, r, "Cannot store BPF program for reload, ignoring: %m"); ++ continue; ++ } ++ ++ bpf_program_ref(p); ++ } ++ ++ Set *s; ++ FOREACH_POINTER(s, ++ u->ip_bpf_custom_ingress, ++ u->ip_bpf_custom_ingress_installed, ++ u->ip_bpf_custom_egress, ++ u->ip_bpf_custom_egress_installed) ++ SET_FOREACH(p, s) { ++ r = set_ensure_put(&m->bpf_limbo_progs, NULL, p); ++ if (r < 0) { ++ log_unit_error_errno(u, r, "Cannot store BPF program for reload, ignoring: %m"); ++ continue; ++ } ++ ++ bpf_program_ref(p); ++ } ++ } ++ ++ log_debug("Pinned %d BPF programs", set_size(m->bpf_limbo_progs)); ++ ++ return m; ++} ++ ++static void manager_skeletonize_all_cgroup_bpf_programs(Manager *m) { ++ BPFProgram *p; ++ ++ SET_FOREACH(p, m->bpf_limbo_progs) ++ bpf_program_skeletonize(p); ++} ++ ++void manager_unpin_all_cgroup_bpf_programs(Manager **m) { ++ if (m && *m) { ++ log_debug("Unpinning %d BPF programs", set_size((*m)->bpf_limbo_progs)); ++ set_clear_with_destructor((*m)->bpf_limbo_progs, bpf_program_unref); ++ } ++} ++ + int manager_reload(Manager *m) { + _cleanup_(manager_reloading_stopp) Manager *reloading = NULL; + _cleanup_fdset_free_ FDSet *fds = NULL; +@@ -3766,6 +3908,13 @@ int manager_reload(Manager *m) { + /* We are officially in reload mode from here on. */ + reloading = manager_reloading_start(m); + ++ /* We need existing BPF programs to survive reload, otherwise there will be a period where no BPF ++ * program is active during task execution within a cgroup. This would be bad since this may have ++ * security or reliability implications: devices we should filter won't be filtered, network activity ++ * we should filter won't be filtered, etc. We pin all the existing devices by bumping their ++ * refcount, and then storing them to later have it decremented. */ ++ (void) manager_pin_all_cgroup_bpf_programs(m); ++ + r = manager_serialize(m, f, fds, false); + if (r < 0) + return r; +@@ -3790,6 +3939,12 @@ int manager_reload(Manager *m) { + m->uid_refs = hashmap_free(m->uid_refs); + m->gid_refs = hashmap_free(m->gid_refs); + ++ /* The only canonical reference left to the dynamically allocated parts of these BPF programs is ++ * going to be on the other side of manager_deserialize, so the freeable parts can now be freed. The ++ * program itself will be detached as part of manager_vacuum. */ ++ manager_skeletonize_all_cgroup_bpf_programs(m); ++ m->bpf_limbo_progs = set_free(m->bpf_limbo_progs); ++ + r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL); + if (r < 0) + log_warning_errno(r, "Failed to initialize path lookup table, ignoring: %m"); +@@ -4721,6 +4876,12 @@ static void manager_vacuum(Manager *m) { + + /* Release any runtimes no longer referenced */ + exec_runtime_vacuum(m); ++ ++ /* Release any outmoded BPF programs that were deserialized from the previous manager, since new ones ++ * should be in action now. We first need to make sure all entries in the cgroup realize queue are ++ * complete, otherwise BPF firewalls/etc may not have been set up yet. */ ++ (void) manager_dispatch_cgroup_realize_queue(m); ++ manager_unpin_all_cgroup_bpf_programs(&m); + } + + int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { +diff --git a/src/core/manager.h b/src/core/manager.h +index f58982a364..16bcdc1277 100644 +--- a/src/core/manager.h ++++ b/src/core/manager.h +@@ -438,6 +438,8 @@ struct Manager { + VarlinkServer *varlink_server; + /* Only systemd-oomd should be using this to subscribe to changes in ManagedOOM settings */ + Varlink *managed_oom_varlink_request; ++ ++ Set *bpf_limbo_progs; + }; + + static inline usec_t manager_default_timeout_abort_usec(Manager *m) { +@@ -479,6 +481,9 @@ int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode + int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Set *affected_jobs, Job **ret); + int manager_propagate_reload(Manager *m, Unit *unit, JobMode mode, sd_bus_error *e); + ++Manager* manager_pin_all_cgroup_bpf_programs(Manager *m); ++void manager_unpin_all_cgroup_bpf_programs(Manager **m); ++ + void manager_dump_units(Manager *s, FILE *f, const char *prefix); + void manager_dump_jobs(Manager *s, FILE *f, const char *prefix); + void manager_dump(Manager *s, FILE *f, const char *prefix); +diff --git a/src/core/unit.h b/src/core/unit.h +index 6de529af92..7c9cc47f92 100644 +--- a/src/core/unit.h ++++ b/src/core/unit.h +@@ -927,10 +927,10 @@ int unit_thaw_vtable_common(Unit *u); + #define log_unit_full_errno(unit, level, error, ...) \ + ({ \ + const Unit *_u = (unit); \ +- const int _l = (level); \ +- (log_get_max_level() < LOG_PRI(_l) || (_u && !unit_log_level_test(_u, _l))) ? -ERRNO_VALUE(error) : \ +- _u ? log_object_internal(_l, error, PROJECT_FILE, __LINE__, __func__, _u->manager->unit_log_field, _u->id, _u->manager->invocation_log_field, _u->invocation_id_string, ##__VA_ARGS__) : \ +- log_internal(_l, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \ ++ const int _v = (level); \ ++ (log_get_max_level() < LOG_PRI(_v) || (_u && !unit_log_level_test(_u, _v))) ? -ERRNO_VALUE(error) : \ ++ _u ? log_object_internal(_v, error, PROJECT_FILE, __LINE__, __func__, _u->manager->unit_log_field, _u->id, _u->manager->invocation_log_field, _u->invocation_id_string, ##__VA_ARGS__) : \ ++ log_internal(_v, error, PROJECT_FILE, __LINE__, __func__, ##__VA_ARGS__); \ + }) + + #define log_unit_full(unit, level, ...) (void) log_unit_full_errno(unit, level, 0, __VA_ARGS__) +diff --git a/src/shared/bpf-program.c b/src/shared/bpf-program.c +index a8a34521fd..314b48b229 100644 +--- a/src/shared/bpf-program.c ++++ b/src/shared/bpf-program.c +@@ -104,6 +104,16 @@ int bpf_program_new_from_bpffs_path(const char *path, BPFProgram **ret) { + return 0; + } + ++void bpf_program_skeletonize(BPFProgram *p) { ++ assert(p); ++ ++ /* Called shortly after serialization. From this point on, we are frozen for serialization and entry ++ * into BPF limbo, so we should proactively free our instructions and attached path. However, we ++ * shouldn't detach the program or close the kernel FD -- we need those on the other side. */ ++ free(p->instructions); ++ free(p->attached_path); ++} ++ + static BPFProgram *bpf_program_free(BPFProgram *p) { + assert(p); + +diff --git a/src/shared/bpf-program.h b/src/shared/bpf-program.h +index 86fd338c93..dcaeb6cde7 100644 +--- a/src/shared/bpf-program.h ++++ b/src/shared/bpf-program.h +@@ -29,6 +29,7 @@ int bpf_program_new(uint32_t prog_type, BPFProgram **ret); + int bpf_program_new_from_bpffs_path(const char *path, BPFProgram **ret); + BPFProgram *bpf_program_ref(BPFProgram *p); + BPFProgram *bpf_program_unref(BPFProgram *p); ++void bpf_program_skeletonize(BPFProgram *p); + + int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *insn, size_t count); + int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size); +-- +2.30.2 + diff --git a/SOURCES/17495.patch b/SOURCES/17495.patch deleted file mode 100644 index d71f54e..0000000 --- a/SOURCES/17495.patch +++ /dev/null @@ -1,421 +0,0 @@ -From 4e42210d40f96e185a55d43041dd6b962ea830dd Mon Sep 17 00:00:00 2001 -From: Chris Down -Date: Thu, 29 Oct 2020 12:03:52 +0000 -Subject: [PATCH] bpf: pid1: Pin reference to BPF programs for post-coldplug -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -During `daemon-reload` and `daemon-reexec`, we detach and reattach all -BPF programs attached to cgroups. This, however, poses a real practical -problem for DevicePolicy (and some other settings using BPF): it -presents a period of time where the old device filtering BPF program has -been unloaded, but the new one has not been loaded yet. - -Since the filtering is at open() time, it has become apparent that that -there's a non-trivial period where applications inside that ostensibly -filtered cgroup can grab any device -- and often do so -- and then -retain access to that device even after the reload is over. Due to the -file continuing to be available after the initial open(), this issue is -particularly visible for DevicePolicy={strict,closed}, however it also -applies to other BPF programs we install. - -In particular, for BPF ingress/egress filtering this may have more -concerning implications: network traffic which is supposed to be -filtered will -- for a very brief period of time -- not be filtered or -subject to any restrictions imposed by BPF. - -These BPF programs are fundamentally attached to a cgroup lifetime, not -our unit lifetime, so it's enough to pin these programs by taking a -reference to affected BPF programs before reload/reexec. We can then -serialise the program's kernel-facing FD and cgroup attachment FD for -the new daemon, and have the daemon on the other side unpin the programs -after it's finished with coldplug. - -That means that, for example, the BPF program lifecycle during -daemon-reload or daemon-reexec changes from this: - - manager_clear_jobs_and_units - │ - ╔══════╪═════════╤═══════╗ - ║ prog │ no prog │ prog' ║ - ╚══════╧═════════╪═══════╝ - │ - manager_coldplug - -to this: - - manager_clear_jobs_and_units manager_dispatch_cgroup_realize_queue - │ │ - ╔══════╪═══════════════╤═══════════════════════╪═══════╗ - ║ prog │ prog (orphan) │ prog (orphan) + prog' │ prog' ║ - ╚══════╧═══════════════╪═══════════════════════╧═══════╝ - │ - manager_coldplug - -For daemon-reexec the semantics are mostly the same, but the point at -which the program becomes orphan is tied to the process lifecycle -instead. - -None of the BPF programs we install require exclusive access, so having -multiple instances of them running at the same time is fine. Custom -programs, of course, are unknown, but it's hard to imagine legitimate -cases which should be affected, whereas the benefits of this "overlap" -approach with reference pinning is immediately tangible. - -[keszybz: use _cleanup_ for unpin, use FOREACH_POINTER] ---- - src/core/bpf-firewall.c | 9 +-- - src/core/main.c | 9 +++ - src/core/manager.c | 161 ++++++++++++++++++++++++++++++++++++++- - src/core/manager.h | 6 ++ - src/shared/bpf-program.c | 10 +++ - src/shared/bpf-program.h | 1 + - 6 files changed, 189 insertions(+), 7 deletions(-) - -diff --git a/src/core/bpf-firewall.c b/src/core/bpf-firewall.c -index 0f588b6ca5f..6283f15737b 100644 ---- a/src/core/bpf-firewall.c -+++ b/src/core/bpf-firewall.c -@@ -698,8 +698,7 @@ int bpf_firewall_install(Unit *u) { - if (r < 0) - return log_unit_error_errno(u, r, "Failed to determine cgroup path: %m"); - -- flags = (supported == BPF_FIREWALL_SUPPORTED_WITH_MULTI && -- (u->type == UNIT_SLICE || unit_cgroup_delegate(u))) ? BPF_F_ALLOW_MULTI : 0; -+ flags = (supported == BPF_FIREWALL_SUPPORTED_WITH_MULTI) ? BPF_F_ALLOW_MULTI : 0; - - /* Unref the old BPF program (which will implicitly detach it) right before attaching the new program, to - * minimize the time window when we don't account for IP traffic. */ -@@ -707,8 +706,7 @@ int bpf_firewall_install(Unit *u) { - u->ip_bpf_ingress_installed = bpf_program_unref(u->ip_bpf_ingress_installed); - - if (u->ip_bpf_egress) { -- r = bpf_program_cgroup_attach(u->ip_bpf_egress, BPF_CGROUP_INET_EGRESS, path, -- flags | (set_isempty(u->ip_bpf_custom_egress) ? 0 : BPF_F_ALLOW_MULTI)); -+ r = bpf_program_cgroup_attach(u->ip_bpf_egress, BPF_CGROUP_INET_EGRESS, path, flags); - if (r < 0) - return log_unit_error_errno(u, r, "Attaching egress BPF program to cgroup %s failed: %m", path); - -@@ -717,8 +715,7 @@ int bpf_firewall_install(Unit *u) { - } - - if (u->ip_bpf_ingress) { -- r = bpf_program_cgroup_attach(u->ip_bpf_ingress, BPF_CGROUP_INET_INGRESS, path, -- flags | (set_isempty(u->ip_bpf_custom_ingress) ? 0 : BPF_F_ALLOW_MULTI)); -+ r = bpf_program_cgroup_attach(u->ip_bpf_ingress, BPF_CGROUP_INET_INGRESS, path, flags); - if (r < 0) - return log_unit_error_errno(u, r, "Attaching ingress BPF program to cgroup %s failed: %m", path); - -diff --git a/src/core/main.c b/src/core/main.c -index eaa56aca2a4..0edf8c0abad 100644 ---- a/src/core/main.c -+++ b/src/core/main.c -@@ -1144,6 +1144,14 @@ static int prepare_reexecute( - if (!fds) - return log_oom(); - -+ /* We need existing BPF programs to survive reload, otherwise there will be a period where no BPF -+ * program is active during task execution within a cgroup. This would be bad since this may have -+ * security or reliability implications: devices we should filter won't be filtered, network activity -+ * we should filter won't be filtered, etc. We pin all the existing devices by bumping their -+ * refcount, and then storing them to later have it decremented. */ -+ _cleanup_(manager_unpin_all_cgroup_bpf_programsp) Manager *m_unpin = -+ manager_pin_all_cgroup_bpf_programs(m); -+ - r = manager_serialize(m, f, fds, switching_root); - if (r < 0) - return r; -@@ -1159,6 +1167,7 @@ static int prepare_reexecute( - if (r < 0) - return log_error_errno(r, "Failed to disable O_CLOEXEC for serialization fds: %m"); - -+ TAKE_PTR(m_unpin); - *ret_f = TAKE_PTR(f); - *ret_fds = TAKE_PTR(fds); - -diff --git a/src/core/manager.c b/src/core/manager.c -index 4b215a61766..c330a02bf79 100644 ---- a/src/core/manager.c -+++ b/src/core/manager.c -@@ -65,6 +65,7 @@ - #include "rm-rf.h" - #include "selinux-util.h" - #include "serialize.h" -+#include "set.h" - #include "signal-util.h" - #include "socket-util.h" - #include "special.h" -@@ -3213,6 +3214,79 @@ static void manager_serialize_gid_refs(Manager *m, FILE *f) { - manager_serialize_uid_refs_internal(m, f, &m->gid_refs, "destroy-ipc-gid"); - } - -+static int serialize_limbo_bpf_program(FILE *f, FDSet *fds, BPFProgram *p) { -+ int copy; -+ _cleanup_free_ char *ap = NULL; -+ -+ /* We don't actually need the instructions or other data, since this is only used on the other side -+ * for BPF limbo, which just requires the program type, cgroup path, and kernel-facing BPF file -+ * descriptor. We don't even need to know what unit or directive it's attached to, since we're just -+ * going to expire it after coldplug. */ -+ -+ assert(f); -+ assert(p); -+ -+ /* If the program isn't attached to the kernel yet, there's no reason to serialise it for limbo. Just -+ * let it be skeletonized and then coldplug can do the work on the other side if it's still -+ * necessary. */ -+ if (p->kernel_fd < 0 || !p->attached_path) -+ return -ENOTCONN; -+ -+ copy = fdset_put_dup(fds, p->kernel_fd); -+ if (copy < 0) -+ return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m"); -+ -+ /* Otherwise, on daemon-reload, we'd remain pinned. */ -+ safe_close(p->kernel_fd); -+ -+ ap = cescape(p->attached_path); -+ if (!ap) -+ return log_oom(); -+ -+ return serialize_item_format(f, "bpf-limbo", "%i %i %i \"%s\"", -+ copy, p->prog_type, p->attached_type, ap); -+} -+ -+static void deserialize_limbo_bpf_program(Manager *m, FDSet *fds, const char *value) { -+ _cleanup_free_ char *raw_fd = NULL, *raw_pt = NULL, *raw_at = NULL, *cgpath = NULL; -+ int fd, r, prog_type, attached_type; -+ -+ assert(m); -+ assert(value); -+ -+ r = extract_first_word(&value, &raw_fd, NULL, 0); -+ if (r <= 0 || safe_atoi(raw_fd, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) -+ return (void) log_error("Failed to parse bpf-limbo FD: %s", value); -+ -+ r = extract_first_word(&value, &raw_pt, NULL, 0); -+ if (r <= 0 || safe_atoi(raw_pt, &prog_type) < 0) -+ return (void) log_error("Failed to parse bpf-limbo program type: %s", value); -+ -+ r = extract_first_word(&value, &raw_at, NULL, 0); -+ if (r <= 0 || safe_atoi(raw_at, &attached_type) < 0) -+ return (void) log_error("Failed to parse bpf-limbo attached type: %s", value); -+ -+ r = extract_first_word(&value, &cgpath, NULL, EXTRACT_CUNESCAPE | EXTRACT_UNQUOTE); -+ if (r <= 0) -+ return (void) log_error("Failed to parse attached path for BPF limbo FD %s", value); -+ -+ _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL; -+ r = bpf_program_new(prog_type, &p); -+ if (r < 0) -+ return (void) log_error_errno(r, "Failed to create BPF limbo program: %m"); -+ -+ /* Just enough to free it when the time is right, this does not have enough information be used as a -+ * real BPFProgram. */ -+ p->attached_type = attached_type; -+ p->kernel_fd = fdset_remove(fds, fd); -+ p->attached_path = TAKE_PTR(cgpath); -+ -+ r = set_ensure_put(&m->bpf_limbo_progs, NULL, p); -+ if (r < 0) -+ return (void) log_error_errno(r, "Failed to register BPF limbo program for FD %s: %m", value); -+ TAKE_PTR(p); -+} -+ - int manager_serialize( - Manager *m, - FILE *f, -@@ -3222,6 +3296,7 @@ int manager_serialize( - const char *t; - Unit *u; - int r; -+ BPFProgram *p; - - assert(m); - assert(f); -@@ -3266,6 +3341,9 @@ int manager_serialize( - (void) serialize_dual_timestamp(f, joined, m->timestamps + q); - } - -+ SET_FOREACH(p, m->bpf_limbo_progs) -+ (void) serialize_limbo_bpf_program(f, fds, p); -+ - if (!switching_root) - (void) serialize_strv(f, "env", m->client_environment); - -@@ -3584,7 +3662,10 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { - else - m->n_failed_jobs += n; - -- } else if ((val = startswith(l, "taint-usr="))) { -+ } else if ((val = startswith(l, "bpf-limbo="))) -+ deserialize_limbo_bpf_program(m, fds, val); -+ -+ else if ((val = startswith(l, "taint-usr="))) { - int b; - - b = parse_boolean(val); -@@ -3760,6 +3841,65 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { - return manager_deserialize_units(m, f, fds); - } - -+Manager* manager_pin_all_cgroup_bpf_programs(Manager *m) { -+ int r; -+ Unit *u; -+ -+ assert(m); -+ -+ HASHMAP_FOREACH(u, m->units) { -+ BPFProgram *p; -+ -+ FOREACH_POINTER(p, -+ u->bpf_device_control_installed, -+ u->ip_bpf_ingress, -+ u->ip_bpf_ingress_installed, -+ u->ip_bpf_egress, -+ u->ip_bpf_egress_installed) -+ if (p) { -+ r = set_ensure_put(&m->bpf_limbo_progs, NULL, p); -+ if (r < 0) { -+ log_unit_error_errno(u, r, "Cannot store BPF program for reload, ignoring: %m"); -+ continue; -+ } -+ -+ bpf_program_ref(p); -+ } -+ -+ Set *s; -+ FOREACH_POINTER(s, -+ u->ip_bpf_custom_ingress, -+ u->ip_bpf_custom_ingress_installed, -+ u->ip_bpf_custom_egress, -+ u->ip_bpf_custom_egress_installed) -+ SET_FOREACH(p, s) { -+ r = set_ensure_put(&m->bpf_limbo_progs, NULL, p); -+ if (r < 0) { -+ log_unit_error_errno(u, r, "Cannot store BPF program for reload, ignoring: %m"); -+ continue; -+ } -+ -+ bpf_program_ref(p); -+ } -+ } -+ -+ log_debug("Pinned %d BPF programs", set_size(m->bpf_limbo_progs)); -+ -+ return m; -+} -+ -+static void manager_skeletonize_all_cgroup_bpf_programs(Manager *m) { -+ BPFProgram *p; -+ -+ SET_FOREACH(p, m->bpf_limbo_progs) -+ bpf_program_skeletonize(p); -+} -+ -+void manager_unpin_all_cgroup_bpf_programs(Manager *m) { -+ log_debug("Unpinning %d BPF programs", set_size(m->bpf_limbo_progs)); -+ set_clear_with_destructor(m->bpf_limbo_progs, bpf_program_unref); -+} -+ - int manager_reload(Manager *m) { - _cleanup_(manager_reloading_stopp) Manager *reloading = NULL; - _cleanup_fdset_free_ FDSet *fds = NULL; -@@ -3779,6 +3919,13 @@ int manager_reload(Manager *m) { - /* We are officially in reload mode from here on. */ - reloading = manager_reloading_start(m); - -+ /* We need existing BPF programs to survive reload, otherwise there will be a period where no BPF -+ * program is active during task execution within a cgroup. This would be bad since this may have -+ * security or reliability implications: devices we should filter won't be filtered, network activity -+ * we should filter won't be filtered, etc. We pin all the existing devices by bumping their -+ * refcount, and then storing them to later have it decremented. */ -+ (void) manager_pin_all_cgroup_bpf_programs(m); -+ - r = manager_serialize(m, f, fds, false); - if (r < 0) - return r; -@@ -3803,6 +3950,12 @@ int manager_reload(Manager *m) { - m->uid_refs = hashmap_free(m->uid_refs); - m->gid_refs = hashmap_free(m->gid_refs); - -+ /* The only canonical reference left to the dynamically allocated parts of these BPF programs is -+ * going to be on the other side of manager_deserialize, so the freeable parts can now be freed. The -+ * program itself will be detached as part of manager_vacuum. */ -+ manager_skeletonize_all_cgroup_bpf_programs(m); -+ m->bpf_limbo_progs = set_free(m->bpf_limbo_progs); -+ - r = lookup_paths_init(&m->lookup_paths, m->unit_file_scope, 0, NULL); - if (r < 0) - log_warning_errno(r, "Failed to initialize path lookup table, ignoring: %m"); -@@ -4737,6 +4890,12 @@ static void manager_vacuum(Manager *m) { - - /* Release any runtimes no longer referenced */ - exec_runtime_vacuum(m); -+ -+ /* Release any outmoded BPF programs that were deserialized from the previous manager, since new ones -+ * should be in action now. We first need to make sure all entries in the cgroup realize queue are -+ * complete, otherwise BPF firewalls/etc may not have been set up yet. */ -+ (void) manager_dispatch_cgroup_realize_queue(m); -+ manager_unpin_all_cgroup_bpf_programs(m); - } - - int manager_dispatch_user_lookup_fd(sd_event_source *source, int fd, uint32_t revents, void *userdata) { -diff --git a/src/core/manager.h b/src/core/manager.h -index 19df889dd89..2c4a2b60632 100644 ---- a/src/core/manager.h -+++ b/src/core/manager.h -@@ -438,6 +438,8 @@ struct Manager { - VarlinkServer *varlink_server; - /* Only systemd-oomd should be using this to subscribe to changes in ManagedOOM settings */ - Varlink *managed_oom_varlink_request; -+ -+ Set *bpf_limbo_progs; - }; - - static inline usec_t manager_default_timeout_abort_usec(Manager *m) { -@@ -479,6 +481,10 @@ int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode - int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Set *affected_jobs, Job **ret); - int manager_propagate_reload(Manager *m, Unit *unit, JobMode mode, sd_bus_error *e); - -+Manager* manager_pin_all_cgroup_bpf_programs(Manager *m); -+void manager_unpin_all_cgroup_bpf_programs(Manager *m); -+DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unpin_all_cgroup_bpf_programs); -+ - void manager_dump_units(Manager *s, FILE *f, const char *prefix); - void manager_dump_jobs(Manager *s, FILE *f, const char *prefix); - void manager_dump(Manager *s, FILE *f, const char *prefix); -diff --git a/src/shared/bpf-program.c b/src/shared/bpf-program.c -index 10239142af3..549490da6ce 100644 ---- a/src/shared/bpf-program.c -+++ b/src/shared/bpf-program.c -@@ -209,6 +209,16 @@ int bpf_program_cgroup_detach(BPFProgram *p) { - return 0; - } - -+void bpf_program_skeletonize(BPFProgram *p) { -+ assert(p); -+ -+ /* Called shortly after serialization. From this point on, we are frozen for serialization and entry -+ * into BPF limbo, so we should proactively free our instructions and attached path. However, we -+ * shouldn't detach the program or close the kernel FD -- we need those on the other side. */ -+ free(p->instructions); -+ free(p->attached_path); -+} -+ - int bpf_map_new(enum bpf_map_type type, size_t key_size, size_t value_size, size_t max_entries, uint32_t flags) { - union bpf_attr attr; - int fd; -diff --git a/src/shared/bpf-program.h b/src/shared/bpf-program.h -index eef77f9d8e1..5957a6ce300 100644 ---- a/src/shared/bpf-program.h -+++ b/src/shared/bpf-program.h -@@ -28,6 +28,7 @@ struct BPFProgram { - int bpf_program_new(uint32_t prog_type, BPFProgram **ret); - BPFProgram *bpf_program_unref(BPFProgram *p); - BPFProgram *bpf_program_ref(BPFProgram *p); -+void bpf_program_skeletonize(BPFProgram *p); - - int bpf_program_add_instructions(BPFProgram *p, const struct bpf_insn *insn, size_t count); - int bpf_program_load_kernel(BPFProgram *p, char *log_buf, size_t log_size); diff --git a/SOURCES/17872.patch b/SOURCES/17872.patch deleted file mode 100644 index fd1ff35..0000000 --- a/SOURCES/17872.patch +++ /dev/null @@ -1,98 +0,0 @@ -From 5b98412c6f0cb9e63a7c8f795064d2043cc0baaa Mon Sep 17 00:00:00 2001 -From: Yu Watanabe -Date: Sun, 6 Dec 2020 22:29:43 +0900 -Subject: [PATCH] core/namespace: use existing /proc when not enough priviledge - -Fixes #17860. ---- - src/core/namespace.c | 61 ++++++++++++++++++++++++-------------------- - 1 file changed, 34 insertions(+), 27 deletions(-) - -diff --git a/src/core/namespace.c b/src/core/namespace.c -index cdf427a6ea9..8560ad9a754 100644 ---- a/src/core/namespace.c -+++ b/src/core/namespace.c -@@ -859,25 +859,15 @@ static int mount_sysfs(const MountEntry *m) { - } - - static int mount_procfs(const MountEntry *m, const NamespaceInfo *ns_info) { -+ _cleanup_free_ char *opts = NULL; - const char *entry_path; -- int r; -+ int r, n; - - assert(m); - assert(ns_info); - -- entry_path = mount_entry_path(m); -- -- /* Mount a new instance, so that we get the one that matches our user namespace, if we are running in -- * one. i.e we don't reuse existing mounts here under any condition, we want a new instance owned by -- * our user namespace and with our hidepid= settings applied. Hence, let's get rid of everything -- * mounted on /proc/ first. */ -- -- (void) mkdir_p_label(entry_path, 0755); -- (void) umount_recursive(entry_path, 0); -- - if (ns_info->protect_proc != PROTECT_PROC_DEFAULT || - ns_info->proc_subset != PROC_SUBSET_ALL) { -- _cleanup_free_ char *opts = NULL; - - /* Starting with kernel 5.8 procfs' hidepid= logic is truly per-instance (previously it - * pretended to be per-instance but actually was per-namespace), hence let's make use of it -@@ -891,23 +881,40 @@ static int mount_procfs(const MountEntry *m, const NamespaceInfo *ns_info) { - ns_info->proc_subset == PROC_SUBSET_PID ? ",subset=pid" : ""); - if (!opts) - return -ENOMEM; -- -- r = mount_nofollow_verbose(LOG_DEBUG, "proc", entry_path, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, opts); -- if (r < 0) { -- if (r != -EINVAL) -- return r; -- -- /* If this failed with EINVAL then this likely means the textual hidepid= stuff is -- * not supported by the kernel, and thus the per-instance hidepid= neither, which -- * means we really don't want to use it, since it would affect our host's /proc -- * mount. Hence let's gracefully fallback to a classic, unrestricted version. */ -- } else -- return 1; - } - -- r = mount_nofollow_verbose(LOG_DEBUG, "proc", entry_path, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL); -- if (r < 0) -- return r; -+ entry_path = mount_entry_path(m); -+ (void) mkdir_p_label(entry_path, 0755); -+ -+ /* Mount a new instance, so that we get the one that matches our user namespace, if we are running in -+ * one. i.e we don't reuse existing mounts here under any condition, we want a new instance owned by -+ * our user namespace and with our hidepid= settings applied. Hence, let's get rid of everything -+ * mounted on /proc/ first. */ -+ -+ n = umount_recursive(entry_path, 0); -+ -+ r = mount_nofollow_verbose(LOG_DEBUG, "proc", entry_path, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, opts); -+ if (r == -EINVAL && opts) -+ /* If this failed with EINVAL then this likely means the textual hidepid= stuff is -+ * not supported by the kernel, and thus the per-instance hidepid= neither, which -+ * means we really don't want to use it, since it would affect our host's /proc -+ * mount. Hence let's gracefully fallback to a classic, unrestricted version. */ -+ r = mount_nofollow_verbose(LOG_DEBUG, "proc", entry_path, "proc", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL); -+ if (r == -EPERM) { -+ /* When we do not have enough priviledge to mount /proc, fallback to use existing /proc. */ -+ -+ if (n > 0) -+ /* /proc or some of sub-mounts are umounted in the above. Refuse incomplete tree. -+ * Propagate the original error code returned by mount() in the above. */ -+ return -EPERM; -+ -+ r = path_is_mount_point(entry_path, NULL, 0); -+ if (r < 0) -+ return log_debug_errno(r, "Unable to determine whether /proc is already mounted: %m"); -+ if (r == 0) -+ /* /proc is not mounted. Propagate the original error code. */ -+ return -EPERM; -+ } - - return 1; - } diff --git a/SOURCES/18211.patch b/SOURCES/18211.patch deleted file mode 100644 index 922721d..0000000 --- a/SOURCES/18211.patch +++ /dev/null @@ -1,106 +0,0 @@ -From 1d225dbe60540867624da8f099852881f28f4293 Mon Sep 17 00:00:00 2001 -From: Anita Zhang -Date: Mon, 11 Jan 2021 20:04:20 -0800 -Subject: [PATCH] core: update setings on the unit and job as the result of - ExecCondition= - -Update ExecCondition= to set Unit->condition_result and return JOB_DONE -in the Job results if the check fails so as to match the current behavior -of ConditionXYZ= w.r.t units/jobs dependency checks. - -Fixes: #18207 ---- - src/core/job.c | 3 +-- - src/core/service.c | 17 ++++++++++------- - src/core/unit.c | 2 -- - src/core/unit.h | 1 - - 4 files changed, 11 insertions(+), 12 deletions(-) - -diff --git a/src/core/job.c b/src/core/job.c -index f3c1a028316..7d5c288ea41 100644 ---- a/src/core/job.c -+++ b/src/core/job.c -@@ -882,8 +882,7 @@ static void job_log_done_status_message(Unit *u, uint32_t job_id, JobType t, Job - return; - - /* Show condition check message if the job did not actually do anything due to failed condition. */ -- if ((t == JOB_START && result == JOB_DONE && !u->condition_result) || -- (t == JOB_START && result == JOB_SKIPPED)) { -+ if (t == JOB_START && result == JOB_DONE && !u->condition_result) { - log_struct(LOG_INFO, - "MESSAGE=Condition check resulted in %s being skipped.", unit_status_string(u), - "JOB_ID=%" PRIu32, job_id, -diff --git a/src/core/service.c b/src/core/service.c -index b1a4d0bf181..93cf711de42 100644 ---- a/src/core/service.c -+++ b/src/core/service.c -@@ -1105,8 +1105,7 @@ static void service_set_state(Service *s, ServiceState state) { - - unit_notify(UNIT(s), table[old_state], table[state], - (s->reload_result == SERVICE_SUCCESS ? 0 : UNIT_NOTIFY_RELOAD_FAILURE) | -- (s->will_auto_restart ? UNIT_NOTIFY_WILL_AUTO_RESTART : 0) | -- (s->result == SERVICE_SKIP_CONDITION ? UNIT_NOTIFY_SKIP_CONDITION : 0)); -+ (s->will_auto_restart ? UNIT_NOTIFY_WILL_AUTO_RESTART : 0)); - } - - static usec_t service_coldplug_timeout(Service *s) { -@@ -3521,10 +3520,6 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { - } else if (s->control_pid == pid) { - s->control_pid = 0; - -- /* ExecCondition= calls that exit with (0, 254] should invoke skip-like behavior instead of failing */ -- if (f == SERVICE_FAILURE_EXIT_CODE && s->state == SERVICE_CONDITION && status < 255) -- f = SERVICE_SKIP_CONDITION; -- - if (s->control_command) { - exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); - -@@ -3532,6 +3527,15 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { - f = SERVICE_SUCCESS; - } - -+ /* ExecCondition= calls that exit with (0, 254] should invoke skip-like behavior instead of failing */ -+ if (s->state == SERVICE_CONDITION) { -+ if (f == SERVICE_FAILURE_EXIT_CODE && status < 255) { -+ UNIT(s)->condition_result = false; -+ f = SERVICE_SKIP_CONDITION; -+ } else if (f == SERVICE_SUCCESS) -+ UNIT(s)->condition_result = true; -+ } -+ - unit_log_process_exit( - u, - "Control process", -@@ -4576,7 +4580,6 @@ const UnitVTable service_vtable = { - }, - .finished_start_job = { - [JOB_FAILED] = "Failed to start %s.", -- [JOB_SKIPPED] = "Skipped %s.", - }, - .finished_stop_job = { - [JOB_DONE] = "Stopped %s.", -diff --git a/src/core/unit.c b/src/core/unit.c -index e209efd4a66..53273b5984b 100644 ---- a/src/core/unit.c -+++ b/src/core/unit.c -@@ -2559,8 +2559,6 @@ static bool unit_process_job(Job *j, UnitActiveState ns, UnitNotifyFlags flags) - if (UNIT_IS_INACTIVE_OR_FAILED(ns)) { - if (ns == UNIT_FAILED) - result = JOB_FAILED; -- else if (FLAGS_SET(flags, UNIT_NOTIFY_SKIP_CONDITION)) -- result = JOB_SKIPPED; - else - result = JOB_DONE; - -diff --git a/src/core/unit.h b/src/core/unit.h -index 02b2b24206f..f040e9dfe6f 100644 ---- a/src/core/unit.h -+++ b/src/core/unit.h -@@ -738,7 +738,6 @@ int unit_kill_common(Unit *u, KillWho who, int signo, pid_t main_pid, pid_t cont - typedef enum UnitNotifyFlags { - UNIT_NOTIFY_RELOAD_FAILURE = 1 << 0, - UNIT_NOTIFY_WILL_AUTO_RESTART = 1 << 1, -- UNIT_NOTIFY_SKIP_CONDITION = 1 << 2, - } UnitNotifyFlags; - - void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlags flags); diff --git a/SOURCES/18886.patch b/SOURCES/18886.patch deleted file mode 100644 index 2dc80ae..0000000 --- a/SOURCES/18886.patch +++ /dev/null @@ -1,56 +0,0 @@ -From f975f1cc748929942188ae1490cf8480f8a64877 Mon Sep 17 00:00:00 2001 -From: Anita Zhang -Date: Thu, 4 Mar 2021 19:56:16 -0800 -Subject: [PATCH 1/2] shutdown: set always_reopen_console - -Back in v232 systemd-shutdown would log to /dev/console. However after -the addition of always_reopen_console (v233) it would log to STDERR. -This caused some debugging issues as container systemd-shutdown logs -weren't being logged to console as the arg `--log-target=console` suggested. - -Since it appears that always_reopen_console was intended for pid1, set -it in systemd-shutdown as well so logs will go to /dev/console. ---- - src/shutdown/shutdown.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c -index 0d07865542e..c1fdc885afb 100644 ---- a/src/shutdown/shutdown.c -+++ b/src/shutdown/shutdown.c -@@ -322,6 +322,9 @@ int main(int argc, char *argv[]) { - log_set_prohibit_ipc(true); - log_parse_environment(); - -+ if (getpid_cached() == 1) -+ log_set_always_reopen_console(true); -+ - r = parse_argv(argc, argv); - if (r < 0) - goto error; - -From 016f36ae72611210d6517b37429bfbdc26c5e31c Mon Sep 17 00:00:00 2001 -From: Anita Zhang -Date: Thu, 4 Mar 2021 20:00:05 -0800 -Subject: [PATCH 2/2] shutdown: log on container exit - ---- - src/shutdown/shutdown.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c -index c1fdc885afb..06b6618ff4c 100644 ---- a/src/shutdown/shutdown.c -+++ b/src/shutdown/shutdown.c -@@ -559,8 +559,10 @@ int main(int argc, char *argv[]) { - sync_with_progress(); - - if (streq(arg_verb, "exit")) { -- if (in_container) -+ if (in_container) { -+ log_info("Exiting container."); - return arg_exit_code; -+ } - - cmd = RB_POWER_OFF; /* We cannot exit() on the host, fallback on another method. */ - } diff --git a/SOURCES/18955.patch b/SOURCES/18955.patch deleted file mode 100644 index b6a3953..0000000 --- a/SOURCES/18955.patch +++ /dev/null @@ -1,1168 +0,0 @@ -From 1677b88d01729f514dd17145e3aefaa5db6cdf95 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= -Date: Wed, 10 Mar 2021 11:49:02 +0100 -Subject: [PATCH 1/9] fstab-generator: do not propagate error if we fail to - canonicalize - -r is used for the return value of the function, so we shouldn't -use it a non-fatal check. ---- - src/fstab-generator/fstab-generator.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c -index 0910a9aa61b..7cb4ea286dc 100644 ---- a/src/fstab-generator/fstab-generator.c -+++ b/src/fstab-generator/fstab-generator.c -@@ -611,11 +611,11 @@ static int parse_fstab(bool initrd) { - * /etc/fstab. So we canonicalize here. Note that we use CHASE_NONEXISTENT to handle the case - * where a symlink refers to another mount target; this works assuming the sub-mountpoint - * target is the final directory. */ -- r = chase_symlinks(where, initrd ? "/sysroot" : NULL, -+ k = chase_symlinks(where, initrd ? "/sysroot" : NULL, - CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, - &canonical_where, NULL); -- if (r < 0) /* If we can't canonicalize we continue on as if it wasn't a symlink */ -- log_debug_errno(r, "Failed to read symlink target for %s, ignoring: %m", where); -+ if (k < 0) /* If we can't canonicalize we continue on as if it wasn't a symlink */ -+ log_debug_errno(k, "Failed to read symlink target for %s, ignoring: %m", where); - else if (streq(canonical_where, where)) /* If it was fully canonicalized, suppress the change */ - canonical_where = mfree(canonical_where); - else - -From 924f65030529d5a232c2be4ab6e2642dfdc2ea71 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= -Date: Wed, 10 Mar 2021 13:20:47 +0100 -Subject: [PATCH 2/9] generators: warn but ignore failure to write timeouts - -When we failed to split the options (because of disallowed quoting syntax, which -might be a bug in its own), we would silently fail. Instead, let's emit a warning. -Since we ignore the value if we cannot parse it anyway, let's ignore this error -too. ---- - src/shared/generator.c | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) - -diff --git a/src/shared/generator.c b/src/shared/generator.c -index 8b95c772db1..41922d67d8c 100644 ---- a/src/shared/generator.c -+++ b/src/shared/generator.c -@@ -216,8 +216,12 @@ int generator_write_timeouts( - r = fstab_filter_options(opts, "comment=systemd.device-timeout\0" - "x-systemd.device-timeout\0", - NULL, &timeout, filtered); -- if (r <= 0) -- return r; -+ if (r < 0) { -+ log_warning_errno(r, "Failed to parse fstab options, ignoring: %m"); -+ return 0; -+ } -+ if (r == 0) -+ return 0; - - r = parse_sec_fix_0(timeout, &u); - if (r < 0) { - -From 5fa2da125157a1beca508792ee5d9be833c2c0cb Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= -Date: Wed, 10 Mar 2021 13:35:26 +0100 -Subject: [PATCH 3/9] shared/fstab-util: immediately drop empty options again -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -In the conversion from strv_split() to strv_split_full() done in -7bb553bb98a57b4e03804f8192bdc5a534325582, EXTRACT_DONT_COALESCE_SEPARATORS was -added. I think this was just by mistake… We never look for "empty options", so -whether we immediately ignore the extra separator or store the empty string in -strv, should make no difference. ---- - src/shared/fstab-util.c | 2 +- - src/test/test-fstab-util.c | 6 +++++- - 2 files changed, 6 insertions(+), 2 deletions(-) - -diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c -index 292b97cd692..1ddcd371cfc 100644 ---- a/src/shared/fstab-util.c -+++ b/src/shared/fstab-util.c -@@ -140,7 +140,7 @@ int fstab_filter_options(const char *opts, const char *names, - break; - } - } else { -- r = strv_split_full(&stor, opts, ",", EXTRACT_DONT_COALESCE_SEPARATORS | EXTRACT_UNESCAPE_SEPARATORS); -+ r = strv_split_full(&stor, opts, ",", EXTRACT_UNESCAPE_SEPARATORS); - if (r < 0) - return r; - -diff --git a/src/test/test-fstab-util.c b/src/test/test-fstab-util.c -index 222ffbb2a75..ebbdd05ca62 100644 ---- a/src/test/test-fstab-util.c -+++ b/src/test/test-fstab-util.c -@@ -91,9 +91,13 @@ static void test_fstab_filter_options(void) { - do_fstab_filter_options("opt =0", "x-opt\0opt\0noopt\0x-noopt\0", 0, NULL, NULL, NULL); - do_fstab_filter_options(" opt ", "opt\0x-opt\0", 0, NULL, NULL, NULL); - -- /* check function will NULL args */ -+ /* check function with NULL args */ - do_fstab_filter_options(NULL, "opt\0", 0, NULL, NULL, ""); - do_fstab_filter_options("", "opt\0", 0, NULL, NULL, ""); -+ -+ /* unnecessary comma separators */ -+ do_fstab_filter_options("opt=x,,,,", "opt\0", 1, "opt", "x", ""); -+ do_fstab_filter_options(",,,opt=x,,,,", "opt\0", 1, "opt", "x", ""); - } - - static void test_fstab_find_pri(void) { - -From 8723c716c795b4083372b11dd7065bca2aadbc70 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= -Date: Wed, 10 Mar 2021 14:23:23 +0100 -Subject: [PATCH 4/9] basic/extract_word: try to explain what the various - options do - -A test for stripping of escaped backslashes without any flags was explicitly -added back in 4034a06ddb82ec9868cd52496fef2f5faa25575f. So it seems to be on -purpose, though I would say that this is at least surprising and hence deserves -a comment. - -In test-extract-word, add tests for standalone EXTRACT_UNESCAPE_SEPARATORS. -Only behaviour combined with EXTRACT_CUNESCAPE was tested. ---- - src/basic/extract-word.h | 16 +++++++++------- - src/test/test-extract-word.c | 21 +++++++++++++++++++++ - 2 files changed, 30 insertions(+), 7 deletions(-) - -diff --git a/src/basic/extract-word.h b/src/basic/extract-word.h -index d1de32e5806..3c1e7d98b64 100644 ---- a/src/basic/extract-word.h -+++ b/src/basic/extract-word.h -@@ -4,13 +4,15 @@ - #include "macro.h" - - typedef enum ExtractFlags { -- EXTRACT_RELAX = 1 << 0, -- EXTRACT_CUNESCAPE = 1 << 1, -- EXTRACT_CUNESCAPE_RELAX = 1 << 2, -- EXTRACT_UNESCAPE_SEPARATORS = 1 << 3, -- EXTRACT_UNQUOTE = 1 << 4, -- EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5, -- EXTRACT_RETAIN_ESCAPE = 1 << 6, -+ EXTRACT_RELAX = 1 << 0, /* Allow unbalanced quote and eat up trailing backslash. */ -+ EXTRACT_CUNESCAPE = 1 << 1, /* Unescape known escape sequences. */ -+ EXTRACT_CUNESCAPE_RELAX = 1 << 2, /* Allow and keep unknown escape sequences, allow and keep trailing backslash. */ -+ EXTRACT_UNESCAPE_SEPARATORS = 1 << 3, /* Unescape separators (those specified, or whitespace by default). */ -+ EXTRACT_UNQUOTE = 1 << 4, /* Remove quoting with "" and ''. */ -+ EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5, /* Don't treat multiple adjacent separators as one */ -+ EXTRACT_RETAIN_ESCAPE = 1 << 6, /* Treat escape character '\' as any other character without special meaning */ -+ -+ /* Note that if no flags are specified, escaped escape characters will be silently stripped. */ - } ExtractFlags; - - int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags); -diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c -index 56b516fe40a..f718556d399 100644 ---- a/src/test/test-extract-word.c -+++ b/src/test/test-extract-word.c -@@ -344,6 +344,27 @@ static void test_extract_first_word(void) { - free(t); - assert_se(p == NULL); - -+ p = "\\:"; -+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == 1); -+ assert_se(streq(t, ":")); -+ free(t); -+ assert_se(p == NULL); -+ -+ p = "a\\:b"; -+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == 1); -+ assert_se(streq(t, "a:b")); -+ free(t); -+ assert_se(p == NULL); -+ -+ p = "a\\ b:c"; -+ assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_UNESCAPE_SEPARATORS) == 1); -+ assert_se(streq(t, "a b")); -+ free(t); -+ assert_se(extract_first_word(&p, &t, WHITESPACE ":", EXTRACT_UNESCAPE_SEPARATORS) == 1); -+ assert_se(streq(t, "c")); -+ free(t); -+ assert_se(p == NULL); -+ - p = "\\:"; - assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1); - assert_se(streq(t, ":")); - -From 76c4e48ee603593822b3076c4cf5b63768ec55e3 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= -Date: Wed, 10 Mar 2021 15:17:56 +0100 -Subject: [PATCH 5/9] basic/extract-word: allow escape character to be escaped - -With EXTRACT_UNESCAPE_SEPARATORS, backslash is used to escape the separator. -But it wasn't possible to insert the backslash itself. Let's allow this and -add test. ---- - src/basic/extract-word.c | 4 ++-- - src/test/test-extract-word.c | 24 ++++++++++++++++++++++++ - 2 files changed, 26 insertions(+), 2 deletions(-) - -diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c -index 4e4e7e8ce93..06b813c031a 100644 ---- a/src/basic/extract-word.c -+++ b/src/basic/extract-word.c -@@ -102,8 +102,8 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra - else - sz += utf8_encode_unichar(s + sz, u); - } else if ((flags & EXTRACT_UNESCAPE_SEPARATORS) && -- strchr(separators, **p)) -- /* An escaped separator char */ -+ (strchr(separators, **p) || **p == '\\')) -+ /* An escaped separator char or the escape char itself */ - s[sz++] = c; - else if (flags & EXTRACT_CUNESCAPE_RELAX) { - s[sz++] = '\\'; -diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c -index f718556d399..217c600dac6 100644 ---- a/src/test/test-extract-word.c -+++ b/src/test/test-extract-word.c -@@ -365,6 +365,18 @@ static void test_extract_first_word(void) { - free(t); - assert_se(p == NULL); - -+ p = "a\\ b:c\\x"; -+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == -EINVAL); -+ -+ p = "a\\\\ b:c\\\\x"; -+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == 1); -+ assert_se(streq(t, "a\\ b")); -+ free(t); -+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_UNESCAPE_SEPARATORS) == 1); -+ assert_se(streq(t, "c\\x")); -+ free(t); -+ assert_se(p == NULL); -+ - p = "\\:"; - assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1); - assert_se(streq(t, ":")); -@@ -386,6 +398,18 @@ static void test_extract_first_word(void) { - free(t); - assert_se(p == NULL); - -+ p = "a\\ b:c\\x"; -+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == -EINVAL); -+ -+ p = "a\\\\ b:c\\\\x"; -+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1); -+ assert_se(streq(t, "a\\ b")); -+ free(t); -+ assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_SEPARATORS) == 1); -+ assert_se(streq(t, "c\\x")); -+ free(t); -+ assert_se(p == NULL); -+ - p = "\\:"; - assert_se(extract_first_word(&p, &t, ":", EXTRACT_CUNESCAPE) == -EINVAL); - - -From 3141089f53274849ecedb84aacc6a35fd679d3c2 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= -Date: Wed, 10 Mar 2021 15:39:47 +0100 -Subject: [PATCH 6/9] basic/extract-word: rename flag - -The flag enables "relaxed mode" for all kinds of unescaping, not just c-unescaping. ---- - src/basic/extract-word.c | 16 ++++++++-------- - src/basic/extract-word.h | 2 +- - src/resolve/resolved-conf.c | 2 +- - src/test/test-extract-word.c | 22 +++++++++++----------- - src/test/test-strv.c | 4 ++-- - 5 files changed, 23 insertions(+), 23 deletions(-) - -diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c -index 06b813c031a..d1af11318a8 100644 ---- a/src/basic/extract-word.c -+++ b/src/basic/extract-word.c -@@ -69,14 +69,14 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra - return -ENOMEM; - - if (c == 0) { -- if ((flags & EXTRACT_CUNESCAPE_RELAX) && -+ if ((flags & EXTRACT_UNESCAPE_RELAX) && - (quote == 0 || flags & EXTRACT_RELAX)) { - /* If we find an unquoted trailing backslash and we're in -- * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the -+ * EXTRACT_UNESCAPE_RELAX mode, keep it verbatim in the - * output. - * - * Unbalanced quotes will only be allowed in EXTRACT_RELAX -- * mode, EXTRACT_CUNESCAPE_RELAX mode does not allow them. -+ * mode, EXTRACT_UNESCAPE_RELAX mode does not allow them. - */ - s[sz++] = '\\'; - goto finish_force_terminate; -@@ -105,7 +105,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra - (strchr(separators, **p) || **p == '\\')) - /* An escaped separator char or the escape char itself */ - s[sz++] = c; -- else if (flags & EXTRACT_CUNESCAPE_RELAX) { -+ else if (flags & EXTRACT_UNESCAPE_RELAX) { - s[sz++] = '\\'; - s[sz++] = c; - } else -@@ -196,7 +196,7 @@ int extract_first_word_and_warn( - const char *rvalue) { - - /* Try to unquote it, if it fails, warn about it and try again -- * but this time using EXTRACT_CUNESCAPE_RELAX to keep the -+ * but this time using EXTRACT_UNESCAPE_RELAX to keep the - * backslashes verbatim in invalid escape sequences. */ - - const char *save; -@@ -207,11 +207,11 @@ int extract_first_word_and_warn( - if (r >= 0) - return r; - -- if (r == -EINVAL && !(flags & EXTRACT_CUNESCAPE_RELAX)) { -+ if (r == -EINVAL && !(flags & EXTRACT_UNESCAPE_RELAX)) { - -- /* Retry it with EXTRACT_CUNESCAPE_RELAX. */ -+ /* Retry it with EXTRACT_UNESCAPE_RELAX. */ - *p = save; -- r = extract_first_word(p, ret, separators, flags|EXTRACT_CUNESCAPE_RELAX); -+ r = extract_first_word(p, ret, separators, flags|EXTRACT_UNESCAPE_RELAX); - if (r >= 0) { - /* It worked this time, hence it must have been an invalid escape sequence. */ - log_syntax(unit, LOG_WARNING, filename, line, EINVAL, "Ignoring unknown escape sequences: \"%s\"", *ret); -diff --git a/src/basic/extract-word.h b/src/basic/extract-word.h -index 3c1e7d98b64..0e9e77e93d3 100644 ---- a/src/basic/extract-word.h -+++ b/src/basic/extract-word.h -@@ -6,7 +6,7 @@ - typedef enum ExtractFlags { - EXTRACT_RELAX = 1 << 0, /* Allow unbalanced quote and eat up trailing backslash. */ - EXTRACT_CUNESCAPE = 1 << 1, /* Unescape known escape sequences. */ -- EXTRACT_CUNESCAPE_RELAX = 1 << 2, /* Allow and keep unknown escape sequences, allow and keep trailing backslash. */ -+ EXTRACT_UNESCAPE_RELAX = 1 << 2, /* Allow and keep unknown escape sequences, allow and keep trailing backslash. */ - EXTRACT_UNESCAPE_SEPARATORS = 1 << 3, /* Unescape separators (those specified, or whitespace by default). */ - EXTRACT_UNQUOTE = 1 << 4, /* Remove quoting with "" and ''. */ - EXTRACT_DONT_COALESCE_SEPARATORS = 1 << 5, /* Don't treat multiple adjacent separators as one */ -diff --git a/src/resolve/resolved-conf.c b/src/resolve/resolved-conf.c -index f2a33162517..87d1794a741 100644 ---- a/src/resolve/resolved-conf.c -+++ b/src/resolve/resolved-conf.c -@@ -348,7 +348,7 @@ int config_parse_dnssd_txt( - int r; - - r = extract_first_word(&rvalue, &word, NULL, -- EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX); -+ EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX); - if (r == 0) - break; - if (r == -ENOMEM) -diff --git a/src/test/test-extract-word.c b/src/test/test-extract-word.c -index 217c600dac6..f1085266df2 100644 ---- a/src/test/test-extract-word.c -+++ b/src/test/test-extract-word.c -@@ -172,19 +172,19 @@ static void test_extract_first_word(void) { - assert_se(isempty(p)); - - p = original = "fooo\\"; -- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX) > 0); -+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX) > 0); - assert_se(streq(t, "fooo\\")); - free(t); - assert_se(isempty(p)); - - p = original = "fooo\\"; -- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0); -+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0); - assert_se(streq(t, "fooo\\")); - free(t); - assert_se(isempty(p)); - - p = original = "fooo\\"; -- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0); -+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX) > 0); - assert_se(streq(t, "fooo\\")); - free(t); - assert_se(isempty(p)); -@@ -230,17 +230,17 @@ static void test_extract_first_word(void) { - assert_se(isempty(p)); - - p = original = "\"foo\\"; -- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE_RELAX) == -EINVAL); -+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_UNESCAPE_RELAX) == -EINVAL); - assert_se(p == original + 5); - - p = original = "\"foo\\"; -- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0); -+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0); - assert_se(streq(t, "foo\\")); - free(t); - assert_se(isempty(p)); - - p = original = "\"foo\\"; -- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0); -+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNQUOTE|EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0); - assert_se(streq(t, "foo\\")); - free(t); - assert_se(isempty(p)); -@@ -252,13 +252,13 @@ static void test_extract_first_word(void) { - assert_se(p == original + 10); - - p = original = "fooo\\ bar quux"; -- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX) > 0); -+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX) > 0); - assert_se(streq(t, "fooo bar")); - free(t); - assert_se(p == original + 10); - - p = original = "fooo\\ bar quux"; -- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE_RELAX|EXTRACT_RELAX) > 0); -+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_UNESCAPE_RELAX|EXTRACT_RELAX) > 0); - assert_se(streq(t, "fooo bar")); - free(t); - assert_se(p == original + 10); -@@ -268,7 +268,7 @@ static void test_extract_first_word(void) { - assert_se(p == original + 5); - - p = original = "fooo\\ bar quux"; -- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0); -+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX) > 0); - assert_se(streq(t, "fooo\\ bar")); - free(t); - assert_se(p == original + 10); -@@ -278,13 +278,13 @@ static void test_extract_first_word(void) { - assert_se(p == original + 1); - - p = original = "\\w+@\\K[\\d.]+"; -- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0); -+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX) > 0); - assert_se(streq(t, "\\w+@\\K[\\d.]+")); - free(t); - assert_se(isempty(p)); - - p = original = "\\w+\\b"; -- assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_CUNESCAPE_RELAX) > 0); -+ assert_se(extract_first_word(&p, &t, NULL, EXTRACT_CUNESCAPE|EXTRACT_UNESCAPE_RELAX) > 0); - assert_se(streq(t, "\\w+\b")); - free(t); - assert_se(isempty(p)); -diff --git a/src/test/test-strv.c b/src/test/test-strv.c -index 162d8bed951..039bb2c78af 100644 ---- a/src/test/test-strv.c -+++ b/src/test/test-strv.c -@@ -333,12 +333,12 @@ static void test_strv_split(void) { - l = strv_free_erase(l); - - assert_se(strv_split_full(&l, " 'one' \" two\t three \"' four five", NULL, -- EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_CUNESCAPE_RELAX) == 2); -+ EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_UNESCAPE_RELAX) == 2); - assert_se(strv_equal(l, (char**) input_table_quoted_joined)); - - l = strv_free_erase(l); - -- assert_se(strv_split_full(&l, "\\", NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_CUNESCAPE_RELAX) == 1); -+ assert_se(strv_split_full(&l, "\\", NULL, EXTRACT_UNQUOTE | EXTRACT_RELAX | EXTRACT_UNESCAPE_RELAX) == 1); - assert_se(strv_equal(l, STRV_MAKE("\\"))); - } - - -From 0264b404b9f193b70a19db0f600cf6bab3a05368 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= -Date: Wed, 10 Mar 2021 16:53:38 +0100 -Subject: [PATCH 7/9] shared/fstab-util: pass through the escape character -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -… when not used to escape the separator (,) or the escape character (\). -This mostly restores behaviour from before 0645b83a40d1c782f173c4d8440ab2fc82a75006, -but still allows "," to be escaped. - -Partially fixes #18952. ---- - src/shared/fstab-util.c | 20 ++++++++++++-------- - src/test/test-fstab-util.c | 4 ++++ - 2 files changed, 16 insertions(+), 8 deletions(-) - -diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c -index 1ddcd371cfc..8dc1733c0de 100644 ---- a/src/shared/fstab-util.c -+++ b/src/shared/fstab-util.c -@@ -97,16 +97,18 @@ int fstab_filter_options(const char *opts, const char *names, - for (const char *word = opts;;) { - const char *end = word; - -- /* Look for an *non-escaped* comma separator. Only commas can be escaped, so "\," is -- * the only valid escape sequence, so we can do a very simple test here. */ -+ /* Look for a *non-escaped* comma separator. Only commas and backslashes can be -+ * escaped, so "\," and "\\" are the only valid escape sequences, and we can do a -+ * very simple test here. */ - for (;;) { -- size_t n = strcspn(end, ","); -+ end += strcspn(end, ",\\"); - -- end += n; -- if (n > 0 && end[-1] == '\\') -- end++; -- else -+ if (IN_SET(*end, ',', '\0')) - break; -+ assert(*end == '\\'); -+ end ++; /* Skip the backslash */ -+ if (*end != '\0') -+ end ++; /* Skip the escaped char, but watch out for a trailing commma */ - } - - NULSTR_FOREACH(name, names) { -@@ -140,7 +142,9 @@ int fstab_filter_options(const char *opts, const char *names, - break; - } - } else { -- r = strv_split_full(&stor, opts, ",", EXTRACT_UNESCAPE_SEPARATORS); -+ /* For backwards compatibility, we need to pass-through escape characters. -+ * The only ones we "consume" are the ones used as "\," or "\\". */ -+ r = strv_split_full(&stor, opts, ",", EXTRACT_UNESCAPE_SEPARATORS | EXTRACT_UNESCAPE_RELAX); - if (r < 0) - return r; - -diff --git a/src/test/test-fstab-util.c b/src/test/test-fstab-util.c -index ebbdd05ca62..3a7ec170d65 100644 ---- a/src/test/test-fstab-util.c -+++ b/src/test/test-fstab-util.c -@@ -98,6 +98,10 @@ static void test_fstab_filter_options(void) { - /* unnecessary comma separators */ - do_fstab_filter_options("opt=x,,,,", "opt\0", 1, "opt", "x", ""); - do_fstab_filter_options(",,,opt=x,,,,", "opt\0", 1, "opt", "x", ""); -+ -+ /* escaped characters */ -+ do_fstab_filter_options("opt1=\\\\,opt2=\\xff", "opt1\0", 1, "opt1", "\\", "opt2=\\xff"); -+ do_fstab_filter_options("opt1=\\\\,opt2=\\xff", "opt2\0", 1, "opt2", "\\xff", "opt1=\\"); - } - - static void test_fstab_find_pri(void) { - -From ff0c31bc2722eed528eae6644a104e85ed97f2f1 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= -Date: Thu, 11 Mar 2021 10:37:36 +0100 -Subject: [PATCH 8/9] shared/fstab-util: teach fstab_filter_options() a mode - where all values are returned - -Apart from tests, the new argument isn't used anywhere, so there should be no -functional change. Note that the two arms of the big conditional are switched, so the -diff is artificially inflated. The actual code change is rather small. I dropped the -path which extracts ret_value manually, because it wasn't supporting unescaping of the -escape character properly. ---- - src/core/mount.c | 2 +- - src/cryptsetup/cryptsetup-generator.c | 9 +- - src/fstab-generator/fstab-generator.c | 2 +- - src/shared/fstab-util.c | 124 +++++++++++++---------- - src/shared/fstab-util.h | 12 ++- - src/shared/generator.c | 2 +- - src/test/test-fstab-util.c | 139 +++++++++++++++----------- - 7 files changed, 169 insertions(+), 121 deletions(-) - -diff --git a/src/core/mount.c b/src/core/mount.c -index 23b558859c2..ca5d0939a18 100644 ---- a/src/core/mount.c -+++ b/src/core/mount.c -@@ -1019,7 +1019,7 @@ static void mount_enter_mounting(Mount *m) { - if (p) { - _cleanup_free_ char *opts = NULL; - -- r = fstab_filter_options(p->options, "nofail\0" "noauto\0" "auto\0", NULL, NULL, &opts); -+ r = fstab_filter_options(p->options, "nofail\0" "noauto\0" "auto\0", NULL, NULL, NULL, &opts); - if (r < 0) - goto fail; - -diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c -index 74f739b5139..98c8408da54 100644 ---- a/src/cryptsetup/cryptsetup-generator.c -+++ b/src/cryptsetup/cryptsetup-generator.c -@@ -301,7 +301,9 @@ static int create_disk( - netdev = fstab_test_option(options, "_netdev\0"); - attach_in_initrd = fstab_test_option(options, "x-initrd.attach\0"); - -- keyfile_can_timeout = fstab_filter_options(options, "keyfile-timeout\0", NULL, &keyfile_timeout_value, NULL); -+ keyfile_can_timeout = fstab_filter_options(options, -+ "keyfile-timeout\0", -+ NULL, &keyfile_timeout_value, NULL, NULL); - if (keyfile_can_timeout < 0) - return log_error_errno(keyfile_can_timeout, "Failed to parse keyfile-timeout= option value: %m"); - -@@ -310,11 +312,12 @@ static int create_disk( - "header\0", - NULL, - &header_path, -+ NULL, - headerdev ? &filtered_header : NULL); - if (detached_header < 0) - return log_error_errno(detached_header, "Failed to parse header= option value: %m"); - -- tmp = fstab_filter_options(options, "tmp\0", NULL, &tmp_fstype, NULL); -+ tmp = fstab_filter_options(options, "tmp\0", NULL, &tmp_fstype, NULL, NULL); - if (tmp < 0) - return log_error_errno(tmp, "Failed to parse tmp= option value: %m"); - -@@ -602,7 +605,7 @@ static int filter_header_device(const char *options, - assert(ret_headerdev); - assert(ret_filtered_headerdev_options); - -- r = fstab_filter_options(options, "header\0", NULL, &headerspec, &filtered_headerspec); -+ r = fstab_filter_options(options, "header\0", NULL, &headerspec, NULL, &filtered_headerspec); - if (r < 0) - return log_error_errno(r, "Failed to parse header= option value: %m"); - -diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c -index 7cb4ea286dc..b454a5980d4 100644 ---- a/src/fstab-generator/fstab-generator.c -+++ b/src/fstab-generator/fstab-generator.c -@@ -200,7 +200,7 @@ static int write_timeout( - usec_t u; - int r; - -- r = fstab_filter_options(opts, filter, NULL, &timeout, NULL); -+ r = fstab_filter_options(opts, filter, NULL, &timeout, NULL, NULL); - if (r < 0) - return log_warning_errno(r, "Failed to parse options: %m"); - if (r == 0) -diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c -index 8dc1733c0de..6674ed4a19f 100644 ---- a/src/shared/fstab-util.c -+++ b/src/shared/fstab-util.c -@@ -79,21 +79,80 @@ int fstab_is_mount_point(const char *mount) { - return false; - } - --int fstab_filter_options(const char *opts, const char *names, -- const char **ret_namefound, char **ret_value, char **ret_filtered) { -+int fstab_filter_options( -+ const char *opts, -+ const char *names, -+ const char **ret_namefound, -+ char **ret_value, -+ char ***ret_values, -+ char **ret_filtered) { -+ - const char *name, *namefound = NULL, *x; -- _cleanup_strv_free_ char **stor = NULL; -- _cleanup_free_ char *v = NULL, **strv = NULL; -+ _cleanup_strv_free_ char **stor = NULL, **values = NULL; -+ _cleanup_free_ char *value = NULL, **filtered = NULL; - int r; - - assert(names && *names); -+ assert(!(ret_value && ret_values)); - - if (!opts) - goto answer; - -- /* If !ret_value and !ret_filtered, this function is not allowed to fail. */ -+ /* Finds any options matching 'names', and returns: -+ * - the last matching option name in ret_namefound, -+ * - the last matching value in ret_value, -+ * - any matching values in ret_values, -+ * - the rest of the option string in ret_filtered. -+ * -+ * If !ret_value and !ret_values and !ret_filtered, this function is not allowed to fail. -+ * -+ * Returns negative on error, true if any matching options were found, false otherwise. */ -+ -+ if (ret_filtered || ret_value || ret_values) { -+ /* For backwards compatibility, we need to pass-through escape characters. -+ * The only ones we "consume" are the ones used as "\," or "\\". */ -+ r = strv_split_full(&stor, opts, ",", EXTRACT_UNESCAPE_SEPARATORS | EXTRACT_UNESCAPE_RELAX); -+ if (r < 0) -+ return r; -+ -+ filtered = memdup(stor, sizeof(char*) * (strv_length(stor) + 1)); -+ if (!filtered) -+ return -ENOMEM; -+ -+ char **t = filtered; -+ for (char **s = t; *s; s++) { -+ NULSTR_FOREACH(name, names) { -+ x = startswith(*s, name); -+ if (!x) -+ continue; -+ /* Match name, but when ret_values, only when followed by assignment. */ -+ if (*x == '=' || (!ret_values && *x == '\0')) -+ goto found; -+ } -+ -+ *t = *s; -+ t++; -+ continue; -+ found: -+ /* Keep the last occurrence found */ -+ namefound = name; -+ -+ if (ret_value || ret_values) { -+ assert(IN_SET(*x, '=', '\0')); - -- if (!ret_filtered) { -+ if (ret_value) { -+ r = free_and_strdup(&value, *x == '=' ? x + 1 : NULL); -+ if (r < 0) -+ return r; -+ } else if (*x) { -+ r = strv_extend(&values, x + 1); -+ if (r < 0) -+ return r; -+ } -+ } -+ } -+ *t = NULL; -+ } else - for (const char *word = opts;;) { - const char *end = word; - -@@ -121,17 +180,6 @@ int fstab_filter_options(const char *opts, const char *names, - x = word + strlen(name); - if (IN_SET(*x, '\0', '=', ',')) { - namefound = name; -- if (ret_value) { -- bool eq = *x == '='; -- assert(eq || IN_SET(*x, ',', '\0')); -- -- r = free_and_strndup(&v, -- eq ? x + 1 : NULL, -- eq ? end - x - 1 : 0); -- if (r < 0) -- return r; -- } -- - break; - } - } -@@ -141,40 +189,6 @@ int fstab_filter_options(const char *opts, const char *names, - else - break; - } -- } else { -- /* For backwards compatibility, we need to pass-through escape characters. -- * The only ones we "consume" are the ones used as "\," or "\\". */ -- r = strv_split_full(&stor, opts, ",", EXTRACT_UNESCAPE_SEPARATORS | EXTRACT_UNESCAPE_RELAX); -- if (r < 0) -- return r; -- -- strv = memdup(stor, sizeof(char*) * (strv_length(stor) + 1)); -- if (!strv) -- return -ENOMEM; -- -- char **t = strv; -- for (char **s = strv; *s; s++) { -- NULSTR_FOREACH(name, names) { -- x = startswith(*s, name); -- if (x && IN_SET(*x, '\0', '=')) -- goto found; -- } -- -- *t = *s; -- t++; -- continue; -- found: -- /* Keep the last occurrence found */ -- namefound = name; -- if (ret_value) { -- assert(IN_SET(*x, '=', '\0')); -- r = free_and_strdup(&v, *x == '=' ? x + 1 : NULL); -- if (r < 0) -- return r; -- } -- } -- *t = NULL; -- } - - answer: - if (ret_namefound) -@@ -182,14 +196,16 @@ int fstab_filter_options(const char *opts, const char *names, - if (ret_filtered) { - char *f; - -- f = strv_join_full(strv, ",", NULL, true); -+ f = strv_join_full(filtered, ",", NULL, true); - if (!f) - return -ENOMEM; - - *ret_filtered = f; - } - if (ret_value) -- *ret_value = TAKE_PTR(v); -+ *ret_value = TAKE_PTR(value); -+ if (ret_values) -+ *ret_values = TAKE_PTR(values); - - return !!namefound; - } -@@ -229,7 +245,7 @@ int fstab_find_pri(const char *options, int *ret) { - - assert(ret); - -- r = fstab_filter_options(options, "pri\0", NULL, &opt, NULL); -+ r = fstab_filter_options(options, "pri\0", NULL, &opt, NULL, NULL); - if (r < 0) - return r; - if (r == 0 || !opt) -diff --git a/src/shared/fstab-util.h b/src/shared/fstab-util.h -index 1a602cb56b2..97f40221afb 100644 ---- a/src/shared/fstab-util.h -+++ b/src/shared/fstab-util.h -@@ -10,12 +10,18 @@ bool fstab_is_extrinsic(const char *mount, const char *opts); - int fstab_is_mount_point(const char *mount); - int fstab_has_fstype(const char *fstype); - --int fstab_filter_options(const char *opts, const char *names, const char **namefound, char **value, char **filtered); -+int fstab_filter_options( -+ const char *opts, -+ const char *names, -+ const char **ret_namefound, -+ char **ret_value, -+ char ***ret_values, -+ char **ret_filtered); - - int fstab_extract_values(const char *opts, const char *name, char ***values); - - static inline bool fstab_test_option(const char *opts, const char *names) { -- return !!fstab_filter_options(opts, names, NULL, NULL, NULL); -+ return !!fstab_filter_options(opts, names, NULL, NULL, NULL, NULL); - } - - int fstab_find_pri(const char *options, int *ret); -@@ -26,7 +32,7 @@ static inline bool fstab_test_yes_no_option(const char *opts, const char *yes_no - /* If first name given is last, return 1. - * If second name given is last or neither is found, return 0. */ - -- assert_se(fstab_filter_options(opts, yes_no, &opt, NULL, NULL) >= 0); -+ assert_se(fstab_filter_options(opts, yes_no, &opt, NULL, NULL, NULL) >= 0); - - return opt == yes_no; - } -diff --git a/src/shared/generator.c b/src/shared/generator.c -index 41922d67d8c..5b9c4325271 100644 ---- a/src/shared/generator.c -+++ b/src/shared/generator.c -@@ -215,7 +215,7 @@ int generator_write_timeouts( - - r = fstab_filter_options(opts, "comment=systemd.device-timeout\0" - "x-systemd.device-timeout\0", -- NULL, &timeout, filtered); -+ NULL, &timeout, NULL, filtered); - if (r < 0) { - log_warning_errno(r, "Failed to parse fstab options, ignoring: %m"); - return 0; -diff --git a/src/test/test-fstab-util.c b/src/test/test-fstab-util.c -index 3a7ec170d65..d2f20185265 100644 ---- a/src/test/test-fstab-util.c -+++ b/src/test/test-fstab-util.c -@@ -6,102 +6,125 @@ - #include "fstab-util.h" - #include "log.h" - #include "string-util.h" -+#include "strv.h" - - /* --int fstab_filter_options(const char *opts, const char *names, -- const char **namefound, char **value, char **filtered); -+int fstab_filter_options( -+ const char *opts, -+ const char *names, -+ const char **ret_namefound, -+ const char **ret_value, -+ const char **ret_values, -+ char **ret_filtered); - */ - - static void do_fstab_filter_options(const char *opts, - const char *remove, - int r_expected, -+ int r_values_expected, - const char *name_expected, - const char *value_expected, -+ const char *values_expected, - const char *filtered_expected) { - int r; - const char *name; -- _cleanup_free_ char *value = NULL, *filtered = NULL; -+ _cleanup_free_ char *value = NULL, *filtered = NULL, *joined = NULL; -+ _cleanup_strv_free_ char **values = NULL; - -- r = fstab_filter_options(opts, remove, &name, &value, &filtered); -- log_info("\"%s\" → %d, \"%s\", \"%s\", \"%s\", expected %d, \"%s\", \"%s\", \"%s\"", -- opts, r, name, value, filtered, -+ /* test mode which returns the last value */ -+ -+ r = fstab_filter_options(opts, remove, &name, &value, NULL, &filtered); -+ log_info("1: \"%s\" → %d, \"%s\", \"%s\", \"%s\", expected %d, \"%s\", \"%s\", \"%s\"", -+ opts, r, strnull(name), value, filtered, - r_expected, name_expected, value_expected, filtered_expected ?: opts); - assert_se(r == r_expected); - assert_se(streq_ptr(name, name_expected)); - assert_se(streq_ptr(value, value_expected)); - assert_se(streq_ptr(filtered, filtered_expected ?: opts)); - -+ /* test mode which returns all the values */ -+ -+ r = fstab_filter_options(opts, remove, &name, NULL, &values, NULL); -+ assert_se(joined = strv_join(values, ":")); -+ log_info("2: \"%s\" → %d, \"%s\", \"%s\", expected %d, \"%s\", \"%s\"", -+ opts, r, strnull(name), joined, -+ r_values_expected, name_expected, values_expected); -+ assert_se(r == r_values_expected); -+ assert_se(streq_ptr(name, r_values_expected > 0 ? name_expected : NULL)); -+ assert_se(streq_ptr(joined, values_expected)); -+ - /* also test the malloc-less mode */ -- r = fstab_filter_options(opts, remove, &name, NULL, NULL); -- log_info("\"%s\" → %d, \"%s\", expected %d, \"%s\"\n-", -- opts, r, name, -+ r = fstab_filter_options(opts, remove, &name, NULL, NULL, NULL); -+ log_info("3: \"%s\" → %d, \"%s\", expected %d, \"%s\"\n-", -+ opts, r, strnull(name), - r_expected, name_expected); - assert_se(r == r_expected); - assert_se(streq_ptr(name, name_expected)); - } - - static void test_fstab_filter_options(void) { -- do_fstab_filter_options("opt=0", "opt\0x-opt\0", 1, "opt", "0", ""); -- do_fstab_filter_options("opt=0", "x-opt\0opt\0", 1, "opt", "0", ""); -- do_fstab_filter_options("opt", "opt\0x-opt\0", 1, "opt", NULL, ""); -- do_fstab_filter_options("opt", "x-opt\0opt\0", 1, "opt", NULL, ""); -- do_fstab_filter_options("x-opt", "x-opt\0opt\0", 1, "x-opt", NULL, ""); -- -- do_fstab_filter_options("opt=0,other", "opt\0x-opt\0", 1, "opt", "0", "other"); -- do_fstab_filter_options("opt=0,other", "x-opt\0opt\0", 1, "opt", "0", "other"); -- do_fstab_filter_options("opt,other", "opt\0x-opt\0", 1, "opt", NULL, "other"); -- do_fstab_filter_options("opt,other", "x-opt\0opt\0", 1, "opt", NULL, "other"); -- do_fstab_filter_options("x-opt,other", "opt\0x-opt\0", 1, "x-opt", NULL, "other"); -- -- do_fstab_filter_options("opt=0\\,1,other", "opt\0x-opt\0", 1, "opt", "0,1", "other"); -- do_fstab_filter_options("opt=0,other,x-opt\\,foobar", "x-opt\0opt\0", 1, "opt", "0", "other,x-opt\\,foobar"); -- do_fstab_filter_options("opt,other,x-opt\\,part", "opt\0x-opt\0", 1, "opt", NULL, "other,x-opt\\,part"); -- do_fstab_filter_options("opt,other,part\\,x-opt", "x-opt\0opt\0", 1, "opt", NULL, "other,part\\,x-opt"); -- do_fstab_filter_options("opt,other\\,\\,\\,opt,x-part", "opt\0x-opt\0", 1, "opt", NULL, "other\\,\\,\\,opt,x-part"); -- -- do_fstab_filter_options("opto=0,other", "opt\0x-opt\0", 0, NULL, NULL, NULL); -- do_fstab_filter_options("opto,other", "opt\0x-opt\0", 0, NULL, NULL, NULL); -- do_fstab_filter_options("x-opto,other", "opt\0x-opt\0", 0, NULL, NULL, NULL); -- -- do_fstab_filter_options("first,opt=0", "opt\0x-opt\0", 1, "opt", "0", "first"); -- do_fstab_filter_options("first=1,opt=0", "opt\0x-opt\0", 1, "opt", "0", "first=1"); -- do_fstab_filter_options("first,opt=", "opt\0x-opt\0", 1, "opt", "", "first"); -- do_fstab_filter_options("first=1,opt", "opt\0x-opt\0", 1, "opt", NULL, "first=1"); -- do_fstab_filter_options("first=1,x-opt", "opt\0x-opt\0", 1, "x-opt", NULL, "first=1"); -- -- do_fstab_filter_options("first,opt=0,last=1", "opt\0x-opt\0", 1, "opt", "0", "first,last=1"); -- do_fstab_filter_options("first=1,opt=0,last=2", "x-opt\0opt\0", 1, "opt", "0", "first=1,last=2"); -- do_fstab_filter_options("first,opt,last", "opt\0", 1, "opt", NULL, "first,last"); -- do_fstab_filter_options("first=1,opt,last", "x-opt\0opt\0", 1, "opt", NULL, "first=1,last"); -- do_fstab_filter_options("first=,opt,last", "opt\0noopt\0", 1, "opt", NULL, "first=,last"); -+ do_fstab_filter_options("opt=0", "opt\0x-opt\0", 1, 1, "opt", "0", "0", ""); -+ do_fstab_filter_options("opt=0", "x-opt\0opt\0", 1, 1, "opt", "0", "0", ""); -+ do_fstab_filter_options("opt", "opt\0x-opt\0", 1, 0, "opt", NULL, "", ""); -+ do_fstab_filter_options("opt", "x-opt\0opt\0", 1, 0, "opt", NULL, "", ""); -+ do_fstab_filter_options("x-opt", "x-opt\0opt\0", 1, 0, "x-opt", NULL, "", ""); -+ -+ do_fstab_filter_options("opt=0,other", "opt\0x-opt\0", 1, 1, "opt", "0", "0", "other"); -+ do_fstab_filter_options("opt=0,other", "x-opt\0opt\0", 1, 1, "opt", "0", "0", "other"); -+ do_fstab_filter_options("opt,other", "opt\0x-opt\0", 1, 0, "opt", NULL, "", "other"); -+ do_fstab_filter_options("opt,other", "x-opt\0opt\0", 1, 0, "opt", NULL, "", "other"); -+ do_fstab_filter_options("x-opt,other", "opt\0x-opt\0", 1, 0, "x-opt", NULL, "", "other"); -+ -+ do_fstab_filter_options("opt=0\\,1,other", "opt\0x-opt\0", 1, 1, "opt", "0,1", "0,1", "other"); -+ do_fstab_filter_options("opt=0,other,x-opt\\,foobar", "x-opt\0opt\0", 1, 1, "opt", "0", "0", "other,x-opt\\,foobar"); -+ do_fstab_filter_options("opt,other,x-opt\\,part", "opt\0x-opt\0", 1, 0, "opt", NULL, "", "other,x-opt\\,part"); -+ do_fstab_filter_options("opt,other,part\\,x-opt", "x-opt\0opt\0", 1, 0, "opt", NULL, "", "other,part\\,x-opt"); -+ do_fstab_filter_options("opt,other\\,\\,\\,opt,x-part", "opt\0x-opt\0", 1, 0, "opt", NULL, "", "other\\,\\,\\,opt,x-part"); -+ -+ do_fstab_filter_options("opto=0,other", "opt\0x-opt\0", 0, 0, NULL, NULL, "", NULL); -+ do_fstab_filter_options("opto,other", "opt\0x-opt\0", 0, 0, NULL, NULL, "", NULL); -+ do_fstab_filter_options("x-opto,other", "opt\0x-opt\0", 0, 0, NULL, NULL, "", NULL); -+ -+ do_fstab_filter_options("first,opt=0", "opt\0x-opt\0", 1, 1, "opt", "0", "0", "first"); -+ do_fstab_filter_options("first=1,opt=0", "opt\0x-opt\0", 1, 1, "opt", "0", "0", "first=1"); -+ do_fstab_filter_options("first,opt=", "opt\0x-opt\0", 1, 1, "opt", "", "", "first"); -+ do_fstab_filter_options("first=1,opt", "opt\0x-opt\0", 1, 0, "opt", NULL, "", "first=1"); -+ do_fstab_filter_options("first=1,x-opt", "opt\0x-opt\0", 1, 0, "x-opt", NULL, "", "first=1"); -+ -+ do_fstab_filter_options("first,opt=0,last=1", "opt\0x-opt\0", 1, 1, "opt", "0", "0", "first,last=1"); -+ do_fstab_filter_options("first=1,opt=0,last=2", "x-opt\0opt\0", 1, 1, "opt", "0", "0", "first=1,last=2"); -+ do_fstab_filter_options("first,opt,last", "opt\0", 1, 0, "opt", NULL, "", "first,last"); -+ do_fstab_filter_options("first=1,opt,last", "x-opt\0opt\0", 1, 0, "opt", NULL, "", "first=1,last"); -+ do_fstab_filter_options("first=,opt,last", "opt\0noopt\0", 1, 0, "opt", NULL, "", "first=,last"); - - /* check repeated options */ -- do_fstab_filter_options("first,opt=0,noopt=1,last=1", "opt\0noopt\0", 1, "noopt", "1", "first,last=1"); -- do_fstab_filter_options("first=1,opt=0,last=2,opt=1", "opt\0", 1, "opt", "1", "first=1,last=2"); -- do_fstab_filter_options("x-opt=0,x-opt=1", "opt\0x-opt\0", 1, "x-opt", "1", ""); -- do_fstab_filter_options("opt=0,x-opt=1", "opt\0x-opt\0", 1, "x-opt", "1", ""); -+ do_fstab_filter_options("first,opt=0,noopt=1,last=1", "opt\0noopt\0", 1, 1, "noopt", "1", "0:1", "first,last=1"); -+ do_fstab_filter_options("first=1,opt=0,last=2,opt=1", "opt\0", 1, 1, "opt", "1", "0:1", "first=1,last=2"); -+ do_fstab_filter_options("x-opt=0,x-opt=1", "opt\0x-opt\0", 1, 1, "x-opt", "1", "0:1", ""); -+ do_fstab_filter_options("opt=0,x-opt=1", "opt\0x-opt\0", 1, 1, "x-opt", "1", "0:1", ""); -+ do_fstab_filter_options("opt=0,opt=1,opt=,opt=,opt=2", "opt\0noopt\0", 1, 1, "opt", "2", "0:1:::2", ""); - - /* check that semicolons are not misinterpreted */ -- do_fstab_filter_options("opt=0;", "opt\0", 1, "opt", "0;", ""); -- do_fstab_filter_options("opt;=0", "x-opt\0opt\0noopt\0x-noopt\0", 0, NULL, NULL, NULL); -- do_fstab_filter_options("opt;", "opt\0x-opt\0", 0, NULL, NULL, NULL); -+ do_fstab_filter_options("opt=0;", "opt\0", 1, 1, "opt", "0;", "0;", ""); -+ do_fstab_filter_options("opt;=0", "x-opt\0opt\0noopt\0x-noopt\0", 0, 0, NULL, NULL, "", NULL); -+ do_fstab_filter_options("opt;", "opt\0x-opt\0", 0, 0, NULL, NULL, "", NULL); - - /* check that spaces are not misinterpreted */ -- do_fstab_filter_options("opt=0 ", "opt\0", 1, "opt", "0 ", ""); -- do_fstab_filter_options("opt =0", "x-opt\0opt\0noopt\0x-noopt\0", 0, NULL, NULL, NULL); -- do_fstab_filter_options(" opt ", "opt\0x-opt\0", 0, NULL, NULL, NULL); -+ do_fstab_filter_options("opt=0 ", "opt\0", 1, 1, "opt", "0 ", "0 ", ""); -+ do_fstab_filter_options("opt =0", "x-opt\0opt\0noopt\0x-noopt\0", 0, 0, NULL, NULL, "", NULL); -+ do_fstab_filter_options(" opt ", "opt\0x-opt\0", 0, 0, NULL, NULL, "", NULL); - - /* check function with NULL args */ -- do_fstab_filter_options(NULL, "opt\0", 0, NULL, NULL, ""); -- do_fstab_filter_options("", "opt\0", 0, NULL, NULL, ""); -+ do_fstab_filter_options(NULL, "opt\0", 0, 0, NULL, NULL, "", ""); -+ do_fstab_filter_options("", "opt\0", 0, 0, NULL, NULL, "", ""); - - /* unnecessary comma separators */ -- do_fstab_filter_options("opt=x,,,,", "opt\0", 1, "opt", "x", ""); -- do_fstab_filter_options(",,,opt=x,,,,", "opt\0", 1, "opt", "x", ""); -+ do_fstab_filter_options("opt=x,,,,", "opt\0", 1, 1, "opt", "x", "x", ""); -+ do_fstab_filter_options(",,,opt=x,,,,", "opt\0", 1, 1, "opt", "x", "x", ""); - - /* escaped characters */ -- do_fstab_filter_options("opt1=\\\\,opt2=\\xff", "opt1\0", 1, "opt1", "\\", "opt2=\\xff"); -- do_fstab_filter_options("opt1=\\\\,opt2=\\xff", "opt2\0", 1, "opt2", "\\xff", "opt1=\\"); -+ do_fstab_filter_options("opt1=\\\\,opt2=\\xff", "opt1\0", 1, 1, "opt1", "\\", "\\", "opt2=\\xff"); -+ do_fstab_filter_options("opt1=\\\\,opt2=\\xff", "opt2\0", 1, 1, "opt2", "\\xff", "\\xff", "opt1=\\"); - } - - static void test_fstab_find_pri(void) { - -From d6cef552dcb4764a89269ce9603eb21f348d911a Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= -Date: Thu, 11 Mar 2021 11:10:32 +0100 -Subject: [PATCH 9/9] fstab-generator: get rid of fstab_extract_values() - -This was a parallel implementation of option parsing that didn't -support escaping of separators. Let's port this over to the common code. - -Fixes #18952. ---- - src/fstab-generator/fstab-generator.c | 14 ++++++------- - src/shared/fstab-util.c | 29 --------------------------- - src/shared/fstab-util.h | 2 -- - 3 files changed, 7 insertions(+), 38 deletions(-) - -diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c -index b454a5980d4..8c1087a9a33 100644 ---- a/src/fstab-generator/fstab-generator.c -+++ b/src/fstab-generator/fstab-generator.c -@@ -241,7 +241,7 @@ static int write_dependency( - assert(f); - assert(opts); - -- r = fstab_extract_values(opts, filter, &names); -+ r = fstab_filter_options(opts, filter, NULL, NULL, &names, NULL); - if (r < 0) - return log_warning_errno(r, "Failed to parse options: %m"); - if (r == 0) -@@ -274,17 +274,17 @@ static int write_dependency( - - static int write_after(FILE *f, const char *opts) { - return write_dependency(f, opts, -- "x-systemd.after", "After=%1$s\n"); -+ "x-systemd.after\0", "After=%1$s\n"); - } - - static int write_requires_after(FILE *f, const char *opts) { - return write_dependency(f, opts, -- "x-systemd.requires", "After=%1$s\nRequires=%1$s\n"); -+ "x-systemd.requires\0", "After=%1$s\nRequires=%1$s\n"); - } - - static int write_before(FILE *f, const char *opts) { - return write_dependency(f, opts, -- "x-systemd.before", "Before=%1$s\n"); -+ "x-systemd.before\0", "Before=%1$s\n"); - } - - static int write_requires_mounts_for(FILE *f, const char *opts) { -@@ -295,7 +295,7 @@ static int write_requires_mounts_for(FILE *f, const char *opts) { - assert(f); - assert(opts); - -- r = fstab_extract_values(opts, "x-systemd.requires-mounts-for", &paths); -+ r = fstab_filter_options(opts, "x-systemd.requires-mounts-for\0", NULL, NULL, &paths, NULL); - if (r < 0) - return log_warning_errno(r, "Failed to parse options: %m"); - if (r == 0) -@@ -376,11 +376,11 @@ static int add_mount( - mount_point_ignore(where)) - return 0; - -- r = fstab_extract_values(opts, "x-systemd.wanted-by", &wanted_by); -+ r = fstab_filter_options(opts, "x-systemd.wanted-by\0", NULL, NULL, &wanted_by, NULL); - if (r < 0) - return r; - -- r = fstab_extract_values(opts, "x-systemd.required-by", &required_by); -+ r = fstab_filter_options(opts, "x-systemd.required-by\0", NULL, NULL, &required_by, NULL); - if (r < 0) - return r; - -diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c -index 6674ed4a19f..7fd3d9c2c34 100644 ---- a/src/shared/fstab-util.c -+++ b/src/shared/fstab-util.c -@@ -210,35 +210,6 @@ int fstab_filter_options( - return !!namefound; - } - --int fstab_extract_values(const char *opts, const char *name, char ***values) { -- _cleanup_strv_free_ char **optsv = NULL, **res = NULL; -- char **s; -- -- assert(opts); -- assert(name); -- assert(values); -- -- optsv = strv_split(opts, ","); -- if (!optsv) -- return -ENOMEM; -- -- STRV_FOREACH(s, optsv) { -- char *arg; -- int r; -- -- arg = startswith(*s, name); -- if (!arg || *arg != '=') -- continue; -- r = strv_extend(&res, arg + 1); -- if (r < 0) -- return r; -- } -- -- *values = TAKE_PTR(res); -- -- return !!*values; --} -- - int fstab_find_pri(const char *options, int *ret) { - _cleanup_free_ char *opt = NULL; - int r, pri; -diff --git a/src/shared/fstab-util.h b/src/shared/fstab-util.h -index 97f40221afb..6b596baafa1 100644 ---- a/src/shared/fstab-util.h -+++ b/src/shared/fstab-util.h -@@ -18,8 +18,6 @@ int fstab_filter_options( - char ***ret_values, - char **ret_filtered); - --int fstab_extract_values(const char *opts, const char *name, char ***values); -- - static inline bool fstab_test_option(const char *opts, const char *names) { - return !!fstab_filter_options(opts, names, NULL, NULL, NULL, NULL); - } diff --git a/SOURCES/fa67d9c0d652dc41574b546f542909e9c8157237.patch b/SOURCES/fa67d9c0d652dc41574b546f542909e9c8157237.patch deleted file mode 100644 index 51d09b1..0000000 --- a/SOURCES/fa67d9c0d652dc41574b546f542909e9c8157237.patch +++ /dev/null @@ -1,49 +0,0 @@ -From fa67d9c0d652dc41574b546f542909e9c8157237 Mon Sep 17 00:00:00 2001 -From: Lennart Poettering -Date: Tue, 12 Jan 2021 21:36:54 +0100 -Subject: [PATCH] extract-word: don't rely on C's downgrade-to-bool feature for - chars - -The `quote` char variable ectually contains a character, not a pointer -or boolean. hence do an explicit comparison rather than rely on C's -downgrade to bool feature, as per our coding style. ---- - src/basic/extract-word.c | 9 ++++----- - 1 file changed, 4 insertions(+), 5 deletions(-) - -diff --git a/src/basic/extract-word.c b/src/basic/extract-word.c -index 76b3fe12e3b..4104dac9a74 100644 ---- a/src/basic/extract-word.c -+++ b/src/basic/extract-word.c -@@ -20,11 +20,10 @@ - int extract_first_word(const char **p, char **ret, const char *separators, ExtractFlags flags) { - _cleanup_free_ char *s = NULL; - size_t allocated = 0, sz = 0; -- char c; -- int r; -- - char quote = 0; /* 0 or ' or " */ - bool backslash = false; /* whether we've just seen a backslash */ -+ char c; -+ int r; - - assert(p); - assert(ret); -@@ -71,7 +70,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra - - if (c == 0) { - if ((flags & EXTRACT_CUNESCAPE_RELAX) && -- (!quote || flags & EXTRACT_RELAX)) { -+ (quote == 0 || flags & EXTRACT_RELAX)) { - /* If we find an unquoted trailing backslash and we're in - * EXTRACT_CUNESCAPE_RELAX mode, keep it verbatim in the - * output. -@@ -116,7 +115,7 @@ int extract_first_word(const char **p, char **ret, const char *separators, Extra - - backslash = false; - -- } else if (quote) { /* inside either single or double quotes */ -+ } else if (quote != 0) { /* inside either single or double quotes */ - for (;; (*p)++, c = **p) { - if (c == 0) { - if (flags & EXTRACT_RELAX) diff --git a/SOURCES/split-files.py b/SOURCES/split-files.py index f3e3aa6..46c4982 100644 --- a/SOURCES/split-files.py +++ b/SOURCES/split-files.py @@ -21,8 +21,11 @@ o_pam = open('.file-list-pam', 'w') o_rpm_macros = open('.file-list-rpm-macros', 'w') o_devel = open('.file-list-devel', 'w') o_container = open('.file-list-container', 'w') +o_oomd_defaults = open('.file-list-oomd-defaults', 'w') o_remote = open('.file-list-remote', 'w') o_tests = open('.file-list-tests', 'w') +o_standalone_tmpfiles = open('.file-list-standalone-tmpfiles', 'w') +o_standalone_sysusers = open('.file-list-standalone-sysusers', 'w') o_rest = open('.file-list-rest', 'w') for file in files(buildroot): n = file.path[1:] @@ -51,10 +54,10 @@ for file in files(buildroot): o = o_pam elif '/rpm/' in n: o = o_rpm_macros - elif re.search(r'/lib.*\.pc|/man3/|/usr/include|(?. # The contents of this are an example to be copied into systemd.spec. # -# Minimum rpm version supported: 4.13.0 +# Minimum rpm version supported: 4.14.0 %transfiletriggerin -P 900900 -- /usr/lib/systemd/system /etc/systemd/system # This script will run after any package is initially installed or # upgraded. We care about the case where a package is initially # installed, because other cases are covered by the *un scriptlets, # so sometimes we will reload needlessly. -if test -d /run/systemd/system; then - %{_bindir}/systemctl daemon-reload +if test -d "/run/systemd/system"; then + %{_bindir}/systemctl daemon-reload || : + %{_bindir}/systemctl reload-or-restart --marked || : fi -%transfiletriggerun -- /usr/lib/systemd/system /etc/systemd/system +%transfiletriggerpostun -P 1000100 -- /usr/lib/systemd/system /etc/systemd/system # On removal, we need to run daemon-reload after any units have been -# removed. %transfiletriggerpostun would be ideal, but it does not get -# executed for some reason. +# removed. # On upgrade, we need to run daemon-reload after any new unit files # have been installed, but before %postun scripts in packages get -# executed. %transfiletriggerun gets the right list of files -# but it is invoked too early (before changes happen). -# %filetriggerpostun happens at the right time, but it fires for -# every package. -# To execute the reload at the right time, we create a state -# file in %transfiletriggerun and execute the daemon-reload in -# the first %filetriggerpostun. - +# executed. if test -d "/run/systemd/system"; then - mkdir -p "%{_localstatedir}/lib/rpm-state/systemd" - touch "%{_localstatedir}/lib/rpm-state/systemd/needs-reload" + %{_bindir}/systemctl daemon-reload || : fi -%filetriggerpostun -P 1000100 -- /usr/lib/systemd/system /etc/systemd/system -if test -f "%{_localstatedir}/lib/rpm-state/systemd/needs-reload"; then - rm -rf "%{_localstatedir}/lib/rpm-state/systemd" - %{_bindir}/systemctl daemon-reload +%transfiletriggerpostun -P 10000 -- /usr/lib/systemd/system /etc/systemd/system +# We restart remaining services that should be restarted here. +if test -d "/run/systemd/system"; then + %{_bindir}/systemctl reload-or-restart --marked || : fi -%transfiletriggerin -P 100700 -- /usr/lib/sysusers.d +%transfiletriggerin -P 1000700 -- /usr/lib/sysusers.d # This script will process files installed in /usr/lib/sysusers.d to create # specified users automatically. The priority is set such that it # will run before the tmpfiles file trigger. -if test -d /run/systemd/system; then +if test -d "/run/systemd/system"; then %{_bindir}/systemd-sysusers || : fi -%transfiletriggerin -P 100500 -- /usr/lib/tmpfiles.d -# This script will process files installed in /usr/lib/tmpfiles.d to create -# tmpfiles automatically. The priority is set such that it will run -# after the sysusers file trigger, but before any other triggers. -if test -d /run/systemd/system; then - %{_bindir}/systemd-tmpfiles --create || : -fi - -%transfiletriggerin udev -- /usr/lib/udev/hwdb.d +%transfiletriggerin -P 1000700 udev -- /usr/lib/udev/hwdb.d # This script will automatically invoke hwdb update if files have been # installed or updated in /usr/lib/udev/hwdb.d. -if test -d /run/systemd/system; then +if test -d "/run/systemd/system"; then %{_bindir}/systemd-hwdb update || : fi -%transfiletriggerin -- /usr/lib/systemd/catalog +%transfiletriggerin -P 1000700 -- /usr/lib/systemd/catalog # This script will automatically invoke journal catalog update if files # have been installed or updated in /usr/lib/systemd/catalog. -if test -d /run/systemd/system; then +if test -d "/run/systemd/system"; then %{_bindir}/journalctl --update-catalog || : fi -%transfiletriggerin udev -- /usr/lib/udev/rules.d +%transfiletriggerin -P 1000700 -- /usr/lib/binfmt.d +# This script will automatically apply binfmt rules if files have been +# installed or updated in /usr/lib/binfmt.d. +if test -d "/run/systemd/system"; then + # systemd-binfmt might fail if binfmt_misc kernel module is not loaded + # during install + /usr/lib/systemd/systemd-binfmt || : +fi + +%transfiletriggerin -P 1000600 -- /usr/lib/tmpfiles.d +# This script will process files installed in /usr/lib/tmpfiles.d to create +# tmpfiles automatically. The priority is set such that it will run +# after the sysusers file trigger, but before any other triggers. +if test -d "/run/systemd/system"; then + %{_bindir}/systemd-tmpfiles --create || : +fi + +%transfiletriggerin -P 1000600 udev -- /usr/lib/udev/rules.d # This script will automatically update udev with new rules if files # have been installed or updated in /usr/lib/udev/rules.d. if test -e /run/udev/control; then %{_bindir}/udevadm control --reload || : fi -%transfiletriggerin -- /usr/lib/sysctl.d +%transfiletriggerin -P 1000500 -- /usr/lib/sysctl.d # This script will automatically apply sysctl rules if files have been # installed or updated in /usr/lib/sysctl.d. -if test -d /run/systemd/system; then +if test -d "/run/systemd/system"; then /usr/lib/systemd/systemd-sysctl || : fi - -%transfiletriggerin -- /usr/lib/binfmt.d -# This script will automatically apply binfmt rules if files have been -# installed or updated in /usr/lib/binfmt.d. -if test -d /run/systemd/system; then - # systemd-binfmt might fail if binfmt_misc kernel module is not loaded - # during install - /usr/lib/systemd/systemd-binfmt || : -fi diff --git a/SPECS/systemd.spec b/SPECS/systemd.spec index c6bb199..86da224 100644 --- a/SPECS/systemd.spec +++ b/SPECS/systemd.spec @@ -1,4 +1,4 @@ -#global commit 551dd873b0bdfb9e7e47431b2933c8b910228f0c +#global commit c4b843473a75fb38ed5bf54e9d3cfb1cb3719efa %{?commit:%global shortcommit %(c=%{commit}; echo ${c:0:7})} %global stable 1 @@ -25,8 +25,8 @@ Name: systemd Url: https://www.freedesktop.org/wiki/Software/systemd -Version: 247.3 -Release: 10%{?dist} +Version: 248.2 +Release: 1.1%{?dist} # For a breakdown of the licensing, see README License: LGPLv2+ and MIT and GPLv2+ Summary: System and Service Manager @@ -74,28 +74,25 @@ GIT_DIR=../../src/systemd/.git git diffab -M v233..master@{2017-06-15} -- hwdb/[ %endif # Backports of patches from upstream (0000–0499) -# 18211: Fixes ExecCondition= dependency bug -Patch0000: https://github.com/systemd/systemd/pull/18211.patch -# 17872: Fixes using PrivateUsers=yes with other sandboxing properties -Patch0001: https://github.com/systemd/systemd/pull/17872.patch -# Commit to make #18955 apply cleanly -Patch0004: https://github.com/systemd/systemd/commit/fa67d9c0d652dc41574b546f542909e9c8157237.patch -#18955: Fixes fstab parsing -Patch0005: https://github.com/systemd/systemd/pull/18955.patch +# +# Any patches which are "in preparation" upstream should be listed +# here, rather than in the next section. Packit CI will drop any +# patches in this range before applying upstream pull requests. %if 0%{?facebook} -# 17495: Fixes BPF pinning post-coldplug -Patch0101: https://github.com/systemd/systemd/pull/17495.patch -# 18886: systemd-shutdown logs to /dev/console not stderr -Patch0102: https://github.com/systemd/systemd/pull/18886.patch -#18621: FB variant of quieting "proc: Bad value for 'hidepid'" messages -Patch0103: 18621-fb.patch +# PR 13496: Extend bpf cgroup program support +Patch0100: 13496-fb.patch +# PR 18621: FB variant of quieting "proc: Bad value for 'hidepid'" messages +Patch0101: 18621-fb.patch +# PR 17495: Fixes BPF pinning post-coldplug +Patch0102: 17495-rebased.patch %else -#18621: Quiet "proc: Bad value for 'hidepid'" messages -Patch0103: https://github.com/systemd/systemd/pull/18621.patch +# PR 18621: Quiet "proc: Bad value for 'hidepid'" messages +Patch0101: https://github.com/systemd/systemd/pull/18621.patch %endif # Downstream-only patches (0500–9999) + # https://github.com/systemd/systemd/pull/17050 Patch0501: https://github.com/systemd/systemd/pull/17050/commits/f58b96d3e8d1cb0dd3666bc74fa673918b586612.patch # Downgrade sysv-generator messages from warning to debug @@ -142,6 +139,9 @@ BuildRequires: qrencode-devel BuildRequires: libmicrohttpd-devel BuildRequires: libxkbcommon-devel BuildRequires: iptables-devel +BuildRequires: pkgconfig(tss2-esys) +BuildRequires: pkgconfig(tss2-rc) +BuildRequires: pkgconfig(tss2-mu) BuildRequires: libxslt BuildRequires: docbook-style-xsl BuildRequires: pkgconfig @@ -176,6 +176,7 @@ Requires: dbus >= 1.9.18 Requires: %{name}-pam = %{version}-%{release} Requires: %{name}-rpm-macros = %{version}-%{release} Requires: %{name}-libs = %{version}-%{release} +%{?fedora:Recommends: %{name}-networkd = %{version}-%{release}} Recommends: diffutils Requires: util-linux Recommends: libxkbcommon%{?_isa} @@ -442,6 +443,7 @@ CONFIGURE_OPTS=( -Defi=true -Dgnu-efi=%{?have_gnu_efi:true}%{?!have_gnu_efi:false} -Dtpm=true + -Dtpm2=true -Dhwdb=true -Dsysusers=true -Ddefault-kill-user-processes=false @@ -462,12 +464,18 @@ CONFIGURE_OPTS=( -Db_ndebug=false -Dman=true -Dversion-tag=v%{version}-%{release} - -Ddocdir=%{_pkgdocdir} +%if 0%{?fedora} -Dfallback-hostname=fedora +%else + -Dfallback-hostname=localhost +%endif -Ddefault-dnssec=no # https://bugzilla.redhat.com/show_bug.cgi?id=1867830 -Ddefault-mdns=no -Ddefault-llmnr=resolve + -Doomd=true + # Need to set this for CentOS build + -Ddocdir=%{_pkgdocdir} # CentOS is missing newer deps required to include these # But also these aren't as relevant for the hyperscale use case -Dp11kit=false @@ -501,6 +509,14 @@ CONFIGURE_OPTS+=( export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8 %meson "${CONFIGURE_OPTS[@]}" + +new_triggers=%{_vpath_builddir}/src/rpm/triggers.systemd.sh +if ! diff -u %{SOURCE1} ${new_triggers}; then + echo -e "\n\n\nWARNING: triggers.systemd in Source1 is different!" + echo -e " cp $PWD/${new_triggers} %{SOURCE1}\n\n\n" + sleep 5 +fi + %meson_build %if %{with selinux} @@ -618,11 +634,11 @@ python3 %{SOURCE2} %buildroot </dev/null || useradd -r -u 192 -l -g systemd-net getent group systemd-resolve &>/dev/null || groupadd -r -g 193 systemd-resolve 2>&1 || : getent passwd systemd-resolve &>/dev/null || useradd -r -u 193 -l -g systemd-resolve -d / -s /sbin/nologin -c "systemd Resolver" systemd-resolve &>/dev/null || : +getent group systemd-oom &>/dev/null || groupadd -r systemd-oom 2>&1 || : +getent passwd systemd-oom &>/dev/null || useradd -r -l -g systemd-oom -d / -s /sbin/nologin -c "systemd Userspace OOM Killer" systemd-oom &>/dev/null || : + %post systemd-machine-id-setup &>/dev/null || : @@ -703,13 +722,13 @@ systemctl daemon-reexec &>/dev/null || { fi } -journalctl --update-catalog &>/dev/null || : -systemd-tmpfiles --create &>/dev/null || : +if [ $1 -eq 1 ]; then + # create /var/log/journal only on initial installation, + # and only if it's writable (it won't be in rpm-ostree). + [ -w %{_localstatedir} ] && mkdir -p %{_localstatedir}/log/journal -# create /var/log/journal only on initial installation, -# and only if it's writable (it won't be in rpm-ostree). -if [ $1 -eq 1 ] && [ -w %{_localstatedir} ]; then - mkdir -p %{_localstatedir}/log/journal + [ -w %{_localstatedir} ] && journalctl --update-catalog || : + systemd-tmpfiles --create &>/dev/null || : fi # Make sure new journal files will be owned by the "systemd-journal" group @@ -751,21 +770,6 @@ if test -d /run/systemd/system/ && ln -fsv ../run/systemd/resolve/stub-resolv.conf /etc/resolv.conf fi -%preun -if [ $1 -eq 0 ] ; then - systemctl disable --quiet \ - remote-fs.target \ - getty@.service \ - serial-getty@.service \ - console-getty.service \ - debug-shell.service \ - systemd-networkd.service \ - systemd-networkd-wait-online.service \ - systemd-resolved.service \ - systemd-homed.service \ - >/dev/null || : -fi - %post libs %{?ldconfig} @@ -844,9 +848,9 @@ grep -q -E '^KEYMAP="?fi-latin[19]"?' /etc/vconsole.conf 2>/dev/null && %systemd_preun %udev_services %postun udev -# Only restart systemd-udev, to run the upgraded dameon. +# Restart some services. # Others are either oneshot services, or sockets, and restarting them causes issues (#1378974) -%systemd_postun_with_restart systemd-udevd.service +%systemd_postun_with_restart systemd-udevd.service systemd-timesyncd.service %pre journal-remote getent group systemd-journal-remote &>/dev/null || groupadd -r systemd-journal-remote 2>&1 || : @@ -854,7 +858,6 @@ getent passwd systemd-journal-remote &>/dev/null || useradd -r -l -g systemd-jou %post journal-remote %systemd_post systemd-journal-gatewayd.socket systemd-journal-gatewayd.service systemd-journal-remote.socket systemd-journal-remote.service systemd-journal-upload.service -%firewalld_reload %preun journal-remote %systemd_preun systemd-journal-gatewayd.socket systemd-journal-gatewayd.service systemd-journal-remote.socket systemd-journal-remote.service systemd-journal-upload.service @@ -935,6 +938,20 @@ fi %endif %changelog +* Mon May 10 2021 Anita Zhang - 248.2-1.1 +- New release for 248 +- Drop patches merged in 248.2 +- FB only backport PR #13496 (Extend bpf cgroup program support) + +* Fri May 7 2021 Zbigniew Jędrzejewski-Szmek - 248.2-1 +- Pull in some more patches from upstream (#1944646, #1885090, #1941340) +- Adjust modes of some %%ghost files (#1956059) + +* Thu May 6 2021 Zbigniew Jędrzejewski-Szmek - 248.1-1 +- Latest stable version: a long list of minor correctness fixes all around + (#1955475, #911766, #1958167, #1952919) +- Enable tpm2-tss dependency (#1949505) + * Wed Apr 14 2021 Anita Zhang - 247.3-10 - Remove systemd-resolved enablement @@ -942,6 +959,9 @@ fi - Reenable LTO now that binutils has been fixed - Update FB configure options +* Tue Apr 6 2021 Adam Williamson - 248-2 +- Re-enable resolved caching, we hope all major bugs are resolved now + * Thu Apr 1 2021 Davide Cavalca - 247.3-8 - Backport https://github.com/SELinuxProject/refpolicy/pull/308 to fix systemd-hostnamed and systemd-localed when SELinux is enabled. @@ -949,6 +969,13 @@ fi * Thu Apr 1 2021 Anita Zhang - 247.3-7 - Downgrade sysv-generator warning even more (to debug) +* Wed Mar 31 2021 Zbigniew Jędrzejewski-Szmek - 248-1 +- Latest upstream release, see + https://github.com/systemd/systemd/blob/v248/NEWS. +- The changes since -rc4 are rather small, various fixes all over the place. + A fix to how systemd-oomd selects a candidate to kill, and more debug logging + to make this more transparent. + * Wed Mar 31 2021 Anita Zhang - 247.3-6 - Backport PR#18621 (Ignore attempts at hidepid and subset for older kernels) - Downgrade sysv-generator warning about missing native systemd unit @@ -956,18 +983,75 @@ fi * Wed Mar 31 2021 Davide Cavalca - 247.3-5 - Add selinux subpackage +* Tue Mar 30 2021 Anita Zhang - 248~rc4-6 +- Increase oomd user memory pressure limit to 50% (#1941170) + +* Fri Mar 26 2021 Zbigniew Jędrzejewski-Szmek - 248~rc4-5 +- Do not preset systemd-networkd.service and systemd-networkd-wait-online.service + on upgrades from before systemd-networkd was split out (#1943263) +- In nsswitch.conf, move nss-myhostname to the front, before nss-mdns4 (#1943199) + +* Wed Mar 24 2021 Zbigniew Jędrzejewski-Szmek - 248~rc4-4 +- Revert patch that seems to cause problems with dns resolution + (see comments on https://bodhi.fedoraproject.org/updates/FEDORA-2021-1c1a870ceb) + +* Mon Mar 22 2021 Zbigniew Jędrzejewski-Szmek - 248~rc4-3 +- Fix hang when processing timers during DST switch in Europe/Dublin timezone (#1941335) +- Fix returning combined IPv4/IPv6 responses from systemd-resolved cache (#1940715) + (But note that the disablement of caching added previously is + retained until we can do more testing.) +- Minor fix to interface naming by udev +- Fix for systemd-repart --size + +* Fri Mar 19 2021 Adam Williamson - 248~rc4-2 +- Disable resolved cache via config snippet (#1940715) + +* Thu Mar 18 2021 Yu Watanabe - 248~rc4-1 +- Latest upstream prerelease, see + https://github.com/systemd/systemd/blob/v248-rc4/NEWS. +- A bunch of documentation updates, and correctness fixes. + * Wed Mar 17 2021 Anita Zhang - 247.3-4 - Backport PR #18955 (Fixes fstab parsing) - FB only backport PR #18886 (systemd-shutdown logs to /dev/console not stderr) - Reenable tests by disabling LTO (work around binutils bug) +* Tue Mar 16 2021 Adam Williamson - 248~rc3-2 +- Backport PR #19009 to fix CNAME redirect resolving some more (#1933433) + +* Thu Mar 11 2021 Zbigniew Jędrzejewski-Szmek - 248~rc3-1 +- Latest upstream prerelease, see + https://github.com/systemd/systemd/blob/v248-rc3/NEWS. +- A bunch of documentation updates, correctness fixes, and systemd-networkd + features. +- Resolves #1933137, #1935084, #1933873, #1931181, #1933335, #1935062, #1927148. + +* Thu Mar 11 2021 Zbigniew Jędrzejewski-Szmek - 248~rc2-5 +- Fix crash in pid1 during daemon-reexec (#1931034) + +* Fri Mar 05 2021 Adam Williamson - 248~rc2-3 +- Fix stub resolver CNAME chain resolving (#1933433) + +* Mon Mar 01 2021 Josh Boyer - 248~rc2-2 +- Don't set the fallback hostname to Fedora on non-Fedora OSes + * Wed Feb 24 2021 Davide Cavalca - 247.3-3 - Remove careinversion usage to make the package usable on older mock versions +* Tue Feb 23 2021 Zbigniew Jędrzejewski-Szmek - 248~rc2-1 +- Latest upstream prelease, just a bunch of small fixes. +- Fixes #1931957. + +* Tue Feb 23 2021 Zbigniew Jędrzejewski-Szmek - 248~rc1-2 +- Rebuild with the newest scriptlets + * Fri Feb 19 2021 Davide Cavalca - 247.3-2 - Disable some tests to workaround a binutils bug triggered by enabling audit - Refresh patches +* Wed Feb 17 2021 Michel Alexandre Salim - 247.3-3 +- Increase oomd user memory pressure limit to 10% (#1929856) + * Wed Feb 17 2021 Anita Zhang - 247.3-1 - New release for 247 - Backport PR #18211 (Fixes ExecCondition= dependency bug) @@ -983,6 +1067,13 @@ fi - Drop no longer needed FB FusionIO patch - Temporarily disable audit support while debugging a link issue +* Fri Feb 5 2021 Anita Zhang - 247.3-2 +- Changes for https://fedoraproject.org/wiki/Changes/EnableSystemdOomd. +- Backports consist primarily of PR #18361, #18444, and #18401 (plus some + additional ones to handle merge conflicts). +- Create systemd-oomd-defaults subpackage to install unit drop-ins that will + configure systemd-oomd to monitor and act. + * Tue Feb 2 2021 Zbigniew Jędrzejewski-Szmek - 247.3-1 - Minor stable release - Fixes #1895937, #1813219, #1903106.