revert changes to apply with system where kernel is 4.19
Make a Github release / release (push) Skipped

This commit is contained in:
2026-09-26 19:04:39 +03:00
parent 4ed4b428ba
commit b5d8617b03
25 changed files with 600 additions and 76 deletions
+80
View File
@@ -0,0 +1,80 @@
# ANCIENT-MOD: running on old kernels (target: 4.19)
Upstream requires kernel ≥ 5.10 (see `README`, "REQUIREMENTS") and dropped the fallback code for
older kernels. This branch restores or adds fallbacks so the system can at least boot on 4.19.
Code touched by this effort is marked with `ANCIENT-MOD` comments where practical.
Legend: **revert** = reverse-applied an upstream commit (possibly adjusted), **new** = new code
because upstream code was refactored since the fallback was removed.
## Boot-critical
| Area | Kernel feature | Change | Where |
|---|---|---|---|
| Mount point checks (`is_mount_point_at()`, `path_is_mount_point()`), used by early `mount_setup()` for `/proc`, `/sys`, `/dev` with `MNT_FATAL` | `STATX_ATTR_MOUNT_ROOT` (5.8) | **new**: `xstatx_full()` emulates the attribute when it is required but missing: compares the mount id (from `/proc/self/fdinfo`) of the inode with its parent's (`..` for directories, parent path for others); same inode as parent ⇒ root ⇒ mount point. Replaces upstream fallback removed in `a98a6eb95c`. | `src/basic/stat-util.c` (`xstatx_emulate()`) |
| `chase()` (used everywhere), `path_get_mnt_id_at()`, `fds_inode_and_mount_same()`, `XSTATX_MNT_ID_BEST` | `STATX_MNT_ID` (5.8/5.10) | **new**: same emulation fills `stx_mnt_id` from `/proc/self/fdinfo/<fd>` `mnt_id:` (available since 3.15). Replaces removals in `7ecfa87c56`, `e5a3127339`. | `src/basic/stat-util.c` |
| cgroup2 mount | `memory_recursiveprot` (5.7) | **revert** (adjusted) of `80c5b63db5`: option is probed again instead of always passed; if it cannot be probed because `fsopen()` is missing (< 5.2) it is not used. | `src/shared/mount-setup.c` |
| `mount_option_supported()` | `fsopen()` (5.2) | **revert** of part of `c2198d0c3f`: return `-EAGAIN` ("don't know") when the new mount API is unavailable. | `src/basic/mountpoint-util.c` |
| `close_all_fds*()` (every fork/exec, `systemd-executor`) | `close_range()` (5.9) | **revert** of `c471cca093` and `d58c826c54`: fall back to iterating `/proc/self/fd` (or the frugal loop). | `src/basic/fd-util.c`, `src/basic/fd-util.h`, `src/test/test-fd-util.c` |
| `pidref_set_pid()` (every process PID1 tracks, `safe_fork()`, `posix_spawn_wrapper()`) | `pidfd_open()` (5.3) | **revert** (adjusted) of part of `6e14c46bac`: ENOSYS ⇒ PidRef without fd (plain PID). | `src/basic/pidref.c` |
| `pidref_wait_for_terminate_full()` with timeout | pidfd (5.3) | **new**: without pidfd poll `waitid(P_PID, …, WNOHANG)` every 10 ms instead of returning `-ENOMEDIUM`. | `src/basic/pidref.c` |
| `pidfd_check_pidfs()`, `pidfd_info_mask_is_supported()`, `pidfd_get_inode_id_self_cached()` | pidfd (5.3) | **revert**/new: ENOSYS ⇒ "not supported" instead of hard error. | `src/basic/pidfd-util.c` |
| sd-event child sources (`sd_event_add_child()`, used by udevd, logind, …) | pidfd (5.3), `waitid(P_PIDFD)` (5.4) | **new** (upstream removed the non-pidfd path in `6e14c46bac` and later made the code pidfd-only): if `pidfd_open()` is unsupported the source works on the PID via `waitid(P_PID)` + SIGCHLD signalfd. SIGCHLD is blocked automatically in that case (upstream no longer requires callers to block it). `send_child_signal()` falls back to `kill()`/`rt_sigqueueinfo()`; `set_child_pidfd_own()` returns `-EOPNOTSUPP` without pidfd. | `src/libsystemd/sd-event/sd-event.c` |
| `pam_systemd` session creation | pidfd (5.3) | **revert** of part of `6e14c46bac`: fall back to `CreateSession` (PID based). | `src/login/pam_systemd.c` |
| Unit cgroup-empty tracking (`unit_check_cgroup_events()`) | `frozen` key in `cgroup.events` (5.2) | **new**: if the key is missing, read only `populated`. Without this every cgroup event read failed and stopped units were never noticed as empty. | `src/core/cgroup.c` |
| Freeze/thaw | cgroup v2 freezer (5.2) | **revert** of `3c86a935ad`: `cg_freezer_supported()` restored; freeze is `-EOPNOTSUPP` / no-op when `cgroup.freeze` is absent. | `src/basic/cgroup-util.[ch]`, `src/core/cgroup.c`, `src/core/unit.c` |
| Credentials (`LoadCredential=`, `SetCredential=`, `ImportCredential=` — used by many stock units) | new mount API `fsopen`/`fsmount`/`move_mount` (5.2) | **new**: if the credentials fs cannot be created because the API is missing, use the existing "plain directory" fallback (previously only used on EPERM). | `src/core/exec-credential.c` |
| `ProtectProc=` / `ProcSubset=` (used by logind, networkd, timesyncd, hostnamed, …) | per-instance procfs, textual `hidepid=`, `subset=` (5.8) | **new**: on kernels < 5.8 the options are ignored (they would either fail with EINVAL or, worse, change the host's shared `/proc` superblock). `/proc/sys` & friends are then still protected by the normal ProtectKernel* mounts. Upstream removed the old fallback in `0cba5bdcf1`. | `src/core/namespace.c` |
| `RootDirectory=` | `open_tree()` (5.2) | **new**: tolerate ENOSYS when cloning the root directory mount and use the classic bind mount path. | `src/core/namespace.c` |
| `futimens_opath()`, `access_fd()` | `AT_EMPTY_PATH` for `utimensat()`/`faccessat()` (5.8) | **revert** of `52bf232e82` (manually): fall back to `/proc/self/fd/<fd>`. | `src/basic/fs-util.c` |
## Networking
| Area | Kernel feature | Change | Where |
|---|---|---|---|
| `socket_bind_to_ifindex()` (DHCP client, resolved, networkd, …) | `SO_BINDTOIFINDEX` (5.1) | **revert** of `ddbbc98bd5` (manually): fall back to `SO_BINDTODEVICE`. | `src/basic/socket-util.c` |
| `rtnl_resolve_ifname_full()` (alternative names) | `IFLA_ALT_IFNAME` lookup (5.5) | **revert** of `42b09813f4`: treat EINVAL like ENODEV. | `src/libsystemd/sd-netlink/netlink-util.c` |
| networkd startup enumeration | nexthop (5.3) | **revert** of `034b69d8b8` (manually): ignore `-EOPNOTSUPP` from nexthop enumeration, otherwise networkd refuses to start. | `src/network/networkd-manager.c` |
| networkd permanent MAC address | `IFLA_PERM_ADDRESS` (5.6) | **revert** of `04f05d9bd7`: fall back to ethtool. | `src/network/networkd-link.c` |
| IPv6 DNAT (nspawn port forwarding / firewall-util) | nft concatenation fix (5.8) | **revert** of `58fb46ecd5` (manually): `-EOVERFLOW` ⇒ `-EOPNOTSUPP`. | `src/shared/firewall-util.c` |
## Other
| Area | Kernel feature | Change | Where |
|---|---|---|---|
| `SurviveFinalKillSignal=` | `user.*` xattrs on cgroupfs (5.7) | **revert** of `8292b859f3`: fall back to `trusted.survive_final_kill_signal`. | `src/core/cgroup.c`, `src/shared/killall.c` |
| Loop devices via `LOOP_SET_STATUS64` fallback | buffer cache invalidation bug (fixed in 5.0) | **revert** of `5e3887e6c1`: issue `BLKFLSBUF` when offset/size is set. | `src/shared/loop-util.c` |
## Known remaining gaps (not addressed)
- **cgroup v1 is gone upstream**: the kernel must boot with a cgroup v2 hierarchy. 4.19's cgroup v2
lacks the `cpuset` (5.0) and `freezer` (5.2) controllers, `cgroup.kill` (5.14, already graceful).
- **PSI** (4.20): `systemd-oomd` does not work; memory-pressure event sources are already optional.
- **BPF**: features needing BPF links / BPF LSM / bounded loops (5.3–5.7) — `RestrictFileSystems=`,
`RestrictNetworkInterfaces=`, `SocketBind*=`, `BindNetworkInterface=`, … — report "not supported"
(upstream probing, `ef12eadfc8`).
- **New mount API without fallback** (5.2/5.12): `PrivateBPF=`, `ExtensionImages=`/`ExtensionDirectories=`
(overlay via `fsopen`), `MountImages=`, runtime `systemctl bind`/`mount-image`, idmapped mounts,
mountfsd, nsresourced, sysext/confext, homed (also needs fscrypt v2 / 5.4), coredump's mount of
the crashed process' root.
- **`MS_NOSYMFOLLOW`** (5.10) is passed as a legacy `mount(2)` flag; older kernels silently ignore
unknown flags, so the mount just lacks `nosymfollow`.
- **Credentials** use a plain directory instead of a dedicated (noswap) tmpfs, i.e. less isolation.
- **Per-device IO weight** (`IODeviceWeight=`, BFQ per-device weight, 5.4): upstream dropped the
EINVAL workaround (`00c4851af0`), expect warnings when used.
- **udev net_id** for IP-over-InfiniBand (`dev_port`, 4.20): `d3d375a686` not reverted; multi-port
IPoIB devices may get colliding names.
- `test-event`'s `test_pidfd` inherently requires pidfds and fails without them.
- Anything else newer than 5.10 listed in `README` as "recommended baseline" is expected to fall
back gracefully upstream, but that has not been verified on a real old kernel.
## Testing done
- Full native test suite (`meson test -C build`) on a 7.x kernel.
- Selected tests (`test-stat-util`, `test-mountpoint-util`, `test-chase`, `test-fs-util`,
`test-fd-util`, `test-pidref`, `test-process-util`, `test-socket-util`, `test-mount-util`,
`test-udev-spawn`, `test-async`, `test-event` except `test_pidfd`) and `systemd --test --system`
(with the built generators) under an `LD_PRELOAD` shim that simulates an old kernel: strips
`STATX_MNT_ID*`/`STATX_ATTR_MOUNT_ROOT` from `statx()` results and makes `pidfd_open`,
`pidfd_send_signal`, `pidfd_spawn`, `close_range` and the new mount API return ENOSYS.
- **Not** booted on a real 4.19 kernel yet.
+17
View File
@@ -206,6 +206,23 @@ bool cg_kill_supported(void) {
return (supported = false);
}
/* ANCIENT-MOD: cgroup v2 freezer is available since kernel 5.2 */
bool cg_freezer_supported(void) {
static thread_local int supported = -1;
if (supported >= 0)
return supported;
if (cg_is_available() <= 0)
return (supported = false);
if (access("/sys/fs/cgroup/init.scope/cgroup.freeze", F_OK) >= 0)
return (supported = true);
if (errno != ENOENT)
log_debug_errno(errno, "Failed to check whether cgroup freezer is available, assuming not: %m");
return (supported = false);
}
int cg_enumerate_subgroups(const char *path, DIR **ret) {
_cleanup_free_ char *fs = NULL;
DIR *d;
+1
View File
@@ -235,6 +235,7 @@ int cg_mask_from_string(const char *s, CGroupMask *ret);
int cg_mask_to_string(CGroupMask mask, char **ret);
bool cg_kill_supported(void);
bool cg_freezer_supported(void);
DECLARE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController);
+80 -16
View File
@@ -260,9 +260,10 @@ int close_all_fds_frugal(const int except[], size_t n_except) {
assert(except || n_except == 0);
/* This is the inner fallback core of close_all_fds(). This never calls malloc() or so and hence is
* safe to be called in signal handler context. Most users should call close_all_fds(), but when we
* assume we are called from signal handler context, then use this simpler call instead. */
/* This is the inner fallback core of close_all_fds(). This never calls malloc() or opendir() or so
* and hence is safe to be called in signal handler context. Most users should call close_all_fds(),
* but when we assume we are called from signal handler context, then use this simpler call
* instead. */
max_fd = get_max_fd();
if (max_fd < 0)
@@ -288,6 +289,44 @@ int close_all_fds_frugal(const int except[], size_t n_except) {
return r;
}
int close_all_fds_by_proc(const int except[], size_t n_except) {
_cleanup_closedir_ DIR *d = NULL;
int r = 0;
d = opendir("/proc/self/fd");
if (!d)
return close_all_fds_frugal(except, n_except); /* ultimate fallback if /proc/ is not available */
FOREACH_DIRENT(de, d, return -errno) {
int fd = -EBADF, q;
if (!IN_SET(de->d_type, DT_LNK, DT_UNKNOWN))
continue;
fd = parse_fd(de->d_name);
if (fd < 0)
/* Let's better ignore this, just in case */
continue;
if (fd < 3)
continue;
if (fd == dirfd(d))
continue;
if (fd_in_set(fd, except, n_except))
continue;
q = close_nointr(fd);
if (q != -EBADF) /* Valgrind has its own FD and doesn't want to have it closed */
RET_GATHER(r, q);
}
return r;
}
static bool have_close_range = true; /* Assume we live in the future */
static int close_all_fds_special_case(const int except[], size_t n_except) {
assert(n_except == 0 || except);
@@ -295,6 +334,9 @@ static int close_all_fds_special_case(const int except[], size_t n_except) {
* nicely, since we won't need sorting for them. Returns > 0 if the special casing worked, 0
* otherwise. */
if (!have_close_range)
return 0;
if (n_except == 1 && except[0] < 0) /* Minor optimization: if we only got one fd, and it's invalid,
* we got none */
n_except = 0;
@@ -303,22 +345,31 @@ static int close_all_fds_special_case(const int except[], size_t n_except) {
case 0:
/* Close everything. Yay! */
if (close_range(3, INT_MAX, 0) < 0)
return -errno;
return 1;
if (close_range(3, INT_MAX, 0) >= 0)
return 1;
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) {
have_close_range = false;
return 0;
}
return -errno;
case 1:
/* Close all but exactly one, then we don't need no sorting. This is a pretty common
* case, hence let's handle it specially. */
if (except[0] > 3 && close_range(3, except[0] - 1, 0) < 0)
return -errno;
if ((except[0] <= 3 || close_range(3, except[0]-1, 0) >= 0) &&
(except[0] >= INT_MAX || close_range(MAX(3, except[0]+1), -1, 0) >= 0))
return 1;
if (except[0] < INT_MAX && close_range(MAX(3, except[0] + 1), -1, 0) < 0)
return -errno;
if (ERRNO_IS_NOT_SUPPORTED(errno) || ERRNO_IS_PRIVILEGE(errno)) {
have_close_range = false;
return 0;
}
return 1;
return -errno;
default:
return 0;
@@ -350,6 +401,9 @@ int close_all_fds(const int except[], size_t n_except) {
if (r > 0) /* special case worked! */
return 0;
if (!have_close_range)
return close_all_fds_by_proc(except, n_except);
_cleanup_free_ int *sorted_malloc = NULL;
size_t n_sorted;
int *sorted;
@@ -369,7 +423,7 @@ int close_all_fds(const int except[], size_t n_except) {
sorted = newa(int, n_sorted);
if (!sorted) /* Fallback on OOM. */
return close_all_fds_frugal(except, n_except);
return close_all_fds_by_proc(except, n_except);
memcpy(sorted, except, n_except * sizeof(int));
@@ -391,8 +445,13 @@ int close_all_fds(const int except[], size_t n_except) {
continue;
/* Close everything between the start and end fds (both of which shall stay open) */
if (close_range(start + 1, end - 1, 0) < 0)
return -errno;
if (close_range(start + 1, end - 1, 0) < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
have_close_range = false;
return close_all_fds_by_proc(except, n_except);
}
}
/* The loop succeeded. Let's now close everything beyond the end */
@@ -400,8 +459,13 @@ int close_all_fds(const int except[], size_t n_except) {
if (sorted[n_sorted-1] >= INT_MAX) /* Dont let the addition below overflow */
return 0;
if (close_range(sorted[n_sorted-1] + 1, INT_MAX, 0) < 0)
return -errno;
if (close_range(sorted[n_sorted-1] + 1, INT_MAX, 0) < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
have_close_range = false;
return close_all_fds_by_proc(except, n_except);
}
return 0;
}
+1
View File
@@ -127,6 +127,7 @@ int get_max_fd(void);
int close_all_fds(const int except[], size_t n_except);
int close_all_fds_without_malloc(const int except[], size_t n_except);
int close_all_fds_by_proc(const int except[], size_t n_except);
int close_all_fds_frugal(const int except[], size_t n_except);
int pack_fds(int fds[], size_t n);
+28 -2
View File
@@ -322,7 +322,20 @@ int futimens_opath(int fd, const struct timespec ts[2]) {
assert(fd >= 0);
return RET_NERRNO(utimensat(fd, "", ts, AT_EMPTY_PATH));
if (utimensat(fd, "", ts, AT_EMPTY_PATH) >= 0)
return 0;
if (errno != EINVAL)
return -errno;
/* ANCIENT-MOD: AT_EMPTY_PATH is supported since kernel 5.8, fall back to /proc/self/fd/ */
if (utimensat(AT_FDCWD, FORMAT_PROC_FD_PATH(fd), ts, /* flags= */ 0) < 0) {
if (errno != ENOENT)
return -errno;
return proc_fd_enoent_errno();
}
return 0;
}
int stat_warn_permissions(const char *path, const struct stat *st) {
@@ -371,7 +384,20 @@ int access_fd(int fd, int mode) {
assert(fd >= 0);
return RET_NERRNO(faccessat(fd, "", mode, AT_EMPTY_PATH));
if (faccessat(fd, "", mode, AT_EMPTY_PATH) >= 0)
return 0;
if (errno != EINVAL)
return -errno;
/* ANCIENT-MOD: AT_EMPTY_PATH is supported since kernel 5.8, fall back to /proc/self/fd/ */
if (access(FORMAT_PROC_FD_PATH(fd), mode) < 0) {
if (errno != ENOENT)
return -errno;
return proc_fd_enoent_errno();
}
return 0;
}
int touch_fd(int fd, usec_t stamp) {
+2
View File
@@ -681,6 +681,8 @@ int mount_option_supported(const char *fstype, const char *key, const char *valu
if (fd < 0) {
if (errno == ENODEV)
return log_debug_errno(errno, "Kernel does not support file system type '%s'.", fstype);
if (ERRNO_IS_NOT_SUPPORTED(errno))
return -EAGAIN; /* ANCIENT-MOD: new mount API not available (kernel < 5.2) → don't know */
return log_debug_errno(errno, "Failed to open superblock context for '%s': %m", fstype);
}
+10 -2
View File
@@ -28,8 +28,11 @@ int pidfd_check_pidfs(int pid_fd) {
_cleanup_close_ int our_fd = -EBADF;
if (pid_fd < 0) {
our_fd = pidfd_open(getpid_cached(), /* flags= */ 0);
if (our_fd < 0)
if (our_fd < 0) {
if (ERRNO_IS_NOT_SUPPORTED(errno)) /* ANCIENT-MOD: kernel < 5.3 */
return (have_pidfs = false);
return -errno;
}
pid_fd = our_fd;
}
@@ -102,6 +105,8 @@ int pidfd_info_mask_is_supported(uint64_t mask) {
if (cached == 0) {
_cleanup_close_ int pidfd = r = RET_NERRNO(pidfd_open(getpid_cached(), /* flags= */ 0));
if (ERRNO_IS_NEG_NOT_SUPPORTED(r)) /* ANCIENT-MOD: kernel < 5.3 */
return (cached = -EOPNOTSUPP);
if (r < 0)
return r;
@@ -334,8 +339,11 @@ int pidfd_get_inode_id_self_cached(uint64_t *ret) {
return initialized;
_cleanup_close_ int fd = pidfd_open(getpid_cached(), 0);
if (fd < 0)
if (fd < 0) {
if (ERRNO_IS_NOT_SUPPORTED(errno)) /* ANCIENT-MOD: kernel < 5.3 */
return (initialized = -EOPNOTSUPP);
return -errno;
}
r = pidfd_get_inode_id(fd, &cached);
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
+27 -8
View File
@@ -106,9 +106,10 @@ int pidref_set_pid_full(PidRef *pidref, pid_t pid, unsigned flags) {
fd = pidfd_open(pid, flags);
if (fd < 0) {
/* Graceful fallback in case the kernel is out of fds.
/* Graceful fallback in case the kernel is out of fds or lacks pidfd_open() (ANCIENT-MOD: < 5.3).
* The PIDFD_THREAD flag is supported since kernel v6.9 (64bef697d33b75fc06c5789b3f8108680271529f). */
if (!(ERRNO_IS_RESOURCE(errno) || (errno == EINVAL && FLAGS_SET(flags, PIDFD_THREAD))))
if (!(ERRNO_IS_RESOURCE(errno) || ERRNO_IS_NOT_SUPPORTED(errno) ||
(errno == EINVAL && FLAGS_SET(flags, PIDFD_THREAD))))
return log_debug_errno(errno, "Failed to open pidfd for pid " PID_FMT ": %m", pid);
fd = -EBADF;
@@ -468,16 +469,34 @@ int pidref_wait_for_terminate_full(PidRef *pidref, usec_t timeout, siginfo_t *re
if (pidref->pid == 1 || pidref_is_self(pidref))
return -ECHILD;
if (pidref->fd < 0 && (timeout != USEC_INFINITY || fiber_ops_is_set()))
return -ENOMEDIUM;
usec_t ts = timeout == USEC_INFINITY ? USEC_INFINITY : usec_add(now(CLOCK_MONOTONIC), timeout);
/* ANCIENT-MOD: no pidfd (kernel < 5.3), poll with waitid(WNOHANG) to honour the timeout */
if (pidref->fd < 0 && ts != USEC_INFINITY)
for (;;) {
siginfo_t si = {};
r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, WEXITED|WNOHANG));
if (r == -EINTR)
continue;
if (r < 0)
return r;
if (si.si_pid != 0) {
if (ret_si)
*ret_si = si;
return 0;
}
if (now(CLOCK_MONOTONIC) >= ts)
return -ETIMEDOUT;
(void) usleep_safe(10 * USEC_PER_MSEC);
}
/* Poll the pidfd before waitid() if either there's a finite timeout (so we can honor it) or
* we're on a fiber (so fd_wait_for_event() can suspend us instead of blocking the event loop
* inside waitid()). Otherwise let waitid() block directly. The precondition above guarantees
* pidref->fd >= 0 in both cases. */
bool poll_first = ts != USEC_INFINITY || fiber_ops_is_set();
* inside waitid()). Otherwise let waitid() block directly. */
bool poll_first = pidref->fd >= 0 && (ts != USEC_INFINITY || fiber_ops_is_set());
for (;;) {
if (poll_first) {
+13 -1
View File
@@ -1419,13 +1419,25 @@ int socket_bind_to_ifname(int fd, const char *ifname) {
}
int socket_bind_to_ifindex(int fd, int ifindex) {
char ifname[IF_NAMESIZE];
int r;
assert(fd >= 0);
if (ifindex <= 0)
/* Drop binding */
return RET_NERRNO(setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0));
return setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
r = setsockopt_int(fd, SOL_SOCKET, SO_BINDTOIFINDEX, ifindex);
if (r != -ENOPROTOOPT)
return r;
/* ANCIENT-MOD: fall back to SO_BINDTODEVICE on kernels < 5.1 which didn't have SO_BINDTOIFINDEX */
r = format_ifname(ifindex, ifname);
if (r < 0)
return r;
return socket_bind_to_ifname(fd, ifname);
}
int socket_autobind(int fd, char **ret_name) {
+111
View File
@@ -11,14 +11,17 @@
#include "dirent-util.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "filesystems.h"
#include "fs-util.h"
#include "hash-funcs.h"
#include "log.h"
#include "mountpoint-util.h"
#include "parse-util.h"
#include "path-util.h"
#include "siphash24.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-util.h"
#include "time-util.h"
@@ -395,6 +398,102 @@ static const char* statx_attribute_to_name(uint64_t attr);
DEFINE_STATX_BITS_TO_STRING(statx_mask, unsigned, statx_mask_one_to_name, "0x%x");
DEFINE_STATX_BITS_TO_STRING(statx_attributes, uint64_t, statx_attribute_to_name, "0x%" PRIx64);
/* ANCIENT-MOD: emulation of STATX_MNT_ID and STATX_ATTR_MOUNT_ROOT for kernels < 5.8 */
static int fd_fdinfo_mnt_id(int fd, uint64_t *ret) {
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
_cleanup_free_ char *p = NULL;
int r;
assert(fd >= 0);
assert(ret);
xsprintf(path, "/proc/self/fdinfo/%i", fd);
r = get_proc_field(path, "mnt_id", &p);
if (r == -ENOENT)
return proc_fd_enoent_errno();
if (r == -ENODATA)
return -EUNATCH;
if (r < 0)
return r;
return safe_atou64(p, ret);
}
static int fd_open_parent(int fd, const struct statx *sx) {
_cleanup_free_ char *p = NULL, *d = NULL;
int r;
if (FLAGS_SET(sx->stx_mask, STATX_TYPE) ? S_ISDIR(sx->stx_mode) : fd_verify_directory(fd) >= 0)
return RET_NERRNO(openat(fd, "..", O_PATH|O_DIRECTORY|O_CLOEXEC));
r = fd_get_path(fd, &p);
if (r < 0)
return r;
r = path_extract_directory(p, &d);
if (r < 0)
return r;
return RET_NERRNO(open(d, O_PATH|O_DIRECTORY|O_CLOEXEC));
}
static int xstatx_emulate(
int fd,
const char *path,
int statx_flags,
bool want_mnt_id,
bool want_mount_root,
struct statx *sx) {
_cleanup_close_ int target_fd = -EBADF, parent_fd = -EBADF;
uint64_t mnt_id, parent_mnt_id;
int r;
assert(sx);
if (isempty(path) && fd >= 0)
target_fd = RET_NERRNO(fcntl(fd, F_DUPFD_CLOEXEC, 3));
else
target_fd = RET_NERRNO(openat(fd, isempty(path) ? "." : path,
O_PATH|O_CLOEXEC|(FLAGS_SET(statx_flags, AT_SYMLINK_NOFOLLOW) ? O_NOFOLLOW : 0)));
if (target_fd < 0)
return target_fd;
r = fd_fdinfo_mnt_id(target_fd, &mnt_id);
if (r < 0)
return r;
if (want_mnt_id) {
sx->stx_mnt_id = mnt_id;
sx->stx_mask |= STATX_MNT_ID;
}
if (!want_mount_root)
return 0;
bool is_root = false;
parent_fd = fd_open_parent(target_fd, sx);
if (parent_fd < 0)
log_debug_errno(parent_fd, "Failed to open parent to determine mount root, assuming not a mount root: %m");
else {
struct stat a, b;
r = fd_fdinfo_mnt_id(parent_fd, &parent_mnt_id);
if (r < 0)
return r;
if (mnt_id != parent_mnt_id)
is_root = true;
else if (fstat(target_fd, &a) >= 0 && fstat(parent_fd, &b) >= 0)
is_root = stat_inode_same(&a, &b);
}
SET_FLAG(sx->stx_attributes, STATX_ATTR_MOUNT_ROOT, is_root);
sx->stx_attributes_mask |= STATX_ATTR_MOUNT_ROOT;
return 0;
}
int xstatx_full(int fd,
const char *path,
int statx_flags,
@@ -439,6 +538,18 @@ int xstatx_full(int fd,
&sx) < 0)
return negative_errno();
bool want_mnt_id =
(FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST) && !(sx.stx_mask & (STATX_MNT_ID|STATX_MNT_ID_UNIQUE))) ||
(FLAGS_SET(request_mask, STATX_MNT_ID) && !FLAGS_SET(sx.stx_mask, STATX_MNT_ID));
bool want_mount_root =
FLAGS_SET(mandatory_attributes, STATX_ATTR_MOUNT_ROOT) &&
!FLAGS_SET(sx.stx_attributes_mask, STATX_ATTR_MOUNT_ROOT);
if (want_mnt_id || want_mount_root) {
r = xstatx_emulate(fd, path, statx_flags, want_mnt_id, want_mount_root, &sx);
if (r < 0)
log_debug_errno(r, "Failed to emulate statx() mount fields, ignoring: %m");
}
if (FLAGS_SET(xstatx_flags, XSTATX_MNT_ID_BEST) &&
!(sx.stx_mask & (STATX_MNT_ID|STATX_MNT_ID_UNIQUE)))
return log_debug_errno(SYNTHETIC_ERRNO(EUNATCH), "statx() did not return either STATX_MNT_ID or STATX_MNT_ID_UNIQUE.");
+41 -4
View File
@@ -937,12 +937,39 @@ static void cgroup_delegate_xattr_apply(Unit *u) {
}
static void cgroup_survive_xattr_apply(Unit *u) {
int r;
assert(u);
if (u->survive_final_kill_signal)
unit_set_xattr_graceful(u, "user.survive_final_kill_signal", "1", 1);
else
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (!crt)
return;
if (u->survive_final_kill_signal) {
r = cg_set_xattr(
crt->cgroup_path,
"user.survive_final_kill_signal",
"1",
1,
/* flags= */ 0);
/* user xattr support was added in kernel v5.7 */
if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
r = cg_set_xattr(
crt->cgroup_path,
"trusted.survive_final_kill_signal",
"1",
1,
/* flags= */ 0);
if (r < 0)
log_unit_debug_errno(u,
r,
"Failed to set 'survive_final_kill_signal' xattr on control "
"group %s, ignoring: %m",
empty_to_root(crt->cgroup_path));
} else {
unit_remove_xattr_graceful(u, "user.survive_final_kill_signal");
unit_remove_xattr_graceful(u, "trusted.survive_final_kill_signal");
}
}
static void cgroup_xattr_apply(Unit *u) {
@@ -3189,6 +3216,13 @@ static int unit_check_cgroup_events(Unit *u) {
"cgroup.events",
STRV_MAKE("populated", "frozen"),
values);
if (r == -ENXIO)
/* ANCIENT-MOD: the "frozen" key exists only since kernel 5.2 (cgroup v2 freezer) */
r = cg_get_keyed_attribute(
crt->cgroup_path,
"cgroup.events",
STRV_MAKE("populated"),
values);
if (r < 0)
return r;
@@ -3203,7 +3237,7 @@ static int unit_check_cgroup_events(Unit *u) {
/* Disregard freezer state changes due to operations not initiated by us.
* See: https://github.com/systemd/systemd/pull/13512/files#r416469963 and
* https://github.com/systemd/systemd/pull/13512#issuecomment-573007207 */
if (IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_FREEZING_BY_PARENT, FREEZER_THAWING))
if (values[1] && IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_FREEZING_BY_PARENT, FREEZER_THAWING))
unit_freezer_complete(u, streq(values[1], "0") ? FREEZER_RUNNING : FREEZER_FROZEN);
free_many_charp(values, ELEMENTSOF(values));
@@ -4122,6 +4156,9 @@ int unit_cgroup_freezer_action(Unit *u, FreezerAction action) {
assert(action >= 0);
assert(action < _FREEZER_ACTION_MAX);
if (!cg_freezer_supported())
return 0;
unit_next_freezer_state(u, action, &next, &objective);
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
+3 -2
View File
@@ -1052,8 +1052,9 @@ static int setup_credentials_internal(
}
mfd = fsmount_credentials_fs(&fs_fd);
if (ERRNO_IS_NEG_PRIVILEGE(mfd) && !dir_mounted) {
log_debug_errno(mfd, "Lacking privilege to mount credentials fs, falling back to plain directory.");
if ((ERRNO_IS_NEG_PRIVILEGE(mfd) || ERRNO_IS_NEG_NOT_SUPPORTED(mfd)) && !dir_mounted) {
/* ANCIENT-MOD: also fall back if the new mount API is unavailable (kernel < 5.2) */
log_debug_errno(mfd, "Lacking privilege or kernel support to mount credentials fs, falling back to plain directory.");
return setup_credentials_plain_dir(context, cred_dir);
}
if (mfd < 0)
+28 -4
View File
@@ -7,6 +7,7 @@
#include <stdlib.h>
#include <sys/file.h>
#include <sys/mount.h>
#include <sys/utsname.h>
#include <unistd.h>
#include "alloc-util.h"
@@ -1562,6 +1563,25 @@ static int mount_private_cgroup2fs(const MountEntry *m, const NamespaceParameter
return mount_private_apivfs("cgroup2", mount_entry_path(m), "/sys/fs/cgroup", /* opts= */ NULL, p->runtime_scope);
}
/* ANCIENT-MOD: before kernel 5.8 procfs options are per pid namespace (shared with the host /proc) and
* textual hidepid= / subset= do not exist, hence ignore ProtectProc=/ProcSubset= there. */
static bool procfs_per_instance_options_supported(void) {
static int cached = -1;
struct utsname uts;
if (cached >= 0)
return cached;
if (uname(&uts) < 0)
return (cached = true);
cached = strverscmp_improved(uts.release, "5.8") >= 0;
if (!cached)
log_debug("Kernel %s does not support per-instance procfs options, ignoring ProtectProc=/ProcSubset=.", uts.release);
return cached;
}
static int mount_procfs(const MountEntry *m, const NamespaceParameters *p) {
_cleanup_free_ char *opts = NULL;
int r;
@@ -1569,8 +1589,9 @@ static int mount_procfs(const MountEntry *m, const NamespaceParameters *p) {
assert(m);
assert(p);
if (p->protect_proc != PROTECT_PROC_DEFAULT ||
p->proc_subset != PROC_SUBSET_ALL) {
if ((p->protect_proc != PROTECT_PROC_DEFAULT ||
p->proc_subset != PROC_SUBSET_ALL) &&
procfs_per_instance_options_supported()) {
opts = strjoin("hidepid=",
p->protect_proc == PROTECT_PROC_DEFAULT ? "off" : protect_proc_to_string(p->protect_proc));
@@ -2663,7 +2684,9 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
* as well now. */
.propagation = MS_SLAVE,
});
if (_root_mount_fd < 0 && !ERRNO_IS_NEG_PRIVILEGE(_root_mount_fd) && _root_mount_fd != -EINVAL)
/* ANCIENT-MOD: also tolerate missing open_tree() (kernel < 5.2) */
if (_root_mount_fd < 0 && !ERRNO_IS_NEG_PRIVILEGE(_root_mount_fd) &&
!ERRNO_IS_NEG_NOT_SUPPORTED(_root_mount_fd) && _root_mount_fd != -EINVAL)
return log_debug_errno(_root_mount_fd, "Failed to clone specified directory: %m");
root_mount_fd = _root_mount_fd;
@@ -2866,7 +2889,8 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
/* In case /proc is successfully mounted with pid tree subset only (ProcSubset=pid), the protective
mounts to non-pid /proc paths would fail. But the pid only option may have failed gracefully, so
let's try the mounts but it's not fatal if they don't succeed. */
bool ignore_protect_proc = p->ignore_protect_paths || p->proc_subset == PROC_SUBSET_PID;
bool ignore_protect_proc = p->ignore_protect_paths ||
(p->proc_subset == PROC_SUBSET_PID && procfs_per_instance_options_supported());
if (p->protect_kernel_tunables) {
r = append_static_mounts(&ml,
protect_kernel_tunables_proc_table,
+1 -1
View File
@@ -6654,7 +6654,7 @@ int unit_freezer_action(Unit *u, FreezerAction action) {
assert(u);
assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
if (!unit_can_freeze(u))
if (!cg_freezer_supported() || !unit_can_freeze(u))
return -EOPNOTSUPP;
if (u->job)
+46 -14
View File
@@ -3,6 +3,7 @@
#include <linux/magic.h>
#include <malloc.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <sys/timerfd.h>
#include <sys/wait.h>
#include <threads.h>
@@ -51,9 +52,20 @@ static bool EVENT_SOURCE_WATCH_PIDFD(const sd_event_source *s) {
/* Returns true if this is a PID event source and can be implemented by watching EPOLLIN */
return s &&
s->type == SOURCE_CHILD &&
s->child.pidfd >= 0 &&
(s->child.options & ~WNOWAIT) == WEXITED;
}
/* ANCIENT-MOD: child sources without pidfd (kernel < 5.3) fall back to waitid(P_PID) */
static int child_waitid(sd_event_source *s, siginfo_t *si, int options) {
assert(s);
if (s->child.pidfd >= 0)
return waitid(P_PIDFD, s->child.pidfd, si, options);
return waitid(P_PID, s->child.pid, si, options);
}
static bool event_source_is_online(sd_event_source *s) {
assert(s);
return s->enabled != SD_EVENT_OFF && !s->ratelimited;
@@ -1098,10 +1110,12 @@ static sd_event_source* source_free(sd_event_source *s) {
if (s->child.process_owned) {
assert(s->child.pid > 0);
assert(s->child.pidfd >= 0);
if (!s->child.exited) {
r = RET_NERRNO(pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0));
if (s->child.pidfd >= 0)
r = RET_NERRNO(pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0));
else
r = RET_NERRNO(kill(s->child.pid, SIGKILL));
if (r < 0 && r != -ESRCH)
log_debug_errno(r, "Failed to kill process " PID_FMT ", ignoring: %m",
s->child.pid);
@@ -1111,7 +1125,7 @@ static sd_event_source* source_free(sd_event_source *s) {
siginfo_t si = {};
/* Reap the child if we can */
(void) waitid(P_PIDFD, s->child.pidfd, &si, WEXITED);
(void) child_waitid(s, &si, WEXITED);
}
}
@@ -1648,10 +1662,22 @@ _public_ int sd_event_add_child(
* PID, and make regular waitid() handling race-free. */
s->child.pidfd = pidfd_open(pid, 0);
if (s->child.pidfd < 0)
return -errno;
if (s->child.pidfd < 0) {
if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
return -errno;
s->child.pidfd_owned = true; /* If we allocate the pidfd we own it by default */
/* Without pidfd we depend on SIGCHLD via signalfd, which only works if it is blocked */
r = signal_is_blocked(SIGCHLD);
if (r < 0)
return r;
if (r == 0) {
log_debug("pidfd_open() unavailable, blocking SIGCHLD to watch child processes.");
r = sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD);
if (r < 0)
return r;
}
} else
s->child.pidfd_owned = true; /* If we allocate the pidfd we own it by default */
s->wakeup = WAKEUP_EVENT_SOURCE;
s->child.options = options;
@@ -3264,7 +3290,6 @@ _public_ int sd_event_source_send_child_signal(sd_event_source *s, int sig, cons
assert_return(s->type == SOURCE_CHILD, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD);
assert_return(SIGNAL_VALID(sig), -EINVAL);
assert(s->child.pidfd >= 0);
/* If we already have seen indication the process exited refuse sending a signal early. */
if (s->child.exited)
@@ -3276,14 +3301,20 @@ _public_ int sd_event_source_send_child_signal(sd_event_source *s, int sig, cons
if (si)
copy = *si;
return RET_NERRNO(pidfd_send_signal(s->child.pidfd, sig, si ? &copy : NULL, flags));
if (s->child.pidfd >= 0)
return RET_NERRNO(pidfd_send_signal(s->child.pidfd, sig, si ? &copy : NULL, flags));
if (flags != 0)
return -EOPNOTSUPP;
if (si)
return RET_NERRNO(syscall(SYS_rt_sigqueueinfo, s->child.pid, sig, &copy));
return RET_NERRNO(kill(s->child.pid, sig));
}
_public_ int sd_event_source_get_child_pidfd_own(sd_event_source *s) {
assert_return(s, -EINVAL);
assert_return(s->type == SOURCE_CHILD, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD);
assert(s->child.pidfd >= 0);
return s->child.pidfd_owned;
}
@@ -3292,7 +3323,9 @@ _public_ int sd_event_source_set_child_pidfd_own(sd_event_source *s, int own) {
assert_return(s, -EINVAL);
assert_return(s->type == SOURCE_CHILD, -EDOM);
assert_return(!event_origin_changed(s->event), -ECHILD);
assert(s->child.pidfd >= 0);
if (s->child.pidfd < 0)
return -EOPNOTSUPP;
s->child.pidfd_owned = own;
return 0;
@@ -3764,7 +3797,6 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
HASHMAP_FOREACH(s, e->child_sources) {
assert(s->type == SOURCE_CHILD);
assert(s->child.pidfd >= 0);
if (s->priority > threshold)
continue;
@@ -3784,7 +3816,7 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
continue;
zero(s->child.siginfo);
if (waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo,
if (child_waitid(s, &s->child.siginfo,
WNOHANG | (s->child.options & WEXITED ? WNOWAIT : 0) | (s->child.options & ~WNOWAIT)) < 0)
return negative_errno();
@@ -3798,7 +3830,7 @@ static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priori
* from the queue, since there's no benefit in leaving it queued. */
assert(s->child.options & (WSTOPPED|WCONTINUED));
(void) waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
(void) child_waitid(s, &s->child.siginfo, WNOHANG|(s->child.options & (WSTOPPED|WCONTINUED)));
}
r = source_set_pending(s, true);
@@ -4281,7 +4313,7 @@ static int source_dispatch(sd_event_source *s) {
/* Now, reap the PID for good (unless WNOWAIT was specified by the caller). */
if (zombie) {
(void) waitid(P_PIDFD, s->child.pidfd, &s->child.siginfo, WNOHANG|WEXITED|(s->child.options & WNOWAIT));
(void) child_waitid(s, &s->child.siginfo, WNOHANG|WEXITED|(s->child.options & WNOWAIT));
if (!FLAGS_SET(s->child.options, WNOWAIT))
s->child.waited = true;
}
+2 -1
View File
@@ -117,7 +117,8 @@ int rtnl_resolve_ifname_full(
r = sd_netlink_call(*rtnl, message, 0, &reply);
if (r >= 0)
return parse_newlink_message(reply, ret_name, ret_altnames);
if (r != -ENODEV)
/* The kernels older than 76c9ac0ee878f6693d398d3a95ccaf85e1f597a6 (v5.5) return -EINVAL. */
if (!IN_SET(r, -ENODEV, -EINVAL))
return r;
}
+1 -1
View File
@@ -866,7 +866,7 @@ static int create_session_message(
if (!avoid_pidfd) {
pidfd = pidfd_open(getpid_cached(), 0);
if (pidfd < 0)
if (pidfd < 0 && !ERRNO_IS_NOT_SUPPORTED(errno)) /* ANCIENT-MOD: kernel < 5.3 */
return -errno;
}
+41 -4
View File
@@ -2448,6 +2448,38 @@ static int link_update_driver(Link *link, sd_netlink_message *message) {
return 1; /* needs reconfigure */
}
static int link_update_permanent_hardware_address_from_ethtool(Link *link, sd_netlink_message *message) {
int r;
assert(link);
assert(link->manager);
assert(message);
if (link->ethtool_permanent_hw_addr_read)
return 0;
/* When udevd is running, read the permanent hardware address after the interface is
* initialized by udevd. Otherwise, ethtool may not work correctly. See issue #22538.
* When udevd is not running, read the value when the interface is detected. */
if (udev_available() && !link->dev)
return 0;
/* If the interface does not have a hardware address, then it will not have a permanent address either. */
r = netlink_message_read_hw_addr(message, IFLA_ADDRESS, NULL);
if (r == -ENODATA)
return 0;
if (r < 0)
return log_link_debug_errno(link, r, "Failed to read IFLA_ADDRESS attribute: %m");
link->ethtool_permanent_hw_addr_read = true;
r = ethtool_get_permanent_hw_addr(&link->manager->ethtool_fd, link->ifname, &link->permanent_hw_addr);
if (r < 0)
log_link_debug_errno(link, r, "Permanent hardware address not found, continuing without: %m");
return 0;
}
static int link_update_permanent_hardware_address(Link *link, sd_netlink_message *message) {
int r;
@@ -2459,10 +2491,15 @@ static int link_update_permanent_hardware_address(Link *link, sd_netlink_message
return 0;
r = netlink_message_read_hw_addr(message, IFLA_PERM_ADDRESS, &link->permanent_hw_addr);
if (r == -ENODATA)
return 0;
if (r < 0)
return log_link_debug_errno(link, r, "Failed to read IFLA_PERM_ADDRESS attribute: %m");
if (r < 0) {
if (r != -ENODATA)
return log_link_debug_errno(link, r, "Failed to read IFLA_PERM_ADDRESS attribute: %m");
/* Fallback to ethtool for kernels older than v5.6 (f74877a5457d34d604dba6dbbb13c4c05bac8b93). */
r = link_update_permanent_hardware_address_from_ethtool(link, message);
if (r < 0)
return r;
}
if (link->permanent_hw_addr.length > 0)
log_link_debug(link, "Saved permanent hardware address: %s", HW_ADDR_TO_STR(&link->permanent_hw_addr));
+6 -1
View File
@@ -52,6 +52,7 @@
#include "networkd-wwan-bus.h"
#include "ordered-set.h"
#include "qdisc.h"
#include "selinux-util.h"
#include "set.h"
#include "stat-util.h"
#include "string-util.h"
@@ -1149,8 +1150,12 @@ int manager_enumerate(Manager *m) {
if (r < 0)
return log_error_errno(r, "Could not enumerate neighbors: %m");
/* ANCIENT-MOD: NextHop support is added in kernel v5.3 (65ee00a9409f751188a8cdc0988167858eb4a536),
* and older kernels return -EOPNOTSUPP, or -EINVAL if SELinux is enabled. */
r = manager_enumerate_nexthop(m);
if (r < 0)
if (r == -EOPNOTSUPP || (r == -EINVAL && mac_selinux_enforcing()))
log_debug_errno(r, "Could not enumerate nexthops, ignoring: %m");
else if (r < 0)
return log_error_errno(r, "Could not enumerate nexthops: %m");
r = manager_enumerate_routes(m);
+12
View File
@@ -1024,6 +1024,7 @@ static int fw_nftables_add_local_dnat_internal(
sd_netlink_message *messages[3] = {};
CLEANUP_ELEMENTS(messages, sd_netlink_message_unref_array_clear);
static bool ipv6_supported = true;
uint32_t data[5], key[2], dlen;
size_t msgcnt = 0;
int r;
@@ -1032,6 +1033,9 @@ static int fw_nftables_add_local_dnat_internal(
assert(add || !previous_remote);
assert(IN_SET(af, AF_INET, AF_INET6));
if (!ipv6_supported && af == AF_INET6)
return -EOPNOTSUPP;
if (!IN_SET(protocol, IPPROTO_TCP, IPPROTO_UDP))
return -EPROTONOSUPPORT;
@@ -1085,6 +1089,14 @@ static int fw_nftables_add_local_dnat_internal(
assert(msgcnt < ELEMENTSOF(messages));
r = sd_nfnl_call_batch(nfnl, messages, msgcnt, NFNL_DEFAULT_TIMEOUT_USECS);
if (r == -EOVERFLOW && af == AF_INET6) {
/* ANCIENT-MOD: The current implementation of DNAT in systemd requires kernel's
* fdb9c405e35bdc6e305b9b4e20ebc141ed14fc81 (v5.8), and the older kernel returns
* -EOVERFLOW. Let's treat the error as -EOPNOTSUPP. */
log_debug_errno(r, "The current implementation of IPv6 DNAT in systemd requires kernel 5.8 or newer, ignoring: %m");
ipv6_supported = false;
return -EOPNOTSUPP;
}
if (r < 0)
return r;
+3
View File
@@ -64,6 +64,9 @@ static bool is_in_survivor_cgroup(const PidRef *pid) {
}
r = cg_get_xattr_bool(cgroup_path, "user.survive_final_kill_signal");
/* user xattr support was added to kernel v5.7, try with the trusted namespace as a fallback */
if (ERRNO_IS_NEG_XATTR_ABSENT(r))
r = cg_get_xattr_bool(cgroup_path, "trusted.survive_final_kill_signal");
if (r < 0 && !ERRNO_IS_NEG_XATTR_ABSENT(r))
log_debug_errno(r,
"Failed to get survive_final_kill_signal xattr of %s, ignoring: %m",
+16
View File
@@ -5,6 +5,7 @@
#endif
#include <fcntl.h>
#include <linux/fs.h>
#include <linux/loop.h>
#include <sys/file.h>
#include <sys/ioctl.h>
@@ -200,6 +201,21 @@ static int loop_configure_fallback(int fd, const struct loop_config *c) {
random_u64_range(UINT64_C(240) * USEC_PER_MSEC * n_attempts/64));
}
/* Work around a kernel bug, where changing offset/size of the loopback device doesn't correctly
* invalidate the buffer cache. For details see:
*
* https://android.googlesource.com/platform/system/apex/+/bef74542fbbb4cd629793f4efee8e0053b360570
*
* This was fixed in kernel 5.0, see:
*
* https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5db470e229e22b7eda6e23b5566e532c96fb5bc3
*
* We'll run the work-around here in the legacy LOOP_SET_STATUS64 codepath. In the LOOP_CONFIGURE
* codepath above it should not be necessary. */
if (c->info.lo_offset != 0 || c->info.lo_sizelimit != 0)
if (ioctl(fd, BLKFLSBUF, 0) < 0)
log_debug_errno(errno, "Failed to issue BLKFLSBUF ioctl, ignoring: %m");
/* If a block size is requested then try to configure it. If that doesn't work, ignore errors, but
* afterwards, let's validate what is in effect, and if it doesn't match what we want, fail */
if (c->block_size != 0) {
+22 -13
View File
@@ -43,26 +43,35 @@ typedef struct MountPoint {
} MountPoint;
static int cgroupfs_mount_options(int priority, const char *type, char **ret) {
_cleanup_free_ char *opts = NULL;
int r;
assert(type);
assert(streq(type, "cgroup2"));
assert(ret);
/* memory_hugetlb_accounting mount option is since kernel v6.7 (8cba9576df601c384abd334a503c3f6e1e29eefb). */
/* ANCIENT-MOD: memory_recursiveprot is probed (kernel >= 5.7) instead of being always passed.
* memory_hugetlb_accounting mount option is since kernel v6.7 (8cba9576df601c384abd334a503c3f6e1e29eefb). */
r = mount_option_supported("cgroup2", "memory_hugetlb_accounting", /* value= */ NULL);
if (r <= 0) {
if (r < 0)
log_full_errno(priority, r, "Failed to determine whether cgroupfs supports 'memory_hugetlb_accounting' mount option, assuming not: %m");
else
log_debug("'memory_hugetlb_accounting' not supported by cgroupfs, not using mount option.");
*ret = NULL;
return 0;
FOREACH_STRING(o, "memory_recursiveprot", "memory_hugetlb_accounting") {
r = mount_option_supported("cgroup2", o, /* value= */ NULL);
if (r == -EAGAIN)
/* No fsopen(), i.e. kernel < 5.2, hence none of these options exist either */
log_debug("Cannot probe cgroupfs mount option '%s', kernel too old, not using it.", o);
else if (r < 0 && streq(o, "memory_recursiveprot")) {
log_full_errno(priority, r, "Failed to determine whether cgroupfs supports '%s' mount option, assuming it does: %m", o);
if (!strextend_with_separator(&opts, ",", o))
return log_oom_full(priority);
} else if (r < 0)
log_full_errno(priority, r, "Failed to determine whether cgroupfs supports '%s' mount option, assuming not: %m", o);
else if (r == 0)
log_debug("'%s' not supported by cgroupfs, not using mount option.", o);
else if (!strextend_with_separator(&opts, ",", o))
return log_oom_full(priority);
}
return strdup_to(ret, "memory_hugetlb_accounting");
*ret = TAKE_PTR(opts);
return 0;
}
int mount_cgroupfs(const char *path) {
@@ -82,7 +91,7 @@ int mount_cgroupfs(const char *path) {
return r;
/* These options shall be kept in sync with those in mount_table below. */
if (!strprepend_with_separator(&opts, ",", "nsdelegate,memory_recursiveprot"))
if (!strprepend_with_separator(&opts, ",", "nsdelegate"))
return log_oom();
return mount_nofollow_verbose(LOG_ERR, "cgroup2", path, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, opts);
@@ -200,7 +209,7 @@ static const MountPoint mount_table[] = {
.what = "cgroup2",
.where = "/sys/fs/cgroup",
.type = "cgroup2",
.options = "nsdelegate,memory_recursiveprot",
.options = "nsdelegate",
.options_fn = cgroupfs_mount_options,
.flags = MS_NOSUID|MS_NOEXEC|MS_NODEV,
.mode = MNT_FATAL|MNT_IN_CONTAINER|MNT_CHECK_WRITABLE,
+8 -2
View File
@@ -343,13 +343,19 @@ TEST(close_all_fds) {
_exit(EXIT_SUCCESS);
}
r = ASSERT_OK(pidref_safe_fork("(caf-nomalloc)", flags, NULL));
ASSERT_OK(r = pidref_safe_fork("(caf-nomalloc)", flags, NULL));
if (r == 0) {
test_close_all_fds_inner(close_all_fds_without_malloc);
_exit(EXIT_SUCCESS);
}
r = ASSERT_OK(pidref_safe_fork("(caf-frugal)", flags, NULL));
ASSERT_OK(r = pidref_safe_fork("(caf-proc)", flags, NULL));
if (r == 0) {
test_close_all_fds_inner(close_all_fds_by_proc);
_exit(EXIT_SUCCESS);
}
ASSERT_OK(r = pidref_safe_fork("(caf-frugal)", flags, NULL));
if (r == 0) {
test_close_all_fds_inner(close_all_fds_frugal);
_exit(EXIT_SUCCESS);