From 965cc99416a3b5a2a71127e9e6d2e4a95ab3c432 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 23 Apr 2020 16:36:06 +0200 Subject: [PATCH 1/5] shared: add common helper for unregistering all binfmt entries --- src/shared/binfmt-util.c | 33 +++++++++++++++++++++++++++++++++ src/shared/binfmt-util.h | 4 ++++ src/shared/meson.build | 2 ++ 3 files changed, 39 insertions(+) create mode 100644 src/shared/binfmt-util.c create mode 100644 src/shared/binfmt-util.h diff --git a/src/shared/binfmt-util.c b/src/shared/binfmt-util.c new file mode 100644 index 0000000000..0229726b42 --- /dev/null +++ b/src/shared/binfmt-util.c @@ -0,0 +1,33 @@ +#include +#include +#include + +#include "binfmt-util.h" +#include "fileio.h" +#include "missing_magic.h" +#include "stat-util.h" + +int disable_binfmt(void) { + int r; + + /* Flush out all rules. This is important during shutdown to cover for rules using "F", since those + * might pin a file and thus block us from unmounting stuff cleanly. + * + * We are a bit careful here, since binfmt_misc might still be an autofs which we don't want to + * trigger. */ + + r = path_is_fs_type("/proc/sys/fs/binfmt_misc", BINFMTFS_MAGIC); + if (r == 0 || r == -ENOENT) { + log_debug("binfmt_misc is not mounted, not detaching entries."); + return 0; + } + if (r < 0) + return log_warning_errno(r, "Failed to determine whether binfmt_misc is mounted: %m"); + + r = write_string_file("/proc/sys/fs/binfmt_misc/status", "-1", WRITE_STRING_FILE_DISABLE_BUFFER); + if (r < 0) + return log_warning_errno(r, "Failed to unregister binfmt_misc entries: %m"); + + log_debug("Unregistered all remaining binfmt_misc entries."); + return 0; +} diff --git a/src/shared/binfmt-util.h b/src/shared/binfmt-util.h new file mode 100644 index 0000000000..70feaad314 --- /dev/null +++ b/src/shared/binfmt-util.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +int disable_binfmt(void); diff --git a/src/shared/meson.build b/src/shared/meson.build index 483148492c..e608ea8a1d 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -12,6 +12,8 @@ shared_sources = files(''' barrier.h base-filesystem.c base-filesystem.h + binfmt-util.c + binfmt-util.h bitmap.c bitmap.h blkid-util.h From 0282c0285a3e3c2e409305ce28555a6ad0489539 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 23 Apr 2020 16:25:53 +0200 Subject: [PATCH 2/5] shutdown: unregister all binfmt_misc entries before entering shutdown loop Apparently if the new "F" flag is used they might pin files, which blocks us from unmounting things. Let's hence clear this up explicitly. Before entering our umount loop. Fixes: #14981 --- src/shutdown/shutdown.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c index f2ad23d055..be8ae75e04 100644 --- a/src/shutdown/shutdown.c +++ b/src/shutdown/shutdown.c @@ -16,6 +16,7 @@ #include "alloc-util.h" #include "async.h" +#include "binfmt-util.h" #include "cgroup-setup.h" #include "cgroup-util.h" #include "def.h" @@ -386,6 +387,7 @@ int main(int argc, char *argv[]) { sync_with_progress(); disable_coredumps(); + disable_binfmt(); log_info("Sending SIGTERM to remaining processes..."); broadcast_signal(SIGTERM, true, true, arg_timeout); From f3670df13e144c2f81bf6f9a0bea581e6d555bdd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 23 Apr 2020 16:27:46 +0200 Subject: [PATCH 3/5] binfmt: modernize code a bit Let's just copy out the bit of the string we need, and let's make sure we refuse rules called "status" and "register", since those are special files in binfmt_misc's file system. --- src/binfmt/binfmt.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c index 7ff844c78c..5e812e89a5 100644 --- a/src/binfmt/binfmt.c +++ b/src/binfmt/binfmt.c @@ -32,18 +32,17 @@ static int delete_rule(const char *rule) { assert(rule); assert(rule[0]); - x = strdup(rule); + e = strchrnul(rule + 1, rule[0]); + x = strndup(rule + 1, e - rule - 1); if (!x) return log_oom(); - e = strchrnul(x+1, x[0]); - *e = 0; - - if (!filename_is_valid(x + 1)) + if (!filename_is_valid(x) || + STR_IN_SET(x, "register", "status")) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Rule file name '%s' is not valid, refusing.", x + 1); + "Rule file name '%s' is not valid, refusing.", x); - fn = path_join("/proc/sys/fs/binfmt_misc", x+1); + fn = path_join("/proc/sys/fs/binfmt_misc", x); if (!fn) return log_oom(); From 846acb6798a63f35162f58e1146d1a1f40849e86 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 23 Apr 2020 16:36:45 +0200 Subject: [PATCH 4/5] binfmt: also unregister binfmt entries from unit We unregister binfmt_misc twice during shutdown with this change: 1. A previous commit added support for doing that in the final shutdown phase, i.e. when we do the aggressive umount loop. This is the robust thing to do, in case the earlier ("clean") shutdown phase didn't work for some reason. 2. This commit adds support for doing that when systemd-binfmt.service is stopped. This is a good idea so that people can order mounts before the service if they want to register binaries from such mounts, as in that case we'll undo the registration on shutdown again, before unmounting those mounts. And all that, just because of that weird "F" flag the kernel introduced that can pin files... Fixes: #14981 --- src/binfmt/binfmt.c | 16 ++++++++++++++-- units/systemd-binfmt.service.in | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c index 5e812e89a5..06aee22dc0 100644 --- a/src/binfmt/binfmt.c +++ b/src/binfmt/binfmt.c @@ -10,6 +10,7 @@ #include #include "alloc-util.h" +#include "binfmt-util.h" #include "conf-files.h" #include "def.h" #include "fd-util.h" @@ -24,6 +25,7 @@ static bool arg_cat_config = false; static PagerFlags arg_pager_flags = 0; +static bool arg_unregister = false; static int delete_rule(const char *rule) { _cleanup_free_ char *x = NULL, *fn = NULL; @@ -115,6 +117,7 @@ static int help(void) { " --version Show package version\n" " --cat-config Show configuration files\n" " --no-pager Do not pipe output into a pager\n" + " --unregister Unregister all existing entries\n" "\nSee the %s for details.\n" , program_invocation_short_name , link @@ -128,6 +131,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_VERSION = 0x100, ARG_CAT_CONFIG, ARG_NO_PAGER, + ARG_UNREGISTER, }; static const struct option options[] = { @@ -135,6 +139,7 @@ static int parse_argv(int argc, char *argv[]) { { "version", no_argument, NULL, ARG_VERSION }, { "cat-config", no_argument, NULL, ARG_CAT_CONFIG }, { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "unregister", no_argument, NULL, ARG_UNREGISTER }, {} }; @@ -161,6 +166,10 @@ static int parse_argv(int argc, char *argv[]) { arg_pager_flags |= PAGER_DISABLE; break; + case ARG_UNREGISTER: + arg_unregister = true; + break; + case '?': return -EINVAL; @@ -168,9 +177,9 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } - if (arg_cat_config && argc > optind) + if ((arg_unregister || arg_cat_config) && argc > optind) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Positional arguments are not allowed with --cat-config"); + "Positional arguments are not allowed with --cat-config or --unregister"); return 1; } @@ -188,6 +197,9 @@ static int run(int argc, char *argv[]) { r = 0; + if (arg_unregister) + return disable_binfmt(); + if (argc > optind) { int i; diff --git a/units/systemd-binfmt.service.in b/units/systemd-binfmt.service.in index 0c0f26451b..e54e95e11d 100644 --- a/units/systemd-binfmt.service.in +++ b/units/systemd-binfmt.service.in @@ -28,4 +28,5 @@ ConditionDirectoryNotEmpty=|/run/binfmt.d Type=oneshot RemainAfterExit=yes ExecStart=@rootlibexecdir@/systemd-binfmt +ExecStop=@rootlibexecdir@/systemd-binfmt --unregister TimeoutSec=90s From cd9aa8f0f91b113acb079e79750c146c0529c2d7 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 23 Apr 2020 16:51:37 +0200 Subject: [PATCH 5/5] man: document binfmt's new --unregister switch --- man/systemd-binfmt.service.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/man/systemd-binfmt.service.xml b/man/systemd-binfmt.service.xml index 27e34195af..41a8247c5f 100644 --- a/man/systemd-binfmt.service.xml +++ b/man/systemd-binfmt.service.xml @@ -41,6 +41,14 @@ Options + + + + If passed, instead of registering configured binary formats in the kernel, the + reverse operation is executed: all currently registered binary formats are unregistered from the + kernel. + +