diff options
author | Stefan Metzmacher <metze@samba.org> | 2011-08-11 12:45:26 +0200 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2011-08-12 11:08:00 +0200 |
commit | edeb41aa204ca9814602bee1c7d9be32e5f737b5 (patch) | |
tree | 99fdacbd4d6be505c2509c6a9c13be6580895952 | |
parent | 02cb2052d8c68a3ba6dc8a19f580f25039321a75 (diff) | |
download | samba-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.c | 45 |
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: |