• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 25026908423

27 Apr 2026 07:14PM UTC coverage: 71.865% (-0.3%) from 72.175%
25026908423

push

github

daandemeyer
udev: don't assert on worker cap after killing a broken idle worker

manager_can_process_event() considers an event processable if either
there is room below children_max to spawn, or an idle worker exists.
When only the latter holds, event_run() picks the idle worker and
tries device_monitor_send(). If that send fails, event_run() SIGKILLs
the worker, marks it WORKER_KILLED and continues the loop. With no
other idle worker available, it falls through to worker_spawn(),
guarded by:

    assert(hashmap_size(manager->workers) < manager->config.children_max);

The just-killed worker is still in manager->workers until its SIGCHLD
is reaped by on_worker_exit(), so at the cap this assertion trips and
udevd aborts:

    Assertion 'hashmap_size(manager->workers) < manager->config.children_max'
    failed at src/udev/udev-manager.c:635, function event_run(). Aborting.

Instead of asserting, bail out when we are already at the worker
limit. The event remains in EVENT_QUEUED; once the killed worker's
SIGCHLD arrives and frees it from the hashmap, on_post() re-runs
event_queue_start() and the event is retried.

1 of 1 new or added line in 1 file covered. (100.0%)

7309 existing lines in 125 files now uncovered.

322519 of 448782 relevant lines covered (71.87%)

1173939.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.3
/src/core/executor.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <stdlib.h>
4

5
#include "sd-messages.h"
6

7
#include "alloc-util.h"
8
#include "argv-util.h"
9
#include "build.h"
10
#include "capability-util.h"
11
#include "cgroup.h"
12
#include "dynamic-user.h"
13
#include "exec-invoke.h"
14
#include "execute.h"
15
#include "execute-serialize.h"
16
#include "exit-status.h"
17
#include "fd-util.h"
18
#include "fdset.h"
19
#include "format-table.h"
20
#include "label-util.h"
21
#include "log.h"
22
#include "options.h"
23
#include "parse-util.h"
24
#include "pretty-print.h"
25
#include "selinux-util.h"
26
#include "static-destruct.h"
27

28
static FILE *arg_serialization = NULL;
29

30
STATIC_DESTRUCTOR_REGISTER(arg_serialization, fclosep);
34✔
31

32
static int help(void) {
×
33
        _cleanup_free_ char *link = NULL;
×
34
        _cleanup_(table_unrefp) Table *options = NULL;
×
35
        int r;
×
36

37
        r = terminal_urlify_man("systemd", "1", &link);
×
38
        if (r < 0)
×
39
                return log_oom();
×
40

41
        r = option_parser_get_help_table(&options);
×
42
        if (r < 0)
×
43
                return r;
44

45
        printf("%s [OPTIONS...]\n\n"
×
46
               "%sSandbox and execute processes.%s\n\n",
47
               program_invocation_short_name,
48
               ansi_highlight(),
49
               ansi_normal());
50

51
        r = table_print_or_warn(options);
×
52
        if (r < 0)
×
53
                return r;
54

55
        printf("\nSee the %s for details.\n", link);
×
56
        return 0;
57
}
58

59
static int parse_argv(int argc, char *argv[]) {
10,448✔
60
        int r;
10,448✔
61

62
        assert(argc >= 0);
10,448✔
63
        assert(argv);
10,448✔
64

65
        OptionParser opts = { argc, argv };
10,448✔
66

67
        FOREACH_OPTION(c, &opts, /* on_error= */ return c)
52,240✔
68
                switch (c) {
31,344✔
69

UNCOV
70
                OPTION_COMMON_HELP:
×
71
                        return help();
×
72

UNCOV
73
                OPTION_COMMON_VERSION:
×
74
                        return version();
×
75

76
                OPTION_COMMON_LOG_LEVEL:
10,448✔
77
                        r = log_set_max_level_from_string(opts.arg);
10,448✔
78
                        if (r < 0)
10,448✔
UNCOV
79
                                return log_error_errno(r, "Failed to parse log level \"%s\": %m", opts.arg);
×
80
                        break;
81

82
                OPTION_COMMON_LOG_TARGET:
10,448✔
83
                        r = log_set_target_from_string(opts.arg);
10,448✔
84
                        if (r < 0)
10,448✔
UNCOV
85
                                return log_error_errno(r, "Failed to parse log target \"%s\": %m", opts.arg);
×
86
                        break;
87

UNCOV
88
                OPTION_COMMON_LOG_COLOR:
×
89
                        r = log_show_color_from_string(opts.arg);
×
90
                        if (r < 0)
×
91
                                return log_error_errno(r, "Failed to parse log color setting \"%s\": %m", opts.arg);
×
92
                        break;
93

UNCOV
94
                OPTION_COMMON_LOG_LOCATION:
×
95
                        r = log_show_location_from_string(opts.arg);
×
96
                        if (r < 0)
×
97
                                return log_error_errno(r, "Failed to parse log location setting \"%s\": %m", opts.arg);
×
98
                        break;
99

UNCOV
100
                OPTION_COMMON_LOG_TIME:
×
101
                        r = log_show_time_from_string(opts.arg);
×
102
                        if (r < 0)
×
103
                                return log_error_errno(r, "Failed to parse log time setting \"%s\": %m", opts.arg);
×
104
                        break;
105

106
                OPTION_LONG("deserialize", "FD", "Deserialize process config from FD"): {
10,448✔
107
                        int fd = parse_fd(opts.arg);
10,448✔
108
                        if (fd < 0)
10,448✔
UNCOV
109
                                return log_error_errno(fd, "Failed to parse serialization fd \"%s\": %m", opts.arg);
×
110

111
                        /* Set O_CLOEXEC and as a side effect, verify that the fd is valid. */
112
                        r = fd_cloexec(fd, /* cloexec= */ true);
10,448✔
113
                        if (r == -EBADF)
10,448✔
UNCOV
114
                                return log_error_errno(r, "Serialization fd %d is not valid.", fd);
×
115
                        if (r < 0)
10,448✔
UNCOV
116
                                return log_error_errno(r, "Failed to set serialization fd %d to close-on-exec: %m",
×
117
                                                       fd);
118

119
                        FILE *f = fdopen(fd, "r");
10,448✔
120
                        if (!f)
10,448✔
UNCOV
121
                                return log_error_errno(errno, "Failed to open serialization fd %d: %m", fd);
×
122

123
                        safe_fclose(arg_serialization);
10,448✔
124
                        arg_serialization = f;
10,448✔
125
                        break;
10,448✔
126
                }
127
                }
128

129
        if (!arg_serialization)
10,448✔
UNCOV
130
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "No serialization fd specified.");
×
131

132
        return 1 /* work to do */;
133
}
134

135
static int run(int argc, char *argv[]) {
10,448✔
136
        _cleanup_fdset_free_ FDSet *fdset = NULL;
34✔
137
        _cleanup_(cgroup_context_done) CGroupContext cgroup_context = {};
34✔
138
        _cleanup_(exec_context_done) ExecContext context = {};
34✔
139
        _cleanup_(exec_command_done) ExecCommand command = {};
34✔
140
        _cleanup_(exec_params_deep_clear) ExecParameters params = EXEC_PARAMETERS_INIT(/* flags= */ 0);
34✔
141
        _cleanup_(exec_shared_runtime_done) ExecSharedRuntime shared = {
34✔
142
                .userns_storage_socket = EBADF_PAIR,
143
                .netns_storage_socket = EBADF_PAIR,
144
                .ipcns_storage_socket = EBADF_PAIR,
145
        };
146
        _cleanup_(dynamic_creds_done) DynamicCreds dynamic_creds = {};
34✔
147
        _cleanup_(exec_runtime_clear) ExecRuntime runtime = {
34✔
148
                .ephemeral_storage_socket = EBADF_PAIR,
149
                .shared = &shared,
150
                .dynamic_creds = &dynamic_creds,
151
        };
152
        int exit_status = EXIT_SUCCESS, r;
10,448✔
153

154
        exec_context_init(&context);
10,448✔
155
        cgroup_context_init(&cgroup_context);
10,448✔
156

157
        /* We might be starting the journal itself, we'll be told by the caller what to do */
158
        log_set_prohibit_ipc(true);
10,448✔
159
        log_setup();
10,448✔
160

161
        r = parse_argv(argc, argv);
10,448✔
162
        if (r <= 0)
10,448✔
163
                return r;
164

165
        /* Now that we know the intended log target, allow IPC and open the final log target. */
166
        log_set_always_reopen_console(true);
10,448✔
167
        log_set_prohibit_ipc(false);
10,448✔
168
        log_open();
10,448✔
169

170
        /* Clear ambient capabilities, so services do not inherit them implicitly. Dropping them does
171
         * not affect the permitted and effective sets which are important for the executor itself to
172
         * operate. */
173
        r = capability_ambient_set_apply(0, /* also_inherit= */ false);
10,448✔
174
        if (r < 0)
10,448✔
UNCOV
175
                log_warning_errno(r, "Failed to clear ambient capabilities, ignoring: %m");
×
176

177
        /* This call would collect all passed fds and enable CLOEXEC. We'll unset it in exec_invoke (flag_fds)
178
         * for fds that shall be passed to the child.
179
         * The serialization fd is set to CLOEXEC in parse_argv, so it's also filtered. */
180
        r = fdset_new_fill(/* filter_cloexec= */ 0, &fdset);
10,448✔
181
        if (r < 0)
10,448✔
UNCOV
182
                return log_error_errno(r, "Failed to create fd set: %m");
×
183

184
        /* Initialize lazily. SMACK is just a few operations, but the SELinux is very slow as it requires
185
         * loading the entire database in memory, so we will do it lazily only if it is actually needed, to
186
         * avoid wasting 2ms-10ms for each sd-executor that gets spawned. */
187
        r = mac_init_lazy();
10,448✔
188
        if (r < 0)
10,448✔
UNCOV
189
                return log_error_errno(r, "Failed to initialize MAC layer: %m");
×
190

191
        r = exec_deserialize_invocation(arg_serialization,
10,448✔
192
                                        fdset,
193
                                        &context,
194
                                        &command,
195
                                        &params,
196
                                        &runtime,
197
                                        &cgroup_context);
198
        if (r < 0)
10,448✔
UNCOV
199
                return log_error_errno(r, "Failed to deserialize: %m");
×
200

201
        LOG_CONTEXT_PUSH_EXEC(&context, &params);
29,948✔
202

203
        arg_serialization = safe_fclose(arg_serialization);
10,448✔
204
        fdset = fdset_free(fdset);
10,448✔
205

206
        r = exec_invoke(&command,
10,448✔
207
                        &context,
208
                        &params,
209
                        &runtime,
210
                        &cgroup_context,
211
                        &exit_status);
212
        if (r < 0) {
34✔
213
                const char *status = ASSERT_PTR(
33✔
214
                                exit_status_to_string(exit_status, EXIT_STATUS_LIBC | EXIT_STATUS_SYSTEMD));
215

216
                log_struct_errno(LOG_ERR, r,
33✔
217
                                 LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED_STR),
218
                                 LOG_EXEC_MESSAGE(&params, "Failed at step %s spawning %s: %m",
219
                                                  status, command.path),
220
                                 LOG_ITEM("EXECUTABLE=%s", command.path));
221
        } else
222
                /* r == 0: 'skip' is chosen in the confirm spawn prompt
223
                 * r > 0:  expected/ignored failure, do not log at error level */
224
                assert((r == 0) == (exit_status == EXIT_SUCCESS));
1✔
225

226
        return exit_status;
34✔
227
}
228

229
int main(int argc, char *argv[]) {
10,448✔
230
        int r;
10,448✔
231

232
        /* We use safe_fork() for spawning sd-pam helper process, which internally calls rename_process().
233
         * As the last step of renaming, all saved argvs are memzero()-ed. Hence, we need to save the argv
234
         * first to prevent showing "intense" cmdline. See #30352. */
235
        save_argc_argv(argc, argv);
10,448✔
236

237
        r = run(argc, argv);
10,448✔
238

239
        mac_selinux_finish();
34✔
240
        static_destruct();
34✔
241

242
        return r < 0 ? EXIT_FAILURE : r;
34✔
243
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc