From cbcf371abc328167fa869721c1add4850c793240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 23 Jul 2021 11:36:44 +0200 Subject: [PATCH] Add variant of close_all_fds() that does not allocate and use it in freeze() Even though it's just a fallback path, let's not be sloppy and allocate in the crash handler. > The deadlock happens because systemd crash in malloc() then in signal > handler, it calls malloc() (close_all_fds()-> opendir()-> __alloc_dir()) > again. malloc() is not a signal-safe function, maybe we should re-think > the logic here. Fixes #20266. --- src/basic/fd-util.c | 8 ++++---- src/basic/fd-util.h | 5 ++++- src/shared/exec-util.c | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index 99b517d8c2f..6b6457dbc22 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -208,7 +208,7 @@ static int get_max_fd(void) { return (int) (m - 1); } -int close_all_fds(int except[], size_t n_except) { +int close_all_fds_full(int except[], size_t n_except, bool allow_alloc) { static bool have_close_range = true; /* Assume we live in the future */ _cleanup_closedir_ DIR *d = NULL; int r = 0; @@ -274,7 +274,7 @@ int close_all_fds(int except[], size_t n_except) { /* Fallback for when close_range() is not supported */ opendir_fallback: - d = opendir("/proc/self/fd"); + d = allow_alloc ? opendir("/proc/self/fd") : NULL; if (d) { struct dirent *de; @@ -302,8 +302,8 @@ int close_all_fds(int except[], size_t n_except) { return r; } - /* Fallback for when /proc isn't available (for example in chroots) by brute-forcing through the file - * descriptor table. */ + /* Fallback for when /proc isn't available (for example in chroots) or when we cannot allocate by + * brute-forcing through the file descriptor table. */ int max_fd = get_max_fd(); if (max_fd < 0) diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index d3c2944d869..61b6684cb3c 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -56,7 +56,10 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(DIR*, closedir, NULL); int fd_nonblock(int fd, bool nonblock); int fd_cloexec(int fd, bool cloexec); -int close_all_fds(int except[], size_t n_except); +int close_all_fds_full(int except[], size_t n_except, bool allow_alloc); +static inline int close_all_fds(int except[], size_t n_except) { + return close_all_fds_full(except, n_except, true); +} int same_fd(int a, int b); diff --git a/src/shared/exec-util.c b/src/shared/exec-util.c index 42f6c4d75ad..cffa3fe96e7 100644 --- a/src/shared/exec-util.c +++ b/src/shared/exec-util.c @@ -452,7 +452,7 @@ _noreturn_ void freeze(void) { log_close(); /* Make sure nobody waits for us on a socket anymore */ - (void) close_all_fds(NULL, 0); + (void) close_all_fds_full(NULL, 0, false); sync();