|
|
4ff8bf |
From 94bacc6955e563a7e698e53151a75323279a9f45 Mon Sep 17 00:00:00 2001
|
|
|
4ff8bf |
From: Simon McVittie <smcv@collabora.com>
|
|
|
4ff8bf |
Date: Mon, 11 Mar 2019 09:03:39 +0000
|
|
|
4ff8bf |
Subject: [PATCH] bus: Try to raise soft fd limit to match hard limit
|
|
|
4ff8bf |
|
|
|
4ff8bf |
Linux systems have traditionally set the soft limit to 1024 and the hard
|
|
|
4ff8bf |
limit to 4096. Recent versions of systemd keep the soft fd limit at
|
|
|
4ff8bf |
1024 to avoid breaking programs that still use select(), but raise the
|
|
|
4ff8bf |
hard limit to 512*1024, while in recent Debian versions a complicated
|
|
|
4ff8bf |
interaction between components gives a soft limit of 1024 and a hard
|
|
|
4ff8bf |
limit of 1024*1024. If we can, we might as well elevate our soft limit
|
|
|
4ff8bf |
to match the hard limit, minimizing the chance that we will run out of
|
|
|
4ff8bf |
file descriptor slots.
|
|
|
4ff8bf |
|
|
|
4ff8bf |
Unlike the previous code to raise the hard and soft limits to at least
|
|
|
4ff8bf |
65536, we do this even if we don't have privileges: privileges are
|
|
|
4ff8bf |
unnecessary to raise the soft limit up to the hard limit.
|
|
|
4ff8bf |
|
|
|
4ff8bf |
If we *do* have privileges, we also continue to raise the hard and soft
|
|
|
4ff8bf |
limits to at least 65536 if they weren't already that high, making
|
|
|
4ff8bf |
it harder to carry out a denial of service attack on the system bus on
|
|
|
4ff8bf |
systems that use the traditional limit (CVE-2014-7824).
|
|
|
4ff8bf |
|
|
|
4ff8bf |
As was previously the case on the system bus, we'll drop the limits back
|
|
|
4ff8bf |
to our initial limits before we execute a subprocess for traditional
|
|
|
4ff8bf |
(non-systemd) activation, if enabled.
|
|
|
4ff8bf |
|
|
|
4ff8bf |
systemd activation doesn't involve us starting subprocesses at all,
|
|
|
4ff8bf |
so in both cases activated services will still inherit the same limits
|
|
|
4ff8bf |
they did previously.
|
|
|
4ff8bf |
|
|
|
4ff8bf |
This change also fixes a bug when the hard limit is very large but
|
|
|
4ff8bf |
the soft limit is not, for example seen as a regression when upgrading
|
|
|
4ff8bf |
to systemd >= 240 (Debian #928877). In such environments, dbus-daemon
|
|
|
4ff8bf |
would previously have changed its fd limit to 64K soft/64K hard. Because
|
|
|
4ff8bf |
this hard limit is less than its original hard limit, it was unable to
|
|
|
4ff8bf |
restore its original hard limit as intended when carrying out traditional
|
|
|
4ff8bf |
activation, leaving activated subprocesses with unintended limits (while
|
|
|
4ff8bf |
logging a warning).
|
|
|
4ff8bf |
|
|
|
4ff8bf |
Reviewed-by: Lennart Poettering <lennart@poettering.net>
|
|
|
4ff8bf |
[smcv: Correct a comment based on Lennart's review, reword commit message]
|
|
|
4ff8bf |
Signed-off-by: Simon McVittie <smcv@collabora.com>
|
|
|
4ff8bf |
(cherry picked from commit 7eacbfece70f16bb54d0f3ac51f87ae398759ef5)
|
|
|
4ff8bf |
[smcv: Mention that this also fixes Debian #928877]
|
|
|
4ff8bf |
---
|
|
|
4ff8bf |
bus/bus.c | 8 ++---
|
|
|
4ff8bf |
dbus/dbus-sysdeps-util-unix.c | 64 +++++++++++++++++++++--------------
|
|
|
4ff8bf |
dbus/dbus-sysdeps-util-win.c | 3 +-
|
|
|
4ff8bf |
dbus/dbus-sysdeps.h | 3 +-
|
|
|
4ff8bf |
4 files changed, 44 insertions(+), 34 deletions(-)
|
|
|
4ff8bf |
|
|
|
4ff8bf |
diff --git a/bus/bus.c b/bus/bus.c
|
|
|
4ff8bf |
index 30ce4e10..2ad8e789 100644
|
|
|
4ff8bf |
--- a/bus/bus.c
|
|
|
4ff8bf |
+++ b/bus/bus.c
|
|
|
4ff8bf |
@@ -693,11 +693,11 @@ raise_file_descriptor_limit (BusContext *context)
|
|
|
4ff8bf |
/* We used to compute a suitable rlimit based on the configured number
|
|
|
4ff8bf |
* of connections, but that breaks down as soon as we allow fd-passing,
|
|
|
4ff8bf |
* because each connection is allowed to pass 64 fds to us, and if
|
|
|
4ff8bf |
- * they all did, we'd hit kernel limits. We now hard-code 64k as a
|
|
|
4ff8bf |
- * good limit, like systemd does: that's enough to avoid DoS from
|
|
|
4ff8bf |
- * anything short of multiple uids conspiring against us.
|
|
|
4ff8bf |
+ * they all did, we'd hit kernel limits. We now hard-code a good
|
|
|
4ff8bf |
+ * limit that is enough to avoid DoS from anything short of multiple
|
|
|
4ff8bf |
+ * uids conspiring against us, much like systemd does.
|
|
|
4ff8bf |
*/
|
|
|
4ff8bf |
- if (!_dbus_rlimit_raise_fd_limit_if_privileged (65536, &error))
|
|
|
4ff8bf |
+ if (!_dbus_rlimit_raise_fd_limit (&error))
|
|
|
4ff8bf |
{
|
|
|
4ff8bf |
bus_context_log (context, DBUS_SYSTEM_LOG_WARNING,
|
|
|
4ff8bf |
"%s: %s", error.name, error.message);
|
|
|
4ff8bf |
diff --git a/dbus/dbus-sysdeps-util-unix.c b/dbus/dbus-sysdeps-util-unix.c
|
|
|
4ff8bf |
index 2be5b779..7c4c3604 100644
|
|
|
4ff8bf |
--- a/dbus/dbus-sysdeps-util-unix.c
|
|
|
4ff8bf |
+++ b/dbus/dbus-sysdeps-util-unix.c
|
|
|
4ff8bf |
@@ -406,23 +406,15 @@ _dbus_rlimit_save_fd_limit (DBusError *error)
|
|
|
4ff8bf |
return self;
|
|
|
4ff8bf |
}
|
|
|
4ff8bf |
|
|
|
4ff8bf |
+/* Enough fds that we shouldn't run out, even if several uids work
|
|
|
4ff8bf |
+ * together to carry out a denial-of-service attack. This happens to be
|
|
|
4ff8bf |
+ * the same number that systemd < 234 would normally use. */
|
|
|
4ff8bf |
+#define ENOUGH_FDS 65536
|
|
|
4ff8bf |
+
|
|
|
4ff8bf |
dbus_bool_t
|
|
|
4ff8bf |
-_dbus_rlimit_raise_fd_limit_if_privileged (unsigned int desired,
|
|
|
4ff8bf |
- DBusError *error)
|
|
|
4ff8bf |
+_dbus_rlimit_raise_fd_limit (DBusError *error)
|
|
|
4ff8bf |
{
|
|
|
4ff8bf |
- struct rlimit lim;
|
|
|
4ff8bf |
-
|
|
|
4ff8bf |
- /* No point to doing this practically speaking
|
|
|
4ff8bf |
- * if we're not uid 0. We expect the system
|
|
|
4ff8bf |
- * bus to use this before we change UID, and
|
|
|
4ff8bf |
- * the session bus takes the Linux default,
|
|
|
4ff8bf |
- * currently 1024 for cur and 4096 for max.
|
|
|
4ff8bf |
- */
|
|
|
4ff8bf |
- if (getuid () != 0)
|
|
|
4ff8bf |
- {
|
|
|
4ff8bf |
- /* not an error, we're probably the session bus */
|
|
|
4ff8bf |
- return TRUE;
|
|
|
4ff8bf |
- }
|
|
|
4ff8bf |
+ struct rlimit old, lim;
|
|
|
4ff8bf |
|
|
|
4ff8bf |
if (getrlimit (RLIMIT_NOFILE, &lim) < 0)
|
|
|
4ff8bf |
{
|
|
|
4ff8bf |
@@ -431,22 +423,43 @@ _dbus_rlimit_raise_fd_limit_if_privileged (unsigned int desired,
|
|
|
4ff8bf |
return FALSE;
|
|
|
4ff8bf |
}
|
|
|
4ff8bf |
|
|
|
4ff8bf |
- if (lim.rlim_cur == RLIM_INFINITY || lim.rlim_cur >= desired)
|
|
|
4ff8bf |
+ old = lim;
|
|
|
4ff8bf |
+
|
|
|
4ff8bf |
+ if (getuid () == 0)
|
|
|
4ff8bf |
{
|
|
|
4ff8bf |
- /* not an error, everything is fine */
|
|
|
4ff8bf |
- return TRUE;
|
|
|
4ff8bf |
+ /* We are privileged, so raise the soft limit to at least
|
|
|
4ff8bf |
+ * ENOUGH_FDS, and the hard limit to at least the desired soft
|
|
|
4ff8bf |
+ * limit. This assumes we can exercise CAP_SYS_RESOURCE on Linux,
|
|
|
4ff8bf |
+ * or other OSs' equivalents. */
|
|
|
4ff8bf |
+ if (lim.rlim_cur != RLIM_INFINITY &&
|
|
|
4ff8bf |
+ lim.rlim_cur < ENOUGH_FDS)
|
|
|
4ff8bf |
+ lim.rlim_cur = ENOUGH_FDS;
|
|
|
4ff8bf |
+
|
|
|
4ff8bf |
+ if (lim.rlim_max != RLIM_INFINITY &&
|
|
|
4ff8bf |
+ lim.rlim_max < lim.rlim_cur)
|
|
|
4ff8bf |
+ lim.rlim_max = lim.rlim_cur;
|
|
|
4ff8bf |
}
|
|
|
4ff8bf |
|
|
|
4ff8bf |
- /* Ignore "maximum limit", assume we have the "superuser"
|
|
|
4ff8bf |
- * privileges. On Linux this is CAP_SYS_RESOURCE.
|
|
|
4ff8bf |
- */
|
|
|
4ff8bf |
- lim.rlim_cur = lim.rlim_max = desired;
|
|
|
4ff8bf |
+ /* Raise the soft limit to match the hard limit, which we can do even
|
|
|
4ff8bf |
+ * if we are unprivileged. In particular, systemd >= 240 will normally
|
|
|
4ff8bf |
+ * set rlim_cur to 1024 and rlim_max to 512*1024, recent Debian
|
|
|
4ff8bf |
+ * versions end up setting rlim_cur to 1024 and rlim_max to 1024*1024,
|
|
|
4ff8bf |
+ * and older and non-systemd Linux systems would typically set rlim_cur
|
|
|
4ff8bf |
+ * to 1024 and rlim_max to 4096. */
|
|
|
4ff8bf |
+ if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max)
|
|
|
4ff8bf |
+ lim.rlim_cur = lim.rlim_max;
|
|
|
4ff8bf |
+
|
|
|
4ff8bf |
+ /* Early-return if there is nothing to do. */
|
|
|
4ff8bf |
+ if (lim.rlim_max == old.rlim_max &&
|
|
|
4ff8bf |
+ lim.rlim_cur == old.rlim_cur)
|
|
|
4ff8bf |
+ return TRUE;
|
|
|
4ff8bf |
|
|
|
4ff8bf |
if (setrlimit (RLIMIT_NOFILE, &lim) < 0)
|
|
|
4ff8bf |
{
|
|
|
4ff8bf |
dbus_set_error (error, _dbus_error_from_errno (errno),
|
|
|
4ff8bf |
- "Failed to set fd limit to %u: %s",
|
|
|
4ff8bf |
- desired, _dbus_strerror (errno));
|
|
|
4ff8bf |
+ "Failed to set fd limit to %lu: %s",
|
|
|
4ff8bf |
+ (unsigned long) lim.rlim_cur,
|
|
|
4ff8bf |
+ _dbus_strerror (errno));
|
|
|
4ff8bf |
return FALSE;
|
|
|
4ff8bf |
}
|
|
|
4ff8bf |
|
|
|
4ff8bf |
@@ -485,8 +498,7 @@ _dbus_rlimit_save_fd_limit (DBusError *error)
|
|
|
4ff8bf |
}
|
|
|
4ff8bf |
|
|
|
4ff8bf |
dbus_bool_t
|
|
|
4ff8bf |
-_dbus_rlimit_raise_fd_limit_if_privileged (unsigned int desired,
|
|
|
4ff8bf |
- DBusError *error)
|
|
|
4ff8bf |
+_dbus_rlimit_raise_fd_limit (DBusError *error)
|
|
|
4ff8bf |
{
|
|
|
4ff8bf |
fd_limit_not_supported (error);
|
|
|
4ff8bf |
return FALSE;
|
|
|
4ff8bf |
diff --git a/dbus/dbus-sysdeps-util-win.c b/dbus/dbus-sysdeps-util-win.c
|
|
|
4ff8bf |
index 1ef4ae6c..1c1d9f7d 100644
|
|
|
4ff8bf |
--- a/dbus/dbus-sysdeps-util-win.c
|
|
|
4ff8bf |
+++ b/dbus/dbus-sysdeps-util-win.c
|
|
|
4ff8bf |
@@ -273,8 +273,7 @@ _dbus_rlimit_save_fd_limit (DBusError *error)
|
|
|
4ff8bf |
}
|
|
|
4ff8bf |
|
|
|
4ff8bf |
dbus_bool_t
|
|
|
4ff8bf |
-_dbus_rlimit_raise_fd_limit_if_privileged (unsigned int desired,
|
|
|
4ff8bf |
- DBusError *error)
|
|
|
4ff8bf |
+_dbus_rlimit_raise_fd_limit (DBusError *error)
|
|
|
4ff8bf |
{
|
|
|
4ff8bf |
fd_limit_not_supported (error);
|
|
|
4ff8bf |
return FALSE;
|
|
|
4ff8bf |
diff --git a/dbus/dbus-sysdeps.h b/dbus/dbus-sysdeps.h
|
|
|
4ff8bf |
index ef786ecc..0b9d7696 100644
|
|
|
4ff8bf |
--- a/dbus/dbus-sysdeps.h
|
|
|
4ff8bf |
+++ b/dbus/dbus-sysdeps.h
|
|
|
4ff8bf |
@@ -698,8 +698,7 @@ dbus_bool_t _dbus_replace_install_prefix (DBusString *path);
|
|
|
4ff8bf |
typedef struct DBusRLimit DBusRLimit;
|
|
|
4ff8bf |
|
|
|
4ff8bf |
DBusRLimit *_dbus_rlimit_save_fd_limit (DBusError *error);
|
|
|
4ff8bf |
-dbus_bool_t _dbus_rlimit_raise_fd_limit_if_privileged (unsigned int desired,
|
|
|
4ff8bf |
- DBusError *error);
|
|
|
4ff8bf |
+dbus_bool_t _dbus_rlimit_raise_fd_limit (DBusError *error);
|
|
|
4ff8bf |
dbus_bool_t _dbus_rlimit_restore_fd_limit (DBusRLimit *saved,
|
|
|
4ff8bf |
DBusError *error);
|
|
|
4ff8bf |
void _dbus_rlimit_free (DBusRLimit *lim);
|
|
|
4ff8bf |
--
|
|
|
4ff8bf |
GitLab
|
|
|
4ff8bf |
|