summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdrian Vovk <adrianvovk@gmail.com>2024-03-22 00:28:38 +0100
committerYu Watanabe <watanabe.yu+github@gmail.com>2024-03-28 05:35:37 +0100
commit1eba24dac443eae187a9e02c5c8e7f6036e091d5 (patch)
treeb5fd50b076bb0ce8e7adc4564964bc7668473553
parentrun: fix generated unit name clash after soft-reboot (diff)
downloadsystemd-1eba24dac443eae187a9e02c5c8e7f6036e091d5.tar.xz
systemd-1eba24dac443eae187a9e02c5c8e7f6036e091d5.zip
homed: Release(): fix assertion failure
This fixes a race condition crash in homed that would happen in the following sequence of events: 1. Client 1 takes a ref on the home area 2. Client 1 calls some method via dbus 3. Client 2 calls Release() In homed, the Release() would check if a ref is still held (in this case: yes it is) and returns an error. Except that is done through a code-path that asserts that no operations are ongoing. In this case, it's valid to have an ongoing operation, and so the assertion fails causing homed to crash.
-rw-r--r--src/home/homed-home.c56
-rw-r--r--src/home/meson.build5
-rw-r--r--src/home/test-homed-regression-31896.c35
-rwxr-xr-xtest/units/testsuite-46.sh4
4 files changed, 73 insertions, 27 deletions
diff --git a/src/home/homed-home.c b/src/home/homed-home.c
index 447e8c597c..757881c2e6 100644
--- a/src/home/homed-home.c
+++ b/src/home/homed-home.c
@@ -2810,7 +2810,8 @@ static int home_dispatch_acquire(Home *h, Operation *o) {
case HOME_ABSENT:
r = sd_bus_error_setf(&error, BUS_ERROR_HOME_ABSENT,
"Home %s is currently missing or not plugged in.", h->user_name);
- goto check;
+ operation_result(o, r, &error);
+ return 1;
case HOME_INACTIVE:
case HOME_DIRTY:
@@ -2841,7 +2842,6 @@ static int home_dispatch_acquire(Home *h, Operation *o) {
if (r >= 0)
r = call(h, o->secret, for_state, &error);
- check:
if (r != 0) /* failure or completed */
operation_result(o, r, &error);
else /* ongoing */
@@ -2871,33 +2871,35 @@ static int home_dispatch_release(Home *h, Operation *o) {
assert(o);
assert(o->type == OPERATION_RELEASE);
- if (home_is_referenced(h))
+ if (home_is_referenced(h)) {
/* If there's now a reference again, then let's abort the release attempt */
r = sd_bus_error_setf(&error, BUS_ERROR_HOME_BUSY, "Home %s is currently referenced.", h->user_name);
- else {
- switch (home_get_state(h)) {
-
- case HOME_UNFIXATED:
- case HOME_ABSENT:
- case HOME_INACTIVE:
- case HOME_DIRTY:
- r = 1; /* done */
- break;
-
- case HOME_LOCKED:
- r = sd_bus_error_setf(&error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
- break;
-
- case HOME_ACTIVE:
- case HOME_LINGERING:
- r = home_deactivate_internal(h, false, &error);
- break;
-
- default:
- /* All other cases means we are currently executing an operation, which means the job remains
- * pending. */
- return 0;
- }
+ operation_result(o, r, &error);
+ return 1;
+ }
+
+ switch (home_get_state(h)) {
+
+ case HOME_UNFIXATED:
+ case HOME_ABSENT:
+ case HOME_INACTIVE:
+ case HOME_DIRTY:
+ r = 1; /* done */
+ break;
+
+ case HOME_LOCKED:
+ r = sd_bus_error_setf(&error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
+ break;
+
+ case HOME_ACTIVE:
+ case HOME_LINGERING:
+ r = home_deactivate_internal(h, false, &error);
+ break;
+
+ default:
+ /* All other cases means we are currently executing an operation, which means the job remains
+ * pending. */
+ return 0;
}
assert(!h->current_operation);
diff --git a/src/home/meson.build b/src/home/meson.build
index a1e912f6a0..f573c5fb15 100644
--- a/src/home/meson.build
+++ b/src/home/meson.build
@@ -106,6 +106,11 @@ executables += [
threads,
],
},
+ test_template + {
+ 'sources' : files('test-homed-regression-31896.c'),
+ 'conditions' : ['ENABLE_HOMED'],
+ 'type' : 'manual',
+ },
]
modules += [
diff --git a/src/home/test-homed-regression-31896.c b/src/home/test-homed-regression-31896.c
new file mode 100644
index 0000000000..1530a2f22b
--- /dev/null
+++ b/src/home/test-homed-regression-31896.c
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "bus-locator.h"
+#include "main-func.h"
+#include "tests.h"
+
+static int run(int argc, char **argv) {
+ _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *ref = NULL;
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ const char *username = NULL;
+
+ /* This is a regression test for the following bug:
+ * https://github.com/systemd/systemd/pull/31896
+ * It is run as part of TEST-46-HOMED
+ */
+
+ test_setup_logging(LOG_DEBUG);
+ assert_se(sd_bus_open_system(&bus) >= 0);
+
+ assert_se(argc == 2);
+ username = argv[1];
+
+ assert_se(bus_call_method(bus, bus_home_mgr, "RefHomeUnrestricted", NULL, &ref, "sb", username, true) >= 0);
+
+ assert_se(bus_call_method_async(bus, NULL, bus_home_mgr, "AuthenticateHome", NULL, NULL, "ss", username, "{}") >= 0);
+ assert_se(sd_bus_flush(bus) >= 0);
+
+ (void) bus_call_method(bus, bus_home_mgr, "ReleaseHome", &error, NULL, "s", username);
+ assert_se(!sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY)); /* Make sure we didn't crash */
+
+ return 0;
+}
+
+DEFINE_MAIN_FUNCTION(run);
diff --git a/test/units/testsuite-46.sh b/test/units/testsuite-46.sh
index b20b39d762..7a2bee8179 100755
--- a/test/units/testsuite-46.sh
+++ b/test/units/testsuite-46.sh
@@ -207,6 +207,10 @@ PASSWORD=xEhErW0ndafV4s homectl with test-user -- rm /home/test-user/xyz
PASSWORD=xEhErW0ndafV4s homectl with test-user -- test ! -f /home/test-user/xyz
(! PASSWORD=xEhErW0ndafV4s homectl with test-user -- test -f /home/test-user/xyz)
+# Regression tests
+wait_for_state test-user inactive
+/usr/lib/systemd/tests/unit-tests/manual/test-homed-regression-31896 test-user
+
wait_for_state test-user inactive
homectl remove test-user