s3:libsmb: use tevent_req_defer_callback() unless there's only one request in cli_smb...
authorStefan Metzmacher <metze@samba.org>
Thu, 11 Aug 2011 10:45:26 +0000 (12:45 +0200)
committerStefan Metzmacher <metze@samba.org>
Fri, 12 Aug 2011 09:08:00 +0000 (11:08 +0200)
Callers of tevent_req_done() (or similar functions) have to return directly.
Otherwise the callback could invalidate the current stack state,
which is likely to trigger segfaults.

If there was only one pending request and we just got the response
for that one, we can use tevent_req_done() directly.

Otherwise there're more pending requests and we need to call
cli_state_receive_next() or we got the response for chained requests.
Both means that we have to use tevent_req_defer_callback().

metze

source3/libsmb/async_smb.c

index 2c053421dd5f90017b72cf6991a321b9ae6342f4..5396d2288131519baeb1f490169f7473ae75df66 100644 (file)
@@ -700,20 +700,57 @@ static void cli_smb_received(struct tevent_req *subreq)
                cli_smb_req_unset_pending(req);
                state->chain_num = 0;
                state->chain_length = 1;
+
+               if (talloc_array_length(cli->conn.pending) == 0) {
+                       tevent_req_done(req);
+                       TALLOC_FREE(frame);
+                       return;
+               }
+
+               tevent_req_defer_callback(req, state->ev);
                tevent_req_done(req);
        } else {
                struct tevent_req **chain = talloc_move(frame,
                                            &state->chained_requests);
                int num_chained = talloc_array_length(chain);
 
+               /*
+                * We steal the inbuf to the chain,
+                * so that it will stay until all
+                * requests of the chain are finished.
+                *
+                * Each requests in the chain will
+                * hold a talloc reference to the chain.
+                * This way we do not expose the talloc_reference()
+                * behavior to the callers.
+                */
+               talloc_steal(chain, inbuf);
+
                for (i=0; i<num_chained; i++) {
-                       state = tevent_req_data(chain[i], struct
-                                               cli_smb_state);
+                       struct tevent_req **ref;
+
+                       req = chain[i];
+                       state = tevent_req_data(req, struct cli_smb_state);
+
+                       cli_smb_req_unset_pending(req);
+
+                       /*
+                        * as we finish multiple requests here
+                        * we need to defer the callbacks as
+                        * they could destroy our current stack state.
+                        */
+                       tevent_req_defer_callback(req, state->ev);
+
+                       ref = talloc_reference(state, chain);
+                       if (tevent_req_nomem(ref, req)) {
+                               continue;
+                       }
+
                        state->inbuf = inbuf;
                        state->chain_num = i;
                        state->chain_length = num_chained;
-                       cli_smb_req_unset_pending(req);
-                       tevent_req_done(chain[i]);
+
+                       tevent_req_done(req);
                }
        }
  done: