ctdb-daemon: Schedule running of callback if there are no event scripts
authorMartin Schwenke <martin@meltin.net>
Fri, 26 Aug 2016 06:29:47 +0000 (16:29 +1000)
committerAmitay Isaacs <amitay@samba.org>
Thu, 1 Sep 2016 11:30:10 +0000 (13:30 +0200)
The callback should never be called before an immediate return.  The
callback might reply to a control and the caller of
ctdb_event_script_callback_v() may not have assigned/stolen the
pointer to control structure into the private data.  Therefore,
calling the callback can dereference an uninitialised pointer to the
control structure when attempting to reply.

ctdb_event_script_callback_v() must succeed when there are no event
scripts.  On success the caller will mark the call as asynchronous and
expect the callback to be called.  Given that it can't be called
before return then it needs to be scheduled.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=12180

Signed-off-by: Martin Schwenke <martin@meltin.net>
Reviewed-by: Amitay Isaacs <amitay@gmail.com>
ctdb/server/eventscript.c

index bd5bc0d6b9445da000b84a8dc8f3f8974d01e557..a5f26805a09b7365a9673d7cd51b008376fcbbf2 100644 (file)
@@ -699,6 +699,62 @@ static int remove_callback(struct event_script_callback *callback)
        return 0;
 }
 
+struct schedule_callback_state {
+       struct ctdb_context *ctdb;
+       void (*callback)(struct ctdb_context *, int, void *);
+       void *private_data;
+       int status;
+       struct tevent_immediate *im;
+};
+
+static void schedule_callback_handler(struct tevent_context *ctx,
+                                     struct tevent_immediate *im,
+                                     void *private_data)
+{
+       struct schedule_callback_state *state =
+               talloc_get_type_abort(private_data,
+                                     struct schedule_callback_state);
+
+       if (state->callback != NULL) {
+               state->callback(state->ctdb, state->status,
+                               state->private_data);
+       }
+       talloc_free(state);
+}
+
+static int
+schedule_callback_immediate(struct ctdb_context *ctdb,
+                           void (*callback)(struct ctdb_context *,
+                                            int, void *),
+                           void *private_data,
+                           int status)
+{
+       struct schedule_callback_state *state;
+       struct tevent_immediate *im;
+
+       state = talloc_zero(ctdb, struct schedule_callback_state);
+       if (state == NULL) {
+               DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
+               return -1;
+       }
+       im = tevent_create_immediate(state);
+       if (im == NULL) {
+               DEBUG(DEBUG_ERR, (__location__ " out of memory\n"));
+               talloc_free(state);
+               return -1;
+       }
+
+       state->ctdb = ctdb;
+       state->callback = callback;
+       state->private_data = private_data;
+       state->status = status;
+       state->im = im;
+
+       tevent_schedule_immediate(im, ctdb->ev,
+                                 schedule_callback_handler, state);
+       return 0;
+}
+
 /*
   run the event script in the background, calling the callback when
   finished
@@ -825,8 +881,14 @@ static int ctdb_event_script_callback_v(struct ctdb_context *ctdb,
 
        /* Nothing to do? */
        if (state->scripts->num_scripts == 0) {
-               callback(ctdb, 0, private_data);
+               int ret = schedule_callback_immediate(ctdb, callback,
+                                                     private_data, 0);
                talloc_free(state);
+               if (ret != 0) {
+                       DEBUG(DEBUG_ERR,
+                             ("Unable to schedule callback for 0 scripts\n"));
+                       return 1;
+               }
                return 0;
        }