diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index d423d9cd9c..d95ac4c430 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -454,6 +454,9 @@ systemd tests: causes all non-matching test functions to be skipped. Only applies to tests using our regular test boilerplate. +* `$SYSTEMD_ASSERT_RETURN_IS_CRITICAL` — Takes a boolean to control if + `assert_return()` and friends call `abort()`. + fuzzers: * `$SYSTEMD_FUZZ_OUTPUT` — A boolean that specifies whether to write output to diff --git a/src/basic/assert-util.c b/src/basic/assert-util.c index a1aa5c82e2..87b760e8fe 100644 --- a/src/basic/assert-util.c +++ b/src/basic/assert-util.c @@ -4,6 +4,7 @@ #include #include "assert-util.h" +#include "env-util.h" #include "errno-util.h" #include "log.h" @@ -17,6 +18,22 @@ void log_set_assert_return_is_critical(bool b) { assert_return_is_critical = b; } +void log_set_assert_return_is_critical_from_env(void) { + static int cached = INT_MIN; + int r; + + if (cached == INT_MIN) { + r = secure_getenv_bool("SYSTEMD_ASSERT_RETURN_IS_CRITICAL"); + if (r < 0 && r != -ENXIO) + log_debug_errno(r, "Failed to parse $SYSTEMD_ASSERT_RETURN_IS_CRITICAL, ignoring: %m"); + + cached = r; + } + + if (cached >= 0) + log_set_assert_return_is_critical(cached); +} + bool log_get_assert_return_is_critical(void) { return assert_return_is_critical; } diff --git a/src/basic/assert-util.h b/src/basic/assert-util.h index f6bb7702ea..fa3e13b220 100644 --- a/src/basic/assert-util.h +++ b/src/basic/assert-util.h @@ -6,23 +6,25 @@ /* Logging for various assertions */ void log_set_assert_return_is_critical(bool b); +void log_set_assert_return_is_critical_from_env(void); bool log_get_assert_return_is_critical(void) _pure_; void log_assert_failed_return(const char *text, const char *file, int line, const char *func); -#define assert_log(expr, message) ((_likely_(expr)) \ - ? (true) \ - : (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __func__), false)) +#define assert_log(expr) \ + (_likely_(expr) ? \ + true : \ + (log_assert_failed_return(#expr, PROJECT_FILE, __LINE__, __func__), false)) #define assert_return(expr, r) \ do { \ - if (!assert_log(expr, #expr)) \ + if (!assert_log(expr)) \ return (r); \ } while (false) #define assert_return_errno(expr, r, err) \ do { \ - if (!assert_log(expr, #expr)) { \ + if (!assert_log(expr)) { \ errno = err; \ return (r); \ } \ diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h index 7d8a284c50..04f3e73c78 100644 --- a/src/basic/signal-util.h +++ b/src/basic/signal-util.h @@ -36,16 +36,21 @@ int signal_from_string(const char *s) _pure_; void nop_signal_handler(int sig); -static inline void block_signals_reset(sigset_t *ss) { - assert_se(sigprocmask(SIG_SETMASK, ss, NULL) >= 0); +static inline void block_signals_reset(sigset_t **ss) { + assert(ss); + + if (!*ss) + return; + + assert_log(sigprocmask(SIG_SETMASK, *ss, NULL) >= 0); } -#define BLOCK_SIGNALS(...) \ - _cleanup_(block_signals_reset) _unused_ sigset_t _saved_sigset = ({ \ - sigset_t _t; \ - assert_se(sigprocmask_many(SIG_BLOCK, &_t, __VA_ARGS__) >= 0); \ - _t; \ - }) +#define BLOCK_SIGNALS(...) \ + sigset_t _saved_sigset; \ + _cleanup_(block_signals_reset) _unused_ sigset_t *_saved_sigsetp = \ + assert_log(sigprocmask_many(SIG_BLOCK, &_saved_sigset, __VA_ARGS__) >= 0) ? \ + &_saved_sigset : NULL; + #define SIGNO_INVALID (-EINVAL) static inline bool SIGNAL_VALID(int signo) { diff --git a/src/fundamental/assert-fundamental.h b/src/fundamental/assert-fundamental.h index 0b5c7f21de..3168e5699a 100644 --- a/src/fundamental/assert-fundamental.h +++ b/src/fundamental/assert-fundamental.h @@ -50,7 +50,7 @@ static inline int __coverity_check_and_return__(int condition) { #define assert_message_se(expr, message) __coverity_check__(!!(expr)) -#define assert_log(expr, message) __coverity_check_and_return__(!!(expr)) +#define assert_log(expr) __coverity_check_and_return__(!!(expr)) #else /* ! __COVERITY__ */ diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h index 0a0dd4143b..e8145622de 100644 --- a/src/libsystemd/sd-bus/bus-internal.h +++ b/src/libsystemd/sd-bus/bus-internal.h @@ -399,7 +399,7 @@ int bus_maybe_reply_error(sd_bus_message *m, int r, const sd_bus_error *e); #define bus_assert_return(expr, r, error) \ do { \ - if (!assert_log(expr, #expr)) \ + if (!assert_log(expr)) \ return sd_bus_error_set_errno(error, r); \ } while (false) diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c index b1a4cfebb8..333e1b3ba7 100644 --- a/src/nss-myhostname/nss-myhostname.c +++ b/src/nss-myhostname/nss-myhostname.c @@ -43,7 +43,7 @@ enum nss_status _nss_myhostname_gethostbyname4_r( char *r_name; PROTECT_ERRNO; - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + NSS_ENTRYPOINT_BEGIN; assert(name); assert(pat); @@ -326,7 +326,7 @@ enum nss_status _nss_myhostname_gethostbyname3_r( int n_addresses = 0; PROTECT_ERRNO; - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + NSS_ENTRYPOINT_BEGIN; assert(name); assert(host); @@ -425,7 +425,7 @@ enum nss_status _nss_myhostname_gethostbyaddr2_r( unsigned n; PROTECT_ERRNO; - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + NSS_ENTRYPOINT_BEGIN; assert(addr); assert(host); diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c index 2878ddc6b6..232e9cb91e 100644 --- a/src/nss-mymachines/nss-mymachines.c +++ b/src/nss-mymachines/nss-mymachines.c @@ -17,15 +17,6 @@ #include "signal-util.h" #include "string-util.h" -static void setup_logging_once(void) { - static pthread_once_t once = PTHREAD_ONCE_INIT; - assert_se(pthread_once(&once, log_parse_environment_variables) == 0); -} - -#define NSS_ENTRYPOINT_BEGIN \ - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); \ - setup_logging_once() - NSS_GETHOSTBYNAME_PROTOTYPES(mymachines); NSS_GETPW_PROTOTYPES(mymachines); NSS_GETGR_PROTOTYPES(mymachines); diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index 3a660ca8b2..4edd6b5fda 100644 --- a/src/nss-resolve/nss-resolve.c +++ b/src/nss-resolve/nss-resolve.c @@ -19,24 +19,6 @@ #include "strv.h" #include "time-util.h" -static sd_json_dispatch_flags_t json_dispatch_flags = SD_JSON_ALLOW_EXTENSIONS; - -static void setup_logging(void) { - log_parse_environment_variables(); - - if (DEBUG_LOGGING) - json_dispatch_flags = SD_JSON_LOG; -} - -static void setup_logging_once(void) { - static pthread_once_t once = PTHREAD_ONCE_INIT; - assert_se(pthread_once(&once, setup_logging) == 0); -} - -#define NSS_ENTRYPOINT_BEGIN \ - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); \ - setup_logging_once() - NSS_GETHOSTBYNAME_PROTOTYPES(resolve); NSS_GETHOSTBYADDR_PROTOTYPES(resolve); @@ -258,7 +240,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( goto not_found; } - r = sd_json_dispatch(rparams, resolve_hostname_reply_dispatch_table, json_dispatch_flags, &p); + r = sd_json_dispatch(rparams, resolve_hostname_reply_dispatch_table, nss_json_dispatch_flags, &p); if (r < 0) goto fail; if (sd_json_variant_is_blank_object(p.addresses)) @@ -268,7 +250,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) { AddressParameters q = {}; - r = sd_json_dispatch(entry, address_parameters_dispatch_table, json_dispatch_flags, &q); + r = sd_json_dispatch(entry, address_parameters_dispatch_table, nss_json_dispatch_flags, &q); if (r < 0) goto fail; @@ -306,7 +288,7 @@ enum nss_status _nss_resolve_gethostbyname4_r( JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) { AddressParameters q = {}; - r = sd_json_dispatch(entry, address_parameters_dispatch_table, json_dispatch_flags, &q); + r = sd_json_dispatch(entry, address_parameters_dispatch_table, nss_json_dispatch_flags, &q); if (r < 0) goto fail; @@ -422,7 +404,7 @@ enum nss_status _nss_resolve_gethostbyname3_r( goto not_found; } - r = sd_json_dispatch(rparams, resolve_hostname_reply_dispatch_table, json_dispatch_flags, &p); + r = sd_json_dispatch(rparams, resolve_hostname_reply_dispatch_table, nss_json_dispatch_flags, &p); if (r < 0) goto fail; if (sd_json_variant_is_blank_object(p.addresses)) @@ -432,7 +414,7 @@ enum nss_status _nss_resolve_gethostbyname3_r( JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) { AddressParameters q = {}; - r = sd_json_dispatch(entry, address_parameters_dispatch_table, json_dispatch_flags, &q); + r = sd_json_dispatch(entry, address_parameters_dispatch_table, nss_json_dispatch_flags, &q); if (r < 0) goto fail; @@ -478,7 +460,7 @@ enum nss_status _nss_resolve_gethostbyname3_r( JSON_VARIANT_ARRAY_FOREACH(entry, p.addresses) { AddressParameters q = {}; - r = sd_json_dispatch(entry, address_parameters_dispatch_table, json_dispatch_flags, &q); + r = sd_json_dispatch(entry, address_parameters_dispatch_table, nss_json_dispatch_flags, &q); if (r < 0) goto fail; @@ -640,7 +622,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( goto not_found; } - r = sd_json_dispatch(rparams, resolve_address_reply_dispatch_table, json_dispatch_flags, &p); + r = sd_json_dispatch(rparams, resolve_address_reply_dispatch_table, nss_json_dispatch_flags, &p); if (r < 0) goto fail; if (sd_json_variant_is_blank_object(p.names)) @@ -651,7 +633,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( JSON_VARIANT_ARRAY_FOREACH(entry, p.names) { _cleanup_(name_parameters_destroy) NameParameters q = {}; - r = sd_json_dispatch(entry, name_parameters_dispatch_table, json_dispatch_flags, &q); + r = sd_json_dispatch(entry, name_parameters_dispatch_table, nss_json_dispatch_flags, &q); if (r < 0) goto fail; @@ -692,7 +674,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( JSON_VARIANT_ARRAY_FOREACH(entry, p.names) { _cleanup_(name_parameters_destroy) NameParameters q = {}; - r = sd_json_dispatch(entry, name_parameters_dispatch_table, json_dispatch_flags, &q); + r = sd_json_dispatch(entry, name_parameters_dispatch_table, nss_json_dispatch_flags, &q); if (r < 0) goto fail; diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index 3c92cc2d00..f9a7d6caea 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -122,15 +122,6 @@ static GetentData getsgent_data = { }; REENABLE_WARNING; -static void setup_logging_once(void) { - static pthread_once_t once = PTHREAD_ONCE_INIT; - assert_se(pthread_once(&once, log_parse_environment_variables) == 0); -} - -#define NSS_ENTRYPOINT_BEGIN \ - BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); \ - setup_logging_once() - NSS_GETPW_PROTOTYPES(systemd); NSS_GETSP_PROTOTYPES(systemd); NSS_GETGR_PROTOTYPES(systemd); diff --git a/src/shared/hostname-setup.c b/src/shared/hostname-setup.c index 0d21e0482d..ac13d06fb2 100644 --- a/src/shared/hostname-setup.c +++ b/src/shared/hostname-setup.c @@ -316,7 +316,8 @@ int gethostname_full(GetHostnameFlags flags, char **ret) { assert(ret); - assert_se(uname(&u) >= 0); + if (uname(&u) < 0) + return -errno; s = u.nodename; if (isempty(s) || streq(s, "(none)") || diff --git a/src/shared/meson.build b/src/shared/meson.build index d433087459..ea5aa21b0c 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -137,6 +137,7 @@ shared_sources = files( 'notify-recv.c', 'nsflags.c', 'nsresource.c', + 'nss-util.c', 'numa-util.c', 'open-file.c', 'openssl-util.c', diff --git a/src/shared/nss-util.c b/src/shared/nss-util.c new file mode 100644 index 0000000000..32a36cd57e --- /dev/null +++ b/src/shared/nss-util.c @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "sd-json.h" + +#include "assert-util.h" +#include "log.h" +#include "nss-util.h" + +sd_json_dispatch_flags_t nss_json_dispatch_flags = SD_JSON_ALLOW_EXTENSIONS; + +static void log_setup_nss_internal(void) { + log_set_assert_return_is_critical_from_env(); + log_parse_environment_variables(); + if (DEBUG_LOGGING) + nss_json_dispatch_flags = SD_JSON_LOG; +} + +void log_setup_nss(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + assert_se(pthread_once(&once, log_setup_nss_internal) == 0); +} diff --git a/src/basic/nss-util.h b/src/shared/nss-util.h similarity index 95% rename from src/basic/nss-util.h rename to src/shared/nss-util.h index 2f55b7bc74..a4a4b5d920 100644 --- a/src/basic/nss-util.h +++ b/src/shared/nss-util.h @@ -7,7 +7,27 @@ #include #include -#define NSS_SIGNALS_BLOCK SIGALRM,SIGVTALRM,SIGPIPE,SIGCHLD,SIGTSTP,SIGIO,SIGHUP,SIGUSR1,SIGUSR2,SIGPROF,SIGURG,SIGWINCH +#include "forward.h" +#include "signal-util.h" + +extern sd_json_dispatch_flags_t nss_json_dispatch_flags; + +void log_setup_nss(void); + +#define NSS_ENTRYPOINT_BEGIN \ + log_setup_nss(); \ + BLOCK_SIGNALS(SIGALRM, \ + SIGVTALRM, \ + SIGPIPE, \ + SIGCHLD, \ + SIGTSTP, \ + SIGIO, \ + SIGHUP, \ + SIGUSR1, \ + SIGUSR2, \ + SIGPROF, \ + SIGURG, \ + SIGWINCH) #ifndef DEPRECATED_RES_USE_INET6 # define DEPRECATED_RES_USE_INET6 0x00002000 diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h index bde97acbec..54d4aa4d35 100644 --- a/src/shared/seccomp-util.h +++ b/src/shared/seccomp-util.h @@ -65,8 +65,8 @@ extern const SyscallFilterSet syscall_filter_sets[]; const SyscallFilterSet *syscall_filter_set_find(const char *name); -int seccomp_filter_set_add_by_name(Hashmap *s, bool b, const char *name); -int seccomp_filter_set_add(Hashmap *s, bool b, const SyscallFilterSet *set); +int seccomp_filter_set_add_by_name(Hashmap *filter, bool add, const char *name); +int seccomp_filter_set_add(Hashmap *filter, bool add, const SyscallFilterSet *set); int seccomp_add_syscall_filter_item( scmp_filter_ctx *ctx, @@ -77,7 +77,7 @@ int seccomp_add_syscall_filter_item( char ***added); int seccomp_load_syscall_filter_set(uint32_t default_action, const SyscallFilterSet *set, uint32_t action, bool log_missing); -int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, uint32_t action, bool log_missing); +int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap *filter, uint32_t action, bool log_missing); typedef enum SeccompParseFlags { SECCOMP_PARSE_INVERT = 1 << 0, diff --git a/src/test/meson.build b/src/test/meson.build index c65e73f487..da04b82d47 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -376,7 +376,10 @@ executables += [ 'nss-test-util.c', ), 'extract' : files('nss-test-util.c'), - 'dependencies' : libdl, + 'dependencies' : [ + libdl, + libseccomp, + ], 'conditions' : ['ENABLE_NSS'], 'timeout' : 120, }, diff --git a/src/test/test-nss-hosts.c b/src/test/test-nss-hosts.c index 8756fcd394..93de6d7add 100644 --- a/src/test/test-nss-hosts.c +++ b/src/test/test-nss-hosts.c @@ -8,6 +8,7 @@ #include "env-util.h" #include "errno-list.h" #include "format-ifname.h" +#include "hashmap.h" #include "hexdecoct.h" #include "hostname-setup.h" #include "in-addr-util.h" @@ -18,6 +19,8 @@ #include "nss-util.h" #include "parse-util.h" #include "path-util.h" +#include "process-util.h" +#include "seccomp-util.h" #include "socket-util.h" #include "string-util.h" #include "strv.h" @@ -472,7 +475,7 @@ static int run(int argc, char **argv) { int n_addresses = 0; int r; - test_setup_logging(LOG_INFO); + test_setup_logging(LOG_DEBUG); r = parse_argv(argc, argv, &modules, &names, &addresses, &n_addresses); if (r < 0) @@ -480,6 +483,32 @@ static int run(int argc, char **argv) { assert_se(path_extract_directory(argv[0], &dir) >= 0); + if (geteuid() != 0 || !is_seccomp_available()) + log_tests_skipped("Not privileged or seccomp is not available"); + else { + /* Testing with several syscalls filtered, and check if the nss modules gracefully handle failures in + * masked syscalls. See issue #38582. */ + + ASSERT_OK(r = safe_fork("(with-seccomp)", FORK_LOG | FORK_WAIT, /* ret_pid = */ NULL)); + if (r == 0) { + _cleanup_hashmap_free_ Hashmap *filter = NULL; + ASSERT_NOT_NULL(filter = hashmap_new(NULL)); + FOREACH_STRING(s, "uname", "olduname", "oldolduname", "sigprocmask", "rt_sigprocmask", "osf_sigprocmask") + ASSERT_OK(seccomp_filter_set_add_by_name(filter, /* add = */ true, s)); + ASSERT_OK(seccomp_load_syscall_filter_set_raw(SCMP_ACT_ALLOW, filter, SCMP_ACT_ERRNO(ENOSYS), /* log_missing = */ true)); + + /* To make assert_return() and friends not call abort(), even built as developer mode. */ + ASSERT_OK_ERRNO(setenv("SYSTEMD_ASSERT_RETURN_IS_CRITICAL", "0", /* overwrite = */ true)); + /* Let's also make nss modules output debugging logs. */ + ASSERT_OK_ERRNO(setenv("SYSTEMD_LOG_LEVEL", "debug", /* overwrite = */ true)); + + STRV_FOREACH(module, modules) + ASSERT_OK(test_one_module(dir, *module, names, addresses, n_addresses)); + + _exit(EXIT_SUCCESS); + } + } + STRV_FOREACH(module, modules) { r = test_one_module(dir, *module, names, addresses, n_addresses); if (r < 0)