The Samba-Bugzilla – Attachment 8844 Details for
Bug 9722
Samba does not properly handle Oplock breaks in compound requests
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
Raw patch for master.
smb2_compound_oplock.patch (text/plain), 11.71 KB, created by
Jeremy Allison
on 2013-05-02 00:23:57 UTC
(
hide
)
Description:
Raw patch for master.
Filename:
MIME Type:
Creator:
Jeremy Allison
Created:
2013-05-02 00:23:57 UTC
Size:
11.71 KB
patch
obsolete
>diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h >index 46baeed..d618aea 100644 >--- a/source3/smbd/globals.h >+++ b/source3/smbd/globals.h >@@ -794,7 +794,6 @@ struct smbd_server_connection { > uint32_t max_trans; > uint32_t max_read; > uint32_t max_write; >- bool compound_related_in_progress; > } smb2; > > struct smbXsrv_connection *conn; >diff --git a/source3/smbd/open.c b/source3/smbd/open.c >index 7d02e52..53f8b8e 100644 >--- a/source3/smbd/open.c >+++ b/source3/smbd/open.c >@@ -2526,10 +2526,11 @@ static NTSTATUS open_file_ntcreate(connection_struct *conn, > > /* > * If we're returning a share violation, ensure we >- * cope with the braindead 1 second delay. >+ * cope with the braindead 1 second delay (SMB1 only). > */ > > if (!(oplock_request & INTERNAL_OPEN_ONLY) && >+ !conn->sconn->using_smb2 && > lp_defer_sharing_violations()) { > struct timeval timeout; > struct deferred_open_record state; >diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c >index 57e9c7b..6625f84 100644 >--- a/source3/smbd/smb2_server.c >+++ b/source3/smbd/smb2_server.c >@@ -1272,7 +1272,6 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, > uint32_t defer_time) > { > NTSTATUS status; >- int idx = req->current_idx; > struct timeval defer_endtime; > uint8_t *outhdr = NULL; > uint32_t flags; >@@ -1296,19 +1295,29 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, > return NT_STATUS_OK; > } > >- if (req->in.vector_count > idx + SMBD_SMB2_NUM_IOV_PER_REQ) { >+ if (req->in.vector_count > req->current_idx + SMBD_SMB2_NUM_IOV_PER_REQ) { > /* > * We're trying to go async in a compound >- * request chain. This is not allowed. >- * Cancel the outstanding request. >+ * request chain. >+ * This is only allowed for opens that >+ * cause an oplock break, otherwise it >+ * is not allowed. See [MS-SMB2].pdf >+ * note <194> on Section 3.3.5.2.7. > */ >- bool ok = tevent_req_cancel(req->subreq); >- if (ok) { >- return NT_STATUS_OK; >+ const uint8_t *inhdr = inhdr = SMBD_SMB2_IN_HDR_PTR(req); >+ >+ if (SVAL(inhdr, SMB2_HDR_OPCODE) != SMB2_OP_CREATE) { >+ /* >+ * Cancel the outstanding request. >+ */ >+ bool ok = tevent_req_cancel(req->subreq); >+ if (ok) { >+ return NT_STATUS_OK; >+ } >+ TALLOC_FREE(req->subreq); >+ return smbd_smb2_request_error(req, >+ NT_STATUS_INTERNAL_ERROR); > } >- TALLOC_FREE(req->subreq); >- return smbd_smb2_request_error(req, >- NT_STATUS_INTERNAL_ERROR); > } > > if (DEBUGLEVEL >= 10) { >@@ -1317,10 +1326,12 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, > print_req_vectors(req); > } > >- if (req->out.vector_count >= (2*SMBD_SMB2_NUM_IOV_PER_REQ)) { >+ if (req->current_idx > 1) { >+ int idx = req->current_idx; >+ > /* >- * This is a compound reply. We >- * must do an interim response >+ * We're going async in a compound chain. >+ * We must do an interim response > * followed by the async response > * to match W2K8R2. > */ >@@ -1330,38 +1341,38 @@ NTSTATUS smbd_smb2_request_pending_queue(struct smbd_smb2_request *req, > } > data_blob_clear_free(&req->first_key); > >- /* >- * We're splitting off the last SMB2 >- * request in a compound set, and the >- * smb2_send_async_interim_response() >- * call above just sent all the replies >- * for the previous SMB2 requests in >- * this compound set. So we're no longer >- * in the "compound_related_in_progress" >- * state, and this is no longer a compound >- * request. >- */ >- req->compound_related = false; >- req->sconn->smb2.compound_related_in_progress = false; >- > req->current_idx = 1; > >- /* Re-arrange the in.vectors. */ >- memmove(&req->in.vector[req->current_idx], >+ /* Re-arrange the in.vectors to remove what >+ we just sent. */ >+ memmove(&req->in.vector[1], > &req->in.vector[idx], >- sizeof(req->in.vector[0])*SMBD_SMB2_NUM_IOV_PER_REQ); >- req->in.vector_count = req->current_idx + SMBD_SMB2_NUM_IOV_PER_REQ; >+ sizeof(req->in.vector[0])*(req->in.vector_count - idx)); >+ req->in.vector_count = 1 + (req->in.vector_count - idx); > >- /* Re-arrange the out.vectors. */ >- memmove(&req->out.vector[req->current_idx], >+ /* Re-arrange the out.vectors to match. */ >+ memmove(&req->out.vector[1], > &req->out.vector[idx], >- sizeof(req->out.vector[0])*SMBD_SMB2_NUM_IOV_PER_REQ); >- req->out.vector_count = req->current_idx + SMBD_SMB2_NUM_IOV_PER_REQ; >+ sizeof(req->out.vector[0])*(req->out.vector_count - idx)); >+ req->out.vector_count = 1 + (req->out.vector_count - idx); > >- outhdr = SMBD_SMB2_OUT_HDR_PTR(req); >- flags = (IVAL(outhdr, SMB2_HDR_FLAGS) & ~SMB2_HDR_FLAG_CHAINED); >- SIVAL(outhdr, SMB2_HDR_FLAGS, flags); >+ if (req->in.vector_count == 1 + SMBD_SMB2_NUM_IOV_PER_REQ) { >+ /* >+ * We're splitting off the last SMB2 >+ * request in a compound set, and the >+ * smb2_send_async_interim_response() >+ * call above just sent all the replies >+ * for the previous SMB2 requests in >+ * request. >+ * Mark this as no longer compound_related. >+ */ >+ req->compound_related = false; >+ outhdr = SMBD_SMB2_OUT_HDR_PTR(req); >+ flags = (IVAL(outhdr, SMB2_HDR_FLAGS) & ~SMB2_HDR_FLAG_CHAINED); >+ SIVAL(outhdr, SMB2_HDR_FLAGS, flags); >+ } > } >+ > data_blob_clear_free(&req->last_key); > > defer_endtime = timeval_current_ofs_usec(defer_time); >@@ -1599,6 +1610,14 @@ static NTSTATUS smbd_smb2_request_process_cancel(struct smbd_smb2_request *req) > uint64_t message_id; > uint64_t async_id; > >+ if (cur->compound_related) { >+ /* >+ * Never cancel anything in a compound request. >+ * Way too hard to deal with the result. >+ */ >+ continue; >+ } >+ > outhdr = SMBD_SMB2_OUT_HDR_PTR(cur); > > message_id = BVAL(outhdr, SMB2_HDR_MESSAGE_ID); >@@ -2024,7 +2043,6 @@ NTSTATUS smbd_smb2_request_dispatch(struct smbd_smb2_request *req) > > if (flags & SMB2_HDR_FLAG_CHAINED) { > req->compound_related = true; >- req->sconn->smb2.compound_related_in_progress = true; > } > > if (call->need_session) { >@@ -2388,7 +2406,6 @@ static NTSTATUS smbd_smb2_request_reply(struct smbd_smb2_request *req) > > if (req->compound_related) { > req->compound_related = false; >- req->sconn->smb2.compound_related_in_progress = false; > } > > smb2_setup_nbt_length(req->out.vector, req->out.vector_count); >@@ -3143,6 +3160,7 @@ static NTSTATUS smbd_smb2_request_next_incoming(struct smbd_server_connection *s > size_t cur_send_queue_len; > struct tevent_req *subreq; > >+#if 0 > if (sconn->smb2.compound_related_in_progress) { > /* > * Can't read another until the related >@@ -3150,6 +3168,7 @@ static NTSTATUS smbd_smb2_request_next_incoming(struct smbd_server_connection *s > */ > return NT_STATUS_OK; > } >+#endif > > if (tevent_queue_length(sconn->smb2.recv_queue) > 0) { > /* >diff --git a/source4/torture/smb2/compound.c b/source4/torture/smb2/compound.c >index 4a47e14..9b3cacc 100644 >--- a/source4/torture/smb2/compound.c >+++ b/source4/torture/smb2/compound.c >@@ -34,6 +34,168 @@ > goto done; \ > }} while (0) > >+static struct { >+ struct smb2_handle handle; >+ uint8_t level; >+ struct smb2_break br; >+ int count; >+ int failures; >+ NTSTATUS failure_status; >+} break_info; >+ >+static void torture_oplock_break_callback(struct smb2_request *req) >+{ >+ NTSTATUS status; >+ struct smb2_break br; >+ >+ ZERO_STRUCT(br); >+ status = smb2_break_recv(req, &break_info.br); >+ if (!NT_STATUS_IS_OK(status)) { >+ break_info.failures++; >+ break_info.failure_status = status; >+ } >+ >+ return; >+} >+ >+/* A general oplock break notification handler. This should be used when a >+ * test expects to break from batch or exclusive to a lower level. */ >+static bool torture_oplock_handler(struct smb2_transport *transport, >+ const struct smb2_handle *handle, >+ uint8_t level, >+ void *private_data) >+{ >+ struct smb2_tree *tree = private_data; >+ const char *name; >+ struct smb2_request *req; >+ ZERO_STRUCT(break_info.br); >+ >+ break_info.handle = *handle; >+ break_info.level = level; >+ break_info.count++; >+ >+ switch (level) { >+ case SMB2_OPLOCK_LEVEL_II: >+ name = "level II"; >+ break; >+ case SMB2_OPLOCK_LEVEL_NONE: >+ name = "none"; >+ break; >+ default: >+ name = "unknown"; >+ break_info.failures++; >+ } >+ printf("Acking to %s [0x%02X] in oplock handler\n", name, level); >+ >+ break_info.br.in.file.handle = *handle; >+ break_info.br.in.oplock_level = level; >+ break_info.br.in.reserved = 0; >+ break_info.br.in.reserved2 = 0; >+ >+ req = smb2_break_send(tree, &break_info.br); >+ req->async.fn = torture_oplock_break_callback; >+ req->async.private_data = NULL; >+ return true; >+} >+ >+static bool test_compound_break(struct torture_context *tctx, >+ struct smb2_tree *tree) >+{ >+ const char *fname1 = "some-file.pptx"; >+ NTSTATUS status; >+ bool ret = true; >+ union smb_open io1; >+ struct smb2_create io2; >+ struct smb2_getinfo gf; >+ struct smb2_request *req[2]; >+ struct smb2_handle h1; >+ struct smb2_handle h; >+ >+ tree->session->transport->oplock.handler = torture_oplock_handler; >+ tree->session->transport->oplock.private_data = tree; >+ >+ ZERO_STRUCT(break_info); >+ >+ /* >+ base ntcreatex parms >+ */ >+ ZERO_STRUCT(io1.smb2); >+ io1.generic.level = RAW_OPEN_SMB2; >+ io1.smb2.in.desired_access = (SEC_STD_SYNCHRONIZE| >+ SEC_STD_READ_CONTROL| >+ SEC_FILE_READ_ATTRIBUTE| >+ SEC_FILE_READ_EA| >+ SEC_FILE_READ_DATA); >+ io1.smb2.in.alloc_size = 0; >+ io1.smb2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; >+ io1.smb2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| >+ NTCREATEX_SHARE_ACCESS_WRITE| >+ NTCREATEX_SHARE_ACCESS_DELETE; >+ io1.smb2.in.create_disposition = NTCREATEX_DISP_OPEN_IF; >+ io1.smb2.in.create_options = 0; >+ io1.smb2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; >+ io1.smb2.in.security_flags = 0; >+ io1.smb2.in.fname = fname1; >+ >+ torture_comment(tctx, "TEST2: open a file with an batch " >+ "oplock (share mode: all)\n"); >+ io1.smb2.in.oplock_level = SMB2_OPLOCK_LEVEL_BATCH; >+ >+ status = smb2_create(tree, tctx, &(io1.smb2)); >+ torture_assert_ntstatus_ok(tctx, status, "Error opening the file"); >+ >+ h1 = io1.smb2.out.file.handle; >+ >+ torture_comment(tctx, "TEST2: Opening second time with compound\n"); >+ >+ ZERO_STRUCT(io2); >+ >+ io2.in.desired_access = (SEC_STD_SYNCHRONIZE| >+ SEC_FILE_READ_ATTRIBUTE| >+ SEC_FILE_READ_EA); >+ io2.in.alloc_size = 0; >+ io2.in.file_attributes = FILE_ATTRIBUTE_NORMAL; >+ io2.in.share_access = NTCREATEX_SHARE_ACCESS_READ| >+ NTCREATEX_SHARE_ACCESS_WRITE| >+ NTCREATEX_SHARE_ACCESS_DELETE; >+ io2.in.create_disposition = NTCREATEX_DISP_OPEN; >+ io2.in.create_options = 0; >+ io2.in.impersonation_level = SMB2_IMPERSONATION_ANONYMOUS; >+ io2.in.security_flags = 0; >+ io2.in.fname = fname1; >+ io2.in.oplock_level = 0; >+ >+ smb2_transport_compound_start(tree->session->transport, 2); >+ >+ req[0] = smb2_create_send(tree, &io2); >+ >+ smb2_transport_compound_set_related(tree->session->transport, true); >+ >+ h.data[0] = UINT64_MAX; >+ h.data[1] = UINT64_MAX; >+ >+ ZERO_STRUCT(gf); >+ gf.in.file.handle = h; >+ gf.in.info_type = SMB2_GETINFO_FILE; >+ gf.in.info_class = 0x16; >+ gf.in.output_buffer_length = 0x1000; >+ gf.in.input_buffer_length = 0; >+ >+ req[1] = smb2_getinfo_send(tree, &gf); >+ >+ status = smb2_create_recv(req[0], tree, &io2); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ >+ status = smb2_getinfo_recv(req[1], tree, &gf); >+ CHECK_STATUS(status, NT_STATUS_OK); >+ >+done: >+ >+ smb2_util_close(tree, h1); >+ smb2_util_unlink(tree, fname1); >+ return ret; >+} >+ > static bool test_compound_related1(struct torture_context *tctx, > struct smb2_tree *tree) > { >@@ -717,6 +879,7 @@ struct torture_suite *torture_smb2_compound_init(void) > torture_suite_add_1smb2_test(suite, "invalid3", test_compound_invalid3); > torture_suite_add_1smb2_test(suite, "interim1", test_compound_interim1); > torture_suite_add_1smb2_test(suite, "interim2", test_compound_interim2); >+ torture_suite_add_1smb2_test(suite, "compound-break", test_compound_break); > > suite->description = talloc_strdup(suite, "SMB2-COMPOUND tests"); >
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 9722
:
8646
|
8647
|
8684
|
8844
|
8846
|
8847
|
8869
|
8873