The Samba-Bugzilla – Attachment 9547 Details for
Bug 10344
SessionLogoff on a signed connection with an outstanding notify request crashes smbd.
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Work-in-progress.
smb2sessionlogout-version2-crash.patchset (text/plain), 13.13 KB, created by
Jeremy Allison
on 2013-12-24 18:11:03 UTC
(
hide
)
Description:
Work-in-progress.
Filename:
MIME Type:
Creator:
Jeremy Allison
Created:
2013-12-24 18:11:03 UTC
Size:
13.13 KB
patch
obsolete
>diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c >index edef0cc..7d1066e 100644 >--- a/source3/smbd/smb2_sesssetup.c >+++ b/source3/smbd/smb2_sesssetup.c >@@ -455,10 +455,31 @@ static int pp_self_ref_destructor(struct smbd_smb2_session_setup_state **pp_stat > return 0; > } > >-static int smbd_smb2_session_setup_state_destructor(struct smbd_smb2_session_setup_state *state) >+static void remove_outstanding_session_refs(struct smbd_smb2_request *smb2req) > { > struct smbd_smb2_request *preq; > >+ for (preq = smb2req->sconn->smb2.requests; preq != NULL; preq = preq->next) { >+ if (preq == smb2req) { >+ /* Don't remove the session from the current >+ request in flight. */ >+ continue; >+ } >+ if (preq->session == smb2req->session) { >+ preq->session = NULL; >+ /* >+ * If we no longer have a session we can't >+ * sign or encrypt replies. >+ */ >+ preq->do_signing = false; >+ preq->do_encryption = false; >+ } >+ } >+ >+} >+ >+static int smbd_smb2_session_setup_state_destructor(struct smbd_smb2_session_setup_state *state) >+{ > /* > * If state->session is not NULL, > * we move the session from the session table to the request on failure >@@ -479,21 +500,7 @@ static int smbd_smb2_session_setup_state_destructor(struct smbd_smb2_session_set > * to it. > */ > >- for (preq = state->smb2req->sconn->smb2.requests; preq != NULL; preq = preq->next) { >- if (preq == state->smb2req) { >- continue; >- } >- if (preq->session == state->smb2req->session) { >- preq->session = NULL; >- /* >- * If we no longer have a session we can't >- * sign or encrypt replies. >- */ >- preq->do_signing = false; >- preq->do_encryption = false; >- } >- } >- >+ remove_outstanding_session_refs(state->smb2req); > return 0; > } > >@@ -788,44 +795,199 @@ static NTSTATUS smbd_smb2_session_setup_recv(struct tevent_req *req, > return status; > } > >+static bool any_outstanding_session_requests(const struct smbd_smb2_request *smb2req, >+ bool do_cancel) >+{ >+ bool ret = false; >+ struct smbd_smb2_request *preq; >+ >+ if (smb2req->session == NULL) { >+ return false; >+ } >+ for (preq = smb2req->sconn->smb2.requests; preq != NULL; preq = preq->next) { >+ if (preq == smb2req) { >+ continue; >+ } >+ if (preq->session == smb2req->session) { >+ if (do_cancel) { >+ tevent_req_cancel(preq->subreq); >+ } >+ ret = true; >+ } >+ } >+ return ret; >+} >+ >+static struct tevent_req *smbd_smb2_logoff_send(TALLOC_CTX *mem_ctx, >+ struct tevent_context *ev, >+ struct smbd_smb2_request *smb2req); >+static NTSTATUS smbd_smb2_logoff_recv(struct tevent_req *req); >+static void smbd_smb2_request_logoff_done(struct tevent_req *subreq); >+ > NTSTATUS smbd_smb2_request_process_logoff(struct smbd_smb2_request *req) > { > NTSTATUS status; >- DATA_BLOB outbody; >+ struct tevent_req *subreq; > > status = smbd_smb2_request_verify_sizes(req, 0x04); > if (!NT_STATUS_IS_OK(status)) { > return smbd_smb2_request_error(req, status); > } > >+ subreq = smbd_smb2_logoff_send(req, req->sconn->ev_ctx, req); >+ if (subreq == NULL) { >+ return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); >+ } >+ tevent_req_set_callback(subreq, smbd_smb2_request_logoff_done, req); >+ >+ return smbd_smb2_request_pending_queue(req, subreq, 500); >+} >+ >+struct smbd_smb2_logout_state { >+ struct timeval end_logout; >+}; >+ >+static struct tevent_req *smbd_smb2_logoff_send(TALLOC_CTX *mem_ctx, >+ struct tevent_context *ev, >+ struct smbd_smb2_request *smb2req) >+{ >+ NTSTATUS status; >+ struct tevent_req *req; >+ struct smbd_smb2_logout_state *state; /* dummy state. */ >+ >+ req = smb2req->subreq; >+ if (req == NULL) { >+ /* New logoff call. */ >+ req = tevent_req_create(mem_ctx, &state, struct smbd_smb2_logout_state); >+ if (req == NULL) { >+ return NULL; >+ } >+ >+ /* Don't wait more than 5 seconds for cancel. */ >+ state->end_logout = tevent_timeval_current_ofs(5, 0); >+ >+ if (any_outstanding_session_requests(smb2req, true)) { >+ /* We have outstanding requests. */ >+ struct tevent_immediate *im = tevent_create_immediate(smb2req); >+ if (!im) { >+ return NULL; >+ } >+ /* Try again immediately - we just requested they be cancelled. */ >+ tevent_schedule_immediate(im, >+ smb2req->sconn->ev_ctx, >+ smbd_smb2_request_dispatch_immediate, >+ smb2req); >+ return req; >+ } >+ } else { >+ /* Re-entrant logoff call. */ >+ struct timeval curr = tevent_timeval_current(); >+ state = tevent_req_data(req, struct smbd_smb2_logout_state); >+ >+ if (tevent_timeval_compare(&state->end_logout, &curr) >= 0) { >+ /* Logic error - terminate.. */ >+ abort(); >+ } >+ >+ if (any_outstanding_session_requests(smb2req, false)) { >+ /* We are still waiting for the cancellation of >+ requests. */ >+ struct tevent_immediate *im = tevent_create_immediate(smb2req); >+ if (!im) { >+ return NULL; >+ } >+ /* Try again later. */ >+ tevent_schedule_immediate(im, >+ smb2req->sconn->ev_ctx, >+ smbd_smb2_request_dispatch_immediate, >+ smb2req); >+ return req; >+ } >+ } >+ > /* >- * TODO: cancel all outstanding requests on the session >+ * When we get here, we are actually going to tear >+ * down the sesssion pointer. > */ >- status = smbXsrv_session_logoff(req->session); >- if (!NT_STATUS_IS_OK(status)) { >+ status = smbXsrv_session_logoff(smb2req->session); >+ if (tevent_req_nterror(req, status)) { > DEBUG(0, ("smbd_smb2_request_process_logoff: " > "smbXsrv_session_logoff() failed: %s\n", > nt_errstr(status))); >- /* >- * If we hit this case, there is something completely >- * wrong, so we better disconnect the transport connection. >- */ >- return status; >- } >+ return tevent_req_post(req, ev); >+ } > > /* > * we may need to sign the response, so we need to keep > * the session until the response is sent to the wire. > */ >- talloc_steal(req, req->session); >+ talloc_steal(smb2req, smb2req->session); > >- outbody = data_blob_talloc(req->out.vector, NULL, 0x04); >+ /* >+ * The return from smbXsrv_session_logoff() >+ * leaves all outstanding pointers to >+ * smb2req->session not on this request in an >+ * undefined state, ensure we don't try and >+ * access any req->session pointers once >+ * we return. >+ */ >+ remove_outstanding_session_refs(smb2req); >+ >+ tevent_req_done(req); >+ return tevent_req_post(req, ev); >+} >+ >+static void smbd_smb2_request_logoff_done(struct tevent_req *subreq) >+{ >+ struct smbd_smb2_request *smb2req = >+ tevent_req_callback_data(subreq, >+ struct smbd_smb2_request); >+ DATA_BLOB outbody; >+ NTSTATUS status; >+ NTSTATUS error; >+ >+ status = smbd_smb2_logoff_recv(subreq); >+ TALLOC_FREE(subreq); >+ if (!NT_STATUS_IS_OK(status)) { >+ error = smbd_smb2_request_error(smb2req, status); >+ if (!NT_STATUS_IS_OK(error)) { >+ smbd_server_connection_terminate(smb2req->sconn, >+ nt_errstr(error)); >+ return; >+ } >+ return; >+ } >+ >+ outbody = data_blob_talloc(smb2req->out.vector, NULL, 0x04); > if (outbody.data == NULL) { >- return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); >+ error = smbd_smb2_request_error(smb2req, NT_STATUS_NO_MEMORY); >+ if (!NT_STATUS_IS_OK(error)) { >+ smbd_server_connection_terminate(smb2req->sconn, >+ nt_errstr(error)); >+ return; >+ } >+ return; > } > > SSVAL(outbody.data, 0x00, 0x04); /* struct size */ > SSVAL(outbody.data, 0x02, 0); /* reserved */ > >- return smbd_smb2_request_done(req, outbody, NULL); >+ error = smbd_smb2_request_done(smb2req, outbody, NULL); >+ if (!NT_STATUS_IS_OK(error)) { >+ smbd_server_connection_terminate(smb2req->sconn, >+ nt_errstr(error)); >+ return; >+ } >+} >+ >+static NTSTATUS smbd_smb2_logoff_recv(struct tevent_req *req) >+{ >+ NTSTATUS status; >+ >+ if (tevent_req_is_nterror(req, &status)) { >+ tevent_req_received(req); >+ return status; >+ } >+ tevent_req_received(req); >+ return NT_STATUS_OK; > } >diff --git a/source3/smbd/smb2_tcon.c b/source3/smbd/smb2_tcon.c >index ca67461..1533b5d 100644 >--- a/source3/smbd/smb2_tcon.c >+++ b/source3/smbd/smb2_tcon.c >@@ -411,6 +411,26 @@ static NTSTATUS smbd_smb2_tree_connect_recv(struct tevent_req *req, > return NT_STATUS_OK; > } > >+static bool cancel_outstanding_tcon_requests(const struct smbd_smb2_request *smb2req) >+{ >+ bool ret = false; >+ struct smbd_smb2_request *preq; >+ >+ if (smb2req->tcon == NULL) { >+ return false; >+ } >+ for (preq = smb2req->sconn->smb2.requests; preq != NULL; preq = preq->next) { >+ if (preq == smb2req) { >+ continue; >+ } >+ if (preq->tcon == smb2req->tcon) { >+ tevent_req_cancel(preq->subreq); >+ ret = true; >+ } >+ } >+ return ret; >+} >+ > NTSTATUS smbd_smb2_request_process_tdis(struct smbd_smb2_request *req) > { > NTSTATUS status; >@@ -421,9 +441,47 @@ NTSTATUS smbd_smb2_request_process_tdis(struct smbd_smb2_request *req) > return smbd_smb2_request_error(req, status); > } > >- /* >- * TODO: cancel all outstanding requests on the tcon >- */ >+ if (req->current_idx == 1) { >+ /* >+ * If we have a singleton request, or we're >+ * at the start of a compound request, >+ * try and cancel all outstanding requests >+ * on this tcon and then re-process this >+ * request. >+ * >+ * If the TDis is not at the start >+ * of a compound sequence then just treat it >+ * synchronously and rely on the tcon >+ * lookup smbd_smb2_request_check_tcon() >+ * to prevent crashes on susbsequently >+ * queued requests on this tcon. It's too hard >+ * at the moment to get this right. >+ * >+ * TODO: for a TDis not at the start >+ * of a compound sequence we should send the >+ * previous reply data using something like >+ * smb2_send_async_interim_response(), then >+ * spin off the rest of the compound requests >+ * into a new request where TDis is >+ * the start of the compound. We can then >+ * treat as a smbd_smb2_request_dispatch_immediate(), >+ * and reschedule as normal. JRA. >+ */ >+ if (cancel_outstanding_tcon_requests(req)) { >+ /* We have outstanding requests. */ >+ struct tevent_immediate *im = tevent_create_immediate(req); >+ if (!im) { >+ return NT_STATUS_NO_MEMORY; >+ } >+ tevent_schedule_immediate(im, >+ req->sconn->ev_ctx, >+ smbd_smb2_request_dispatch_immediate, >+ req); >+ /* Try again later. */ >+ return NT_STATUS_OK; >+ } >+ } >+ > status = smbXsrv_tcon_disconnect(req->tcon, req->tcon->compat->vuid); > if (!NT_STATUS_IS_OK(status)) { > DEBUG(0, ("smbd_smb2_request_process_tdis: " >diff --git a/source4/torture/smb2/notify.c b/source4/torture/smb2/notify.c >index e83b099..1e52635 100644 >--- a/source4/torture/smb2/notify.c >+++ b/source4/torture/smb2/notify.c >@@ -69,6 +69,13 @@ > goto done; \ > }} while (0) > >+#define WAIT_FOR_ASYNC_RESPONSE(req) \ >+ while (!req->cancel.can_cancel && req->state <= SMB2_REQUEST_RECV) { \ >+ if (tevent_loop_once(torture->ev) != 0) { \ >+ break; \ >+ } \ >+ } >+ > #define BASEDIR "test_notify" > #define FNAME "smb2-notify01.dat" > >@@ -1980,12 +1987,89 @@ done: > } > > /* >+ basic testing of change notifies followed by a cancel on the change notify, >+ followed by a ulogoff >+*/ >+ >+static bool torture_smb2_notify_cancel_ulogoff(struct torture_context *torture, >+ struct smb2_tree *tree1, >+ struct smb2_tree *tree2) >+{ >+ bool ret = true; >+ NTSTATUS status; >+ union smb_notify notify; >+ union smb_open io; >+ struct smb2_handle h1; >+ struct smb2_request *req; >+ >+ smb2_deltree(tree1, BASEDIR); >+ smb2_util_rmdir(tree1, BASEDIR); >+ >+ torture_comment(torture, >+ "TESTING CHANGE NOTIFY FOLLOWED BY CANCEL + ULOGOFF\n"); >+ >+ /* >+ get a handle on the directory >+ */ >+ ZERO_STRUCT(io.smb2); >+ io.generic.level = RAW_OPEN_SMB2; >+ io.smb2.in.create_flags = 0; >+ io.smb2.in.desired_access = SEC_FILE_ALL; >+ io.smb2.in.create_options = NTCREATEX_OPTIONS_DIRECTORY; >+ io.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; >+ io.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ | >+ NTCREATEX_SHARE_ACCESS_WRITE; >+ io.smb2.in.alloc_size = 0; >+ io.smb2.in.create_disposition = NTCREATEX_DISP_CREATE; >+ io.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; >+ io.smb2.in.security_flags = 0; >+ io.smb2.in.fname = BASEDIR; >+ >+ status = smb2_create(tree2, torture, &(io.smb2)); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ >+ io.smb2.in.create_disposition = NTCREATEX_DISP_OPEN; >+ status = smb2_create(tree2, torture, &(io.smb2)); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ h1 = io.smb2.out.file.handle; >+ >+ /* ask for a change notify, >+ on file or directory name changes */ >+ ZERO_STRUCT(notify.smb2); >+ notify.smb2.level = RAW_NOTIFY_SMB2; >+ notify.smb2.in.buffer_size = 1000; >+ notify.smb2.in.completion_filter = FILE_NOTIFY_CHANGE_NAME; >+ notify.smb2.in.file.handle = h1; >+ notify.smb2.in.recursive = true; >+ >+ req = smb2_notify_send(tree1, &(notify.smb2)); >+ >+ WAIT_FOR_ASYNC_RESPONSE(req); >+ >+// sleep(2); >+ >+// smb2_cancel(req); >+ >+ status = smb2_logoff(tree2->session); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ >+ status = smb2_notify_recv(req, torture, &(notify.smb2)); >+ CHECK_VAL(notify.smb2.out.num_changes, 0); >+ >+done: >+ smb2_deltree(tree1, BASEDIR); >+ return ret; >+} >+ >+ >+/* > basic testing of SMB2 change notify > */ > struct torture_suite *torture_smb2_notify_init(void) > { > struct torture_suite *suite = torture_suite_create(talloc_autofree_context(), "notify"); > >+ torture_suite_add_1smb2_test(suite, "cancel+logoff", torture_smb2_notify_cancel_ulogoff); > torture_suite_add_1smb2_test(suite, "valid-req", test_valid_request); > torture_suite_add_1smb2_test(suite, "tcon", torture_smb2_notify_tcon); > torture_suite_add_2smb2_test(suite, "dir", torture_smb2_notify_dir);
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 10344
:
9547
|
9619
|
9620
|
9736
|
9761
|
9770
|
9772
|
9773
|
9780
|
9815
|
9816
|
9817
|
9818