teknoraver / rpms / systemd

Forked from rpms/systemd 2 months ago
Clone

Blame SOURCES/0677-core-Check-unit-start-rate-limiting-earlier.patch

eb8b6e
From 6f3b6308bb5705fdcadec3f4cac105211bc0a3e2 Mon Sep 17 00:00:00 2001
eb8b6e
From: Daan De Meyer <daan.j.demeyer@gmail.com>
eb8b6e
Date: Tue, 24 Aug 2021 16:46:47 +0100
eb8b6e
Subject: [PATCH] core: Check unit start rate limiting earlier
eb8b6e
eb8b6e
Fixes #17433. Currently, if any of the validations we do before we
eb8b6e
check start rate limiting fail, we can still enter a busy loop as
eb8b6e
no rate limiting gets applied. A common occurence of this scenario
eb8b6e
is path units triggering a service that fails a condition check.
eb8b6e
eb8b6e
To fix the issue, we simply move up start rate limiting checks to
eb8b6e
be the first thing we do when starting a unit. To achieve this,
eb8b6e
we add a new method to the unit vtable and implement it for the
eb8b6e
relevant unit types so that we can do the start rate limit checks
eb8b6e
earlier on.
eb8b6e
eb8b6e
(cherry picked from commit 9727f2427ff6b2e1f4ab927cc57ad8e888f04e95)
eb8b6e
eb8b6e
Related: #2037395
eb8b6e
eb8b6e
[msekleta: I've deleted part of the original commit that adds test for
eb8b6e
issue #17433. This was necessary because upstream commit assumes newer
eb8b6e
organization of the test code which we don't have in RHEL-8 tree. As
eb8b6e
a consequence we must add explicit test for this in the internal
eb8b6e
test-suite.]
eb8b6e
---
eb8b6e
 src/core/automount.c | 23 +++++++++++++++++------
eb8b6e
 src/core/mount.c     | 23 +++++++++++++++++------
eb8b6e
 src/core/path.c      | 23 +++++++++++++++++------
eb8b6e
 src/core/service.c   | 25 ++++++++++++++++++-------
eb8b6e
 src/core/socket.c    | 23 +++++++++++++++++------
eb8b6e
 src/core/swap.c      | 23 +++++++++++++++++------
eb8b6e
 src/core/timer.c     | 22 ++++++++++++++++------
eb8b6e
 src/core/unit.c      | 14 ++++++++++----
eb8b6e
 src/core/unit.h      |  4 ++++
eb8b6e
 9 files changed, 133 insertions(+), 47 deletions(-)
eb8b6e
eb8b6e
diff --git a/src/core/automount.c b/src/core/automount.c
eb8b6e
index 2bc160cb07..5e16adabb5 100644
eb8b6e
--- a/src/core/automount.c
eb8b6e
+++ b/src/core/automount.c
eb8b6e
@@ -808,12 +808,6 @@ static int automount_start(Unit *u) {
eb8b6e
                 return -ENOENT;
eb8b6e
         }
eb8b6e
 
eb8b6e
-        r = unit_test_start_limit(u);
eb8b6e
-        if (r < 0) {
eb8b6e
-                automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT);
eb8b6e
-                return r;
eb8b6e
-        }
eb8b6e
-
eb8b6e
         r = unit_acquire_invocation_id(u);
eb8b6e
         if (r < 0)
eb8b6e
                 return r;
eb8b6e
@@ -1077,6 +1071,21 @@ static bool automount_supported(void) {
eb8b6e
         return supported;
eb8b6e
 }
eb8b6e
 
eb8b6e
+static int automount_test_start_limit(Unit *u) {
eb8b6e
+        Automount *a = AUTOMOUNT(u);
eb8b6e
+        int r;
eb8b6e
+
eb8b6e
+        assert(a);
eb8b6e
+
eb8b6e
+        r = unit_test_start_limit(u);
eb8b6e
+        if (r < 0) {
eb8b6e
+                automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT);
eb8b6e
+                return r;
eb8b6e
+        }
eb8b6e
+
eb8b6e
+        return 0;
eb8b6e
+}
eb8b6e
+
eb8b6e
 static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = {
eb8b6e
         [AUTOMOUNT_SUCCESS] = "success",
eb8b6e
         [AUTOMOUNT_FAILURE_RESOURCES] = "resources",
eb8b6e
@@ -1135,4 +1144,6 @@ const UnitVTable automount_vtable = {
eb8b6e
                         [JOB_FAILED]     = "Failed to unset automount %s.",
eb8b6e
                 },
eb8b6e
         },
eb8b6e
+
eb8b6e
+        .test_start_limit = automount_test_start_limit,
eb8b6e
 };
eb8b6e
diff --git a/src/core/mount.c b/src/core/mount.c
eb8b6e
index 4cb49d845d..619f64d5b7 100644
eb8b6e
--- a/src/core/mount.c
eb8b6e
+++ b/src/core/mount.c
eb8b6e
@@ -1065,12 +1065,6 @@ static int mount_start(Unit *u) {
eb8b6e
 
eb8b6e
         assert(IN_SET(m->state, MOUNT_DEAD, MOUNT_FAILED));
eb8b6e
 
eb8b6e
-        r = unit_test_start_limit(u);
eb8b6e
-        if (r < 0) {
eb8b6e
-                mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT);
eb8b6e
-                return r;
eb8b6e
-        }
eb8b6e
-
eb8b6e
         r = unit_acquire_invocation_id(u);
eb8b6e
         if (r < 0)
eb8b6e
                 return r;
eb8b6e
@@ -1953,6 +1947,21 @@ static int mount_control_pid(Unit *u) {
eb8b6e
         return m->control_pid;
eb8b6e
 }
eb8b6e
 
eb8b6e
+static int mount_test_start_limit(Unit *u) {
eb8b6e
+        Mount *m = MOUNT(u);
eb8b6e
+        int r;
eb8b6e
+
eb8b6e
+        assert(m);
eb8b6e
+
eb8b6e
+        r = unit_test_start_limit(u);
eb8b6e
+        if (r < 0) {
eb8b6e
+                mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT);
eb8b6e
+                return r;
eb8b6e
+        }
eb8b6e
+
eb8b6e
+        return 0;
eb8b6e
+}
eb8b6e
+
eb8b6e
 static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = {
eb8b6e
         [MOUNT_EXEC_MOUNT] = "ExecMount",
eb8b6e
         [MOUNT_EXEC_UNMOUNT] = "ExecUnmount",
eb8b6e
@@ -2044,4 +2053,6 @@ const UnitVTable mount_vtable = {
eb8b6e
                         [JOB_TIMEOUT]    = "Timed out unmounting %s.",
eb8b6e
                 },
eb8b6e
         },
eb8b6e
+
eb8b6e
+        .test_start_limit = mount_test_start_limit,
eb8b6e
 };
eb8b6e
diff --git a/src/core/path.c b/src/core/path.c
eb8b6e
index 4bccc0396b..1e69a1f05f 100644
eb8b6e
--- a/src/core/path.c
eb8b6e
+++ b/src/core/path.c
eb8b6e
@@ -565,12 +565,6 @@ static int path_start(Unit *u) {
eb8b6e
                 return -ENOENT;
eb8b6e
         }
eb8b6e
 
eb8b6e
-        r = unit_test_start_limit(u);
eb8b6e
-        if (r < 0) {
eb8b6e
-                path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT);
eb8b6e
-                return r;
eb8b6e
-        }
eb8b6e
-
eb8b6e
         r = unit_acquire_invocation_id(u);
eb8b6e
         if (r < 0)
eb8b6e
                 return r;
eb8b6e
@@ -730,6 +724,21 @@ static void path_reset_failed(Unit *u) {
eb8b6e
         p->result = PATH_SUCCESS;
eb8b6e
 }
eb8b6e
 
eb8b6e
+static int path_test_start_limit(Unit *u) {
eb8b6e
+        Path *p = PATH(u);
eb8b6e
+        int r;
eb8b6e
+
eb8b6e
+        assert(p);
eb8b6e
+
eb8b6e
+        r = unit_test_start_limit(u);
eb8b6e
+        if (r < 0) {
eb8b6e
+                path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT);
eb8b6e
+                return r;
eb8b6e
+        }
eb8b6e
+
eb8b6e
+        return 0;
eb8b6e
+}
eb8b6e
+
eb8b6e
 static const char* const path_type_table[_PATH_TYPE_MAX] = {
eb8b6e
         [PATH_EXISTS] = "PathExists",
eb8b6e
         [PATH_EXISTS_GLOB] = "PathExistsGlob",
eb8b6e
@@ -782,4 +791,6 @@ const UnitVTable path_vtable = {
eb8b6e
 
eb8b6e
         .bus_vtable = bus_path_vtable,
eb8b6e
         .bus_set_property = bus_path_set_property,
eb8b6e
+
eb8b6e
+        .test_start_limit = path_test_start_limit,
eb8b6e
 };
eb8b6e
diff --git a/src/core/service.c b/src/core/service.c
eb8b6e
index 01cb1a375e..a21d98ab8f 100644
eb8b6e
--- a/src/core/service.c
eb8b6e
+++ b/src/core/service.c
eb8b6e
@@ -2356,13 +2356,6 @@ static int service_start(Unit *u) {
eb8b6e
 
eb8b6e
         assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED));
eb8b6e
 
eb8b6e
-        /* Make sure we don't enter a busy loop of some kind. */
eb8b6e
-        r = unit_test_start_limit(u);
eb8b6e
-        if (r < 0) {
eb8b6e
-                service_enter_dead(s, SERVICE_FAILURE_START_LIMIT_HIT, false);
eb8b6e
-                return r;
eb8b6e
-        }
eb8b6e
-
eb8b6e
         r = unit_acquire_invocation_id(u);
eb8b6e
         if (r < 0)
eb8b6e
                 return r;
eb8b6e
@@ -4050,6 +4043,22 @@ static bool service_needs_console(Unit *u) {
eb8b6e
                       SERVICE_FINAL_SIGKILL);
eb8b6e
 }
eb8b6e
 
eb8b6e
+static int service_test_start_limit(Unit *u) {
eb8b6e
+        Service *s = SERVICE(u);
eb8b6e
+        int r;
eb8b6e
+
eb8b6e
+        assert(s);
eb8b6e
+
eb8b6e
+        /* Make sure we don't enter a busy loop of some kind. */
eb8b6e
+        r = unit_test_start_limit(u);
eb8b6e
+        if (r < 0) {
eb8b6e
+                service_enter_dead(s, SERVICE_FAILURE_START_LIMIT_HIT, false);
eb8b6e
+                return r;
eb8b6e
+        }
eb8b6e
+
eb8b6e
+        return 0;
eb8b6e
+}
eb8b6e
+
eb8b6e
 static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
eb8b6e
         [SERVICE_RESTART_NO] = "no",
eb8b6e
         [SERVICE_RESTART_ON_SUCCESS] = "on-success",
eb8b6e
@@ -4191,4 +4200,6 @@ const UnitVTable service_vtable = {
eb8b6e
                         [JOB_FAILED]     = "Stopped (with error) %s.",
eb8b6e
                 },
eb8b6e
         },
eb8b6e
+
eb8b6e
+        .test_start_limit = service_test_start_limit,
eb8b6e
 };
eb8b6e
diff --git a/src/core/socket.c b/src/core/socket.c
eb8b6e
index 09491c6677..36d2e4f823 100644
eb8b6e
--- a/src/core/socket.c
eb8b6e
+++ b/src/core/socket.c
eb8b6e
@@ -2469,12 +2469,6 @@ static int socket_start(Unit *u) {
eb8b6e
 
eb8b6e
         assert(IN_SET(s->state, SOCKET_DEAD, SOCKET_FAILED));
eb8b6e
 
eb8b6e
-        r = unit_test_start_limit(u);
eb8b6e
-        if (r < 0) {
eb8b6e
-                socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT);
eb8b6e
-                return r;
eb8b6e
-        }
eb8b6e
-
eb8b6e
         r = unit_acquire_invocation_id(u);
eb8b6e
         if (r < 0)
eb8b6e
                 return r;
eb8b6e
@@ -3267,6 +3261,21 @@ static int socket_control_pid(Unit *u) {
eb8b6e
         return s->control_pid;
eb8b6e
 }
eb8b6e
 
eb8b6e
+static int socket_test_start_limit(Unit *u) {
eb8b6e
+        Socket *s = SOCKET(u);
eb8b6e
+        int r;
eb8b6e
+
eb8b6e
+        assert(s);
eb8b6e
+
eb8b6e
+        r = unit_test_start_limit(u);
eb8b6e
+        if (r < 0) {
eb8b6e
+                socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT);
eb8b6e
+                return r;
eb8b6e
+        }
eb8b6e
+
eb8b6e
+        return 0;
eb8b6e
+}
eb8b6e
+
eb8b6e
 static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
eb8b6e
         [SOCKET_EXEC_START_PRE] = "ExecStartPre",
eb8b6e
         [SOCKET_EXEC_START_CHOWN] = "ExecStartChown",
eb8b6e
@@ -3359,4 +3368,6 @@ const UnitVTable socket_vtable = {
eb8b6e
                         [JOB_TIMEOUT]    = "Timed out stopping %s.",
eb8b6e
                 },
eb8b6e
         },
eb8b6e
+
eb8b6e
+        .test_start_limit = socket_test_start_limit,
eb8b6e
 };
eb8b6e
diff --git a/src/core/swap.c b/src/core/swap.c
eb8b6e
index 823699699e..90fcd69300 100644
eb8b6e
--- a/src/core/swap.c
eb8b6e
+++ b/src/core/swap.c
eb8b6e
@@ -851,12 +851,6 @@ static int swap_start(Unit *u) {
eb8b6e
                 if (UNIT(other)->job && UNIT(other)->job->state == JOB_RUNNING)
eb8b6e
                         return -EAGAIN;
eb8b6e
 
eb8b6e
-        r = unit_test_start_limit(u);
eb8b6e
-        if (r < 0) {
eb8b6e
-                swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT);
eb8b6e
-                return r;
eb8b6e
-        }
eb8b6e
-
eb8b6e
         r = unit_acquire_invocation_id(u);
eb8b6e
         if (r < 0)
eb8b6e
                 return r;
eb8b6e
@@ -1458,6 +1452,21 @@ static int swap_control_pid(Unit *u) {
eb8b6e
         return s->control_pid;
eb8b6e
 }
eb8b6e
 
eb8b6e
+static int swap_test_start_limit(Unit *u) {
eb8b6e
+        Swap *s = SWAP(u);
eb8b6e
+        int r;
eb8b6e
+
eb8b6e
+        assert(s);
eb8b6e
+
eb8b6e
+        r = unit_test_start_limit(u);
eb8b6e
+        if (r < 0) {
eb8b6e
+                swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT);
eb8b6e
+                return r;
eb8b6e
+        }
eb8b6e
+
eb8b6e
+        return 0;
eb8b6e
+}
eb8b6e
+
eb8b6e
 static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = {
eb8b6e
         [SWAP_EXEC_ACTIVATE] = "ExecActivate",
eb8b6e
         [SWAP_EXEC_DEACTIVATE] = "ExecDeactivate",
eb8b6e
@@ -1547,4 +1556,6 @@ const UnitVTable swap_vtable = {
eb8b6e
                         [JOB_TIMEOUT]    = "Timed out deactivating swap %s.",
eb8b6e
                 },
eb8b6e
         },
eb8b6e
+
eb8b6e
+        .test_start_limit = swap_test_start_limit,
eb8b6e
 };
eb8b6e
diff --git a/src/core/timer.c b/src/core/timer.c
eb8b6e
index be16321296..fb9ae17990 100644
eb8b6e
--- a/src/core/timer.c
eb8b6e
+++ b/src/core/timer.c
eb8b6e
@@ -599,12 +599,6 @@ static int timer_start(Unit *u) {
eb8b6e
                 return -ENOENT;
eb8b6e
         }
eb8b6e
 
eb8b6e
-        r = unit_test_start_limit(u);
eb8b6e
-        if (r < 0) {
eb8b6e
-                timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT);
eb8b6e
-                return r;
eb8b6e
-        }
eb8b6e
-
eb8b6e
         r = unit_acquire_invocation_id(u);
eb8b6e
         if (r < 0)
eb8b6e
                 return r;
eb8b6e
@@ -829,6 +823,21 @@ static void timer_timezone_change(Unit *u) {
eb8b6e
         timer_enter_waiting(t, false);
eb8b6e
 }
eb8b6e
 
eb8b6e
+static int timer_test_start_limit(Unit *u) {
eb8b6e
+        Timer *t = TIMER(u);
eb8b6e
+        int r;
eb8b6e
+
eb8b6e
+        assert(t);
eb8b6e
+
eb8b6e
+        r = unit_test_start_limit(u);
eb8b6e
+        if (r < 0) {
eb8b6e
+                timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT);
eb8b6e
+                return r;
eb8b6e
+        }
eb8b6e
+
eb8b6e
+        return 0;
eb8b6e
+}
eb8b6e
+
eb8b6e
 static const char* const timer_base_table[_TIMER_BASE_MAX] = {
eb8b6e
         [TIMER_ACTIVE] = "OnActiveSec",
eb8b6e
         [TIMER_BOOT] = "OnBootSec",
eb8b6e
@@ -884,4 +893,5 @@ const UnitVTable timer_vtable = {
eb8b6e
         .bus_set_property = bus_timer_set_property,
eb8b6e
 
eb8b6e
         .can_transient = true,
eb8b6e
+        .test_start_limit = timer_test_start_limit,
eb8b6e
 };
eb8b6e
diff --git a/src/core/unit.c b/src/core/unit.c
eb8b6e
index fa94a67fdd..9005f79df3 100644
eb8b6e
--- a/src/core/unit.c
eb8b6e
+++ b/src/core/unit.c
eb8b6e
@@ -1726,10 +1726,16 @@ int unit_start(Unit *u) {
eb8b6e
 
eb8b6e
         assert(u);
eb8b6e
 
eb8b6e
-        /* If this is already started, then this will succeed. Note
eb8b6e
-         * that this will even succeed if this unit is not startable
eb8b6e
-         * by the user. This is relied on to detect when we need to
eb8b6e
-         * wait for units and when waiting is finished. */
eb8b6e
+        /* Check start rate limiting early so that failure conditions don't cause us to enter a busy loop. */
eb8b6e
+        if (UNIT_VTABLE(u)->test_start_limit) {
eb8b6e
+                int r = UNIT_VTABLE(u)->test_start_limit(u);
eb8b6e
+                if (r < 0)
eb8b6e
+                        return r;
eb8b6e
+        }
eb8b6e
+
eb8b6e
+        /* If this is already started, then this will succeed. Note that this will even succeed if this unit
eb8b6e
+         * is not startable by the user. This is relied on to detect when we need to wait for units and when
eb8b6e
+         * waiting is finished. */
eb8b6e
         state = unit_active_state(u);
eb8b6e
         if (UNIT_IS_ACTIVE_OR_RELOADING(state))
eb8b6e
                 return -EALREADY;
eb8b6e
diff --git a/src/core/unit.h b/src/core/unit.h
eb8b6e
index a8bc350b66..9e6f1bcf81 100644
eb8b6e
--- a/src/core/unit.h
eb8b6e
+++ b/src/core/unit.h
eb8b6e
@@ -567,6 +567,10 @@ typedef struct UnitVTable {
eb8b6e
         /* The bus vtable */
eb8b6e
         const sd_bus_vtable *bus_vtable;
eb8b6e
 
eb8b6e
+        /* If this function is set, it's invoked first as part of starting a unit to allow start rate
eb8b6e
+         * limiting checks to occur before we do anything else. */
eb8b6e
+        int (*test_start_limit)(Unit *u);
eb8b6e
+
eb8b6e
         /* The strings to print in status messages */
eb8b6e
         UnitStatusMessageFormats status_message_formats;
eb8b6e