]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-event: ref event loop while in sd_event_prepare() ot sd_event_run()
authorLennart Poettering <lennart@poettering.net>
Mon, 23 Nov 2020 14:38:00 +0000 (15:38 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 1 Dec 2020 14:10:50 +0000 (15:10 +0100)
sd_event_prepare() invokes callbacks that might drop the last user ref
on our event loop. Let's make sure we keep an explicit ref around it, so
that we won't end up with an invalid pointer. Similar in sd_event_run().

Basically, any function that is publically callable that might end up
invoking callbacks should ref the relevant objects to be protected
against callbacks destroying these objects while we still want to access
them. We did this correctly in sd_event_dispatch() and sd_event_loop(),
but these are not the only ones which are callable from the outside.

src/libsystemd/sd-event/sd-event.c

index d5a3cb79a368a81b7625210b948921ac1434b028..9894e6f4b4d16e2247e2f01ea9c4ef05e8b2a3da 100644 (file)
@@ -3407,7 +3407,6 @@ static int event_prepare(sd_event *e) {
 
 static int dispatch_exit(sd_event *e) {
         sd_event_source *p;
-        _cleanup_(sd_event_unrefp) sd_event *ref = NULL;
         int r;
 
         assert(e);
@@ -3418,7 +3417,7 @@ static int dispatch_exit(sd_event *e) {
                 return 0;
         }
 
-        ref = sd_event_ref(e);
+        _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
         e->iteration++;
         e->state = SD_EVENT_EXITING;
         r = source_dispatch(p);
@@ -3514,6 +3513,9 @@ _public_ int sd_event_prepare(sd_event *e) {
          * syscalls */
         assert_return(!e->default_event_ptr || e->tid == gettid(), -EREMOTEIO);
 
+        /* Make sure that none of the preparation callbacks ends up freeing the event source under our feet */
+        _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
+
         if (e->exit_requested)
                 goto pending;
 
@@ -3719,9 +3721,8 @@ _public_ int sd_event_dispatch(sd_event *e) {
 
         p = event_next_pending(e);
         if (p) {
-                _cleanup_(sd_event_unrefp) sd_event *ref = NULL;
+                _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
 
-                ref = sd_event_ref(e);
                 e->state = SD_EVENT_RUNNING;
                 r = source_dispatch(p);
                 e->state = SD_EVENT_INITIAL;
@@ -3771,6 +3772,9 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
                 }
         }
 
+        /* Make sure that none of the preparation callbacks ends up freeing the event source under our feet */
+        _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = sd_event_ref(e);
+
         r = sd_event_prepare(e);
         if (r == 0)
                 /* There was nothing? Then wait... */
@@ -3792,7 +3796,6 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
 }
 
 _public_ int sd_event_loop(sd_event *e) {
-        _cleanup_(sd_event_unrefp) sd_event *ref = NULL;
         int r;
 
         assert_return(e, -EINVAL);
@@ -3800,7 +3803,7 @@ _public_ int sd_event_loop(sd_event *e) {
         assert_return(!event_pid_changed(e), -ECHILD);
         assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
 
-        ref = sd_event_ref(e);
+        _unused_ _cleanup_(sd_event_unrefp) sd_event *ref = NULL;
 
         while (e->state != SD_EVENT_FINISHED) {
                 r = sd_event_run(e, (uint64_t) -1);