Merge pull request #10935 from poettering/rlimit-nofile-safe

Merged by hand to resolve a trivial conflict in TODO.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2018-12-06 17:19:21 +01:00
commit 871fa294ff
29 changed files with 124 additions and 41 deletions

4
TODO
View File

@ -29,6 +29,10 @@ Features:
* when we fork off generators and such, lower LIMIT_NOFILE soft limit to 1K
* Maybe introduce a helper safe_exec() or so, which is to execve() which
safe_fork() is to fork(). And then make revert the RLIMIT_NOFILE soft limit
to 1K implicitly, unless explicitly opted-out.
* rework seccomp/nnp logic that that even if User= is used in combination with
a seccomp option we don't have to set NNP. For that, change uid first whil
keeping CAP_SYS_ADMIN, then apply seccomp, the drop cap.

View File

@ -249,7 +249,7 @@ static int fork_and_exec_process(const char* child, char** argv, char **env, int
if (!joined)
return log_oom();
r = safe_fork("(activate)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &child_pid);
r = safe_fork("(activate)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &child_pid);
if (r < 0)
return r;
if (r == 0) {

View File

@ -149,6 +149,8 @@ basic_sources = files('''
refcnt.h
replace-var.c
replace-var.h
rlimit-util.c
rlimit-util.h
rm-rf.c
rm-rf.h
selinux-util.c

View File

@ -35,6 +35,7 @@
#include "missing.h"
#include "process-util.h"
#include "raw-clone.h"
#include "rlimit-util.h"
#include "signal-util.h"
#include "stat-util.h"
#include "string-table.h"
@ -1401,6 +1402,14 @@ int safe_fork_full(
}
}
if (flags & FORK_RLIMIT_NOFILE_SAFE) {
r = rlimit_nofile_safe();
if (r < 0) {
log_full_errno(prio, r, "Failed to lower RLIMIT_NOFILE's soft limit to 1K: %m");
_exit(EXIT_FAILURE);
}
}
if (ret_pid)
*ret_pid = getpid_cached();
@ -1512,6 +1521,8 @@ int fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret
safe_close_above_stdio(fd);
}
(void) rlimit_nofile_safe();
/* Count arguments */
va_start(ap, path);
for (n = 0; va_arg(ap, char*); n++)

View File

@ -142,15 +142,16 @@ void reset_cached_pid(void);
int must_be_root(void);
typedef enum ForkFlags {
FORK_RESET_SIGNALS = 1 << 0,
FORK_CLOSE_ALL_FDS = 1 << 1,
FORK_DEATHSIG = 1 << 2,
FORK_NULL_STDIO = 1 << 3,
FORK_REOPEN_LOG = 1 << 4,
FORK_LOG = 1 << 5,
FORK_WAIT = 1 << 6,
FORK_NEW_MOUNTNS = 1 << 7,
FORK_MOUNTNS_SLAVE = 1 << 8,
FORK_RESET_SIGNALS = 1 << 0, /* Reset all signal handlers and signal mask */
FORK_CLOSE_ALL_FDS = 1 << 1, /* Close all open file descriptors in the child, except for 0,1,2 */
FORK_DEATHSIG = 1 << 2, /* Set PR_DEATHSIG in the child */
FORK_NULL_STDIO = 1 << 3, /* Connect 0,1,2 to /dev/null */
FORK_REOPEN_LOG = 1 << 4, /* Reopen log connection */
FORK_LOG = 1 << 5, /* Log above LOG_DEBUG log level about failures */
FORK_WAIT = 1 << 6, /* Wait until child exited */
FORK_NEW_MOUNTNS = 1 << 7, /* Run child in its own mount namespace */
FORK_MOUNTNS_SLAVE = 1 << 8, /* Make child's mount namespace MS_SLAVE */
FORK_RLIMIT_NOFILE_SAFE = 1 << 9, /* Set RLIMIT_NOFILE soft limit to 1K for select() compat */
} ForkFlags;
int safe_fork_full(const char *name, const int except_fds[], size_t n_except_fds, ForkFlags flags, pid_t *ret_pid);

View File

@ -389,3 +389,22 @@ int rlimit_nofile_bump(int limit) {
return 0;
}
int rlimit_nofile_safe(void) {
struct rlimit rl;
/* Resets RLIMIT_NOFILE's soft limit FD_SETSIZE (i.e. 1024), for compatibility with software still using
* select() */
if (getrlimit(RLIMIT_NOFILE, &rl) < 0)
return log_debug_errno(errno, "Failed to query RLIMIT_NOFILE: %m");
if (rl.rlim_cur <= FD_SETSIZE)
return 0;
rl.rlim_cur = FD_SETSIZE;
if (setrlimit(RLIMIT_NOFILE, &rl) < 0)
return log_debug_errno(errno, "Failed to lower RLIMIT_NOFILE's soft limit to " RLIM_FMT ": %m", rl.rlim_cur);
return 1;
}

View File

@ -22,3 +22,4 @@ void rlimit_free_all(struct rlimit **rl);
#define RLIMIT_MAKE_CONST(lim) ((struct rlimit) { lim, lim })
int rlimit_nofile_bump(int limit);
int rlimit_nofile_safe(void);

View File

@ -236,6 +236,7 @@ _noreturn_ static void crash(int sig) {
else if (pid == 0) {
(void) setsid();
(void) make_console_stdio();
(void) rlimit_nofile_safe();
(void) execle("/bin/sh", "/bin/sh", NULL, environ);
log_emergency_errno(errno, "execle() failed: %m");
@ -1733,6 +1734,7 @@ static void do_reexecute(
/* Reenable any blocked signals, especially important if we switch from initial ramdisk to init=... */
(void) reset_all_signal_handlers();
(void) reset_signal_mask();
(void) rlimit_nofile_safe();
if (switch_root_init) {
args[0] = switch_root_init;

View File

@ -28,6 +28,7 @@
#include "parse-util.h"
#include "process-util.h"
#include "reboot-util.h"
#include "rlimit-util.h"
#include "signal-util.h"
#include "string-util.h"
#include "switch-root.h"
@ -443,13 +444,15 @@ int main(int argc, char *argv[]) {
arguments[2] = NULL;
execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL);
(void) rlimit_nofile_safe();
if (can_initrd) {
r = switch_root_initramfs();
if (r >= 0) {
argv[0] = (char*) "/shutdown";
setsid();
make_console_stdio();
(void) setsid();
(void) make_console_stdio();
log_info("Successfully changed into root pivot.\n"
"Returning to initrd...");

View File

@ -968,7 +968,7 @@ static int run_debug(int argc, char **argv, void *userdata) {
fork_name = strjoina("(", arg_debugger, ")");
r = safe_fork(fork_name, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
r = safe_fork(fork_name, FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
goto finish;
if (r == 0) {

View File

@ -169,7 +169,7 @@ static int found_override(const char *top, const char *bottom) {
fflush(stdout);
r = safe_fork("(diff)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
r = safe_fork("(diff)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {

View File

@ -27,6 +27,7 @@
#include "path-util.h"
#include "proc-cmdline.h"
#include "process-util.h"
#include "rlimit-util.h"
#include "signal-util.h"
#include "socket-util.h"
#include "special.h"
@ -401,6 +402,8 @@ static int run(int argc, char *argv[]) {
cmdline[i++] = device;
cmdline[i++] = NULL;
(void) rlimit_nofile_safe();
execv(cmdline[0], (char**) cmdline);
_exit(FSCK_OPERATIONAL_ERROR);
}

View File

@ -14,6 +14,7 @@
#include "process-util.h"
#include "pull-common.h"
#include "pull-job.h"
#include "rlimit-util.h"
#include "rm-rf.h"
#include "signal-util.h"
#include "siphash24.h"
@ -472,6 +473,8 @@ int pull_verify(PullJob *main_job,
_exit(EXIT_FAILURE);
}
(void) rlimit_nofile_safe();
cmd[k++] = strjoina("--homedir=", gpg_home);
/* We add the user keyring only to the command line

View File

@ -81,6 +81,8 @@ static int spawn_child(const char* child, char** argv) {
_exit(EXIT_FAILURE);
}
(void) rlimit_nofile_safe();
execvp(child, argv);
log_error_errno(errno, "Failed to exec child %s: %m", child);
_exit(EXIT_FAILURE);

View File

@ -21,6 +21,7 @@
#include "missing.h"
#include "path-util.h"
#include "process-util.h"
#include "rlimit-util.h"
#include "selinux-util.h"
#include "signal-util.h"
#include "stdio-util.h"
@ -932,6 +933,8 @@ int bus_socket_exec(sd_bus *b) {
if (rearrange_stdio(s[1], s[1], STDERR_FILENO) < 0)
_exit(EXIT_FAILURE);
(void) rlimit_nofile_safe();
if (b->exec_argv)
execvp(b->exec_path, b->exec_argv);
else {

View File

@ -303,7 +303,7 @@ static int run(int argc, char *argv[]) {
if (fd < 0)
return log_error_errno(fd, "Failed to inhibit: %s", bus_error_message(&error, fd));
r = safe_fork("(inhibit)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
r = safe_fork("(inhibit)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {

View File

@ -170,7 +170,7 @@ int bus_image_method_clone(
if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0)
return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");
r = safe_fork("(imgclone)", FORK_RESET_SIGNALS, &child);
r = safe_fork("(sd-imgclone)", FORK_RESET_SIGNALS, &child);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to fork(): %m");
if (r == 0) {

View File

@ -12,6 +12,7 @@
#include "mkdir.h"
#include "nspawn-setuid.h"
#include "process-util.h"
#include "rlimit-util.h"
#include "signal-util.h"
#include "string-util.h"
#include "strv.h"
@ -44,6 +45,8 @@ static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
close_all_fds(NULL, 0);
(void) rlimit_nofile_safe();
execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env);
execle("/bin/getent", "getent", database, key, NULL, &empty_env);
_exit(EXIT_FAILURE);

View File

@ -28,7 +28,7 @@ static int makefs(const char *type, const char *device) {
if (access(mkfs, X_OK) != 0)
return log_error_errno(errno, "%s is not executable: %m", mkfs);
r = safe_fork("(fsck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
r = safe_fork("(mkfs)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {

View File

@ -78,7 +78,7 @@ static int run(int argc, char *argv[]) {
return 0;
}
r = safe_fork("(quotacheck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
r = safe_fork("(quotacheck)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_WAIT|FORK_LOG, NULL);
if (r < 0)
return r;
if (r == 0) {

View File

@ -62,7 +62,7 @@ static int run(int argc, char *argv[]) {
log_debug("Remounting %s", me->mnt_dir);
r = safe_fork("(remount)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
r = safe_fork("(remount)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {

View File

@ -17,6 +17,7 @@
#include "hashmap.h"
#include "macro.h"
#include "process-util.h"
#include "rlimit-util.h"
#include "serialize.h"
#include "set.h"
#include "signal-util.h"
@ -52,6 +53,8 @@ static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
_exit(EXIT_FAILURE);
}
(void) rlimit_nofile_safe();
if (!argv) {
_argv[0] = (char*) path;
_argv[1] = NULL;

View File

@ -120,8 +120,6 @@ shared_sources = files('''
reboot-util.h
resolve-util.c
resolve-util.h
rlimit-util.c
rlimit-util.h
seccomp-util.h
securebits-util.c
securebits-util.h

View File

@ -19,6 +19,7 @@
#include "macro.h"
#include "pager.h"
#include "process-util.h"
#include "rlimit-util.h"
#include "signal-util.h"
#include "string-util.h"
#include "strv.h"
@ -55,7 +56,7 @@ static int no_quit_on_interrupt(int exe_name_fd, const char *less_opts) {
file = fdopen(exe_name_fd, "r");
if (!file) {
safe_close(exe_name_fd);
return log_debug_errno(errno, "Failed to create FILE object: %m");
return log_error_errno(errno, "Failed to create FILE object: %m");
}
/* Find the last line */
@ -64,7 +65,7 @@ static int no_quit_on_interrupt(int exe_name_fd, const char *less_opts) {
r = read_line(file, LONG_LINE_MAX, &t);
if (r < 0)
return r;
return log_error_errno(r, "Failed to read from socket: %m");
if (r == 0)
break;
@ -96,7 +97,7 @@ int pager_open(PagerFlags flags) {
return 0;
if (!is_main_thread())
return -EPERM;
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Pager invoked from wrong thread.");
pager = getenv("SYSTEMD_PAGER");
if (!pager)
@ -105,7 +106,7 @@ int pager_open(PagerFlags flags) {
if (pager) {
pager_args = strv_split(pager, WHITESPACE);
if (!pager_args)
return -ENOMEM;
return log_oom();
/* If the pager is explicitly turned off, honour it */
if (strv_isempty(pager_args) || strv_equal(pager_args, STRV_MAKE("cat")))
@ -131,7 +132,7 @@ int pager_open(PagerFlags flags) {
if (flags & PAGER_JUMP_TO_END)
less_opts = strjoina(less_opts, " +G");
r = safe_fork("(pager)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pager_pid);
r = safe_fork_full("(pager)", fd, 2, FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pager_pid);
if (r < 0)
return r;
if (r == 0) {
@ -139,11 +140,17 @@ int pager_open(PagerFlags flags) {
/* In the child start the pager */
(void) dup2(fd[0], STDIN_FILENO);
if (dup2(fd[0], STDIN_FILENO) < 0) {
log_error_errno(errno, "Failed to duplicate file descriptor to STDIN: %m");
_exit(EXIT_FAILURE);
}
safe_close_pair(fd);
if (setenv("LESS", less_opts, 1) < 0)
if (setenv("LESS", less_opts, 1) < 0) {
log_error_errno(errno, "Failed to set environment variable LESS: %m");
_exit(EXIT_FAILURE);
}
/* Initialize a good charset for less. This is
* particularly important if we output UTF-8
@ -152,14 +159,21 @@ int pager_open(PagerFlags flags) {
if (!less_charset && is_locale_utf8())
less_charset = "utf-8";
if (less_charset &&
setenv("LESSCHARSET", less_charset, 1) < 0)
setenv("LESSCHARSET", less_charset, 1) < 0) {
log_error_errno(errno, "Failed to set environment variable LESSCHARSET: %m");
_exit(EXIT_FAILURE);
}
if (pager_args) {
if (loop_write(exe_name_pipe[1], pager_args[0], strlen(pager_args[0]) + 1, false) < 0)
r = loop_write(exe_name_pipe[1], pager_args[0], strlen(pager_args[0]) + 1, false);
if (r < 0) {
log_error_errno(r, "Failed to write pager name to socket: %m");
_exit(EXIT_FAILURE);
}
execvp(pager_args[0], pager_args);
log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
"Failed execute %s, using fallback pagers: %m", pager_args[0]);
}
/* Debian's alternatives command for pagers is
@ -169,13 +183,21 @@ int pager_open(PagerFlags flags) {
* is similar to this one anyway, but is
* Debian-specific. */
FOREACH_STRING(exe, "pager", "less", "more") {
if (loop_write(exe_name_pipe[1], exe, strlen(exe) + 1, false) < 0)
r = loop_write(exe_name_pipe[1], exe, strlen(exe) + 1, false);
if (r < 0) {
log_error_errno(r, "Failed to write pager name to socket: %m");
_exit(EXIT_FAILURE);
}
execlp(exe, exe, NULL);
log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, errno,
"Failed execute %s, using next fallback pager: %m", exe);
}
if (loop_write(exe_name_pipe[1], "(built-in)", strlen("(built-in") + 1, false) < 0)
r = loop_write(exe_name_pipe[1], "(built-in)", strlen("(built-in") + 1, false);
if (r < 0) {
log_error_errno(r, "Failed to write pager name to socket: %m");
_exit(EXIT_FAILURE);
}
pager_fallback();
/* not reached */
}
@ -256,7 +278,7 @@ int show_man_page(const char *desc, bool null_stdio) {
} else
args[1] = desc;
r = safe_fork("(man)", FORK_RESET_SIGNALS|FORK_DEATHSIG|(null_stdio ? FORK_NULL_STDIO : 0)|FORK_LOG, &pid);
r = safe_fork("(man)", FORK_RESET_SIGNALS|FORK_DEATHSIG|(null_stdio ? FORK_NULL_STDIO : 0)|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {

View File

@ -69,7 +69,7 @@ static int fork_wait(const char* const cmdline[]) {
pid_t pid;
int r;
r = safe_fork("(sulogin)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
r = safe_fork("(sulogin)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {

View File

@ -3553,7 +3553,7 @@ static int load_kexec_kernel(void) {
if (arg_dry_run)
return 0;
r = safe_fork("(kexec)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
r = safe_fork("(kexec)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {
@ -6026,7 +6026,7 @@ static int enable_sysv_units(const char *verb, char **args) {
if (!arg_quiet)
log_info("Executing: %s", l);
j = safe_fork("(sysv-install)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
j = safe_fork("(sysv-install)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (j < 0)
return j;
if (j == 0) {
@ -6921,7 +6921,7 @@ static int run_editor(char **paths) {
assert(paths);
r = safe_fork("(editor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL);
r = safe_fork("(editor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG|FORK_WAIT, NULL);
if (r < 0)
return r;
if (r == 0) {
@ -8323,6 +8323,7 @@ static int parse_argv(int argc, char *argv[]) {
/* Hmm, so some other init system is running, we need to forward this request to
* it. For now we simply guess that it is Upstart. */
(void) rlimit_nofile_safe();
execv(TELINIT, argv);
return log_error_errno(SYNTHETIC_ERRNO(EIO),

View File

@ -20,6 +20,7 @@
#include "netlink-util.h"
#include "path-util.h"
#include "process-util.h"
#include "rlimit-util.h"
#include "signal-util.h"
#include "stdio-util.h"
#include "string-util.h"
@ -650,6 +651,7 @@ int udev_event_spawn(struct udev_event *event,
_exit(EXIT_FAILURE);
(void) close_all_fds(NULL, 0);
(void) rlimit_nofile_safe();
execve(argv[0], argv, envp);
_exit(EXIT_FAILURE);

View File

@ -534,7 +534,7 @@ static int worker_spawn(Manager *manager, struct event *event) {
if (r < 0)
return log_error_errno(r, "Worker: Failed to enable receiving of device: %m");
r = safe_fork(NULL, FORK_DEATHSIG, &pid);
r = safe_fork("(worker)", FORK_DEATHSIG, &pid);
if (r < 0) {
event->state = EVENT_QUEUED;
return log_error_errno(r, "Failed to fork() worker: %m");
@ -1850,7 +1850,7 @@ static int run(int argc, char *argv[]) {
return 0;
/* child */
setsid();
(void) setsid();
r = set_oom_score_adjust(-1000);
if (r < 0)

View File

@ -150,7 +150,7 @@ static int keyboard_load_and_wait(const char *vc, const char *map, const char *m
log_debug("Executing \"%s\"...", strnull(cmd));
}
r = safe_fork("(loadkeys)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
r = safe_fork("(loadkeys)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {
@ -193,7 +193,7 @@ static int font_load_and_wait(const char *vc, const char *font, const char *map,
log_debug("Executing \"%s\"...", strnull(cmd));
}
r = safe_fork("(setfont)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_LOG, &pid);
r = safe_fork("(setfont)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid);
if (r < 0)
return r;
if (r == 0) {