summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2011-08-11 12:45:26 +0200
committerStefan Metzmacher <metze@samba.org>2011-08-12 11:08:00 +0200
commitedeb41aa204ca9814602bee1c7d9be32e5f737b5 (patch)
tree99fdacbd4d6be505c2509c6a9c13be6580895952
parent02cb2052d8c68a3ba6dc8a19f580f25039321a75 (diff)
downloadsamba-edeb41aa204ca9814602bee1c7d9be32e5f737b5.tar.gz
samba-edeb41aa204ca9814602bee1c7d9be32e5f737b5.tar.bz2
samba-edeb41aa204ca9814602bee1c7d9be32e5f737b5.zip
s3:libsmb: use tevent_req_defer_callback() unless there's only one request in cli_smb_received()
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
-rw-r--r--source3/libsmb/async_smb.c45
1 files changed, 41 insertions, 4 deletions
diff --git a/source3/libsmb/async_smb.c b/source3/libsmb/async_smb.c
index 2c053421dd..5396d22881 100644
--- a/source3/libsmb/async_smb.c
+++ b/source3/libsmb/async_smb.c
@@ -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: