Failure with compound requests and aio enabled

Christof Schmitt cs at samba.org
Wed Sep 20 23:47:18 UTC 2017


Mac clients sometimes send a combination of CREATE-WRITE-CLOSE requests
as compound requests. This works in the default config, but fails on a
share with 'aio read size' and 'aio write size' enabled.

The attached patches add a smbtorture testcase that demonstrates this
problem. I think this is the result of the codepath in
source3/smbd/smb2_server.c:

	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 only allowed for opens that cause an
		 * oplock break or for the last operation in the
		 * chain, otherwise it is not allowed. See
		 * [MS-SMB2].pdf note <206> on Section 3.3.5.2.7.
		 */
		const uint8_t *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);

I am not quite sure how to address this. Maybe this would require a
similar treatment as the SMB QUERY DIRECTORY to run async internally,
but pretend to be synchronous for the client.

Also the testsuite integration surfaced this error:

failure: samba3.smb2.compound aio.compound-padding(nt4_dc) [
Exception: Exception: (../source4/torture/smb2/compound.c:539) Incorrect value req[1]->in.body_size=19 - should be 24

Christof
-------------- next part --------------
From 2af5a586ac0526dbb4aaf5dcaa629a179bc86762 Mon Sep 17 00:00:00 2001
From: Christof Schmitt <cs at samba.org>
Date: Wed, 20 Sep 2017 16:07:50 -0700
Subject: [PATCH 1/2] torture: Add testcase for compound CREATE-WRITE-CLOSE
 request

Signed-off-by: Christof Schmitt <cs at samba.org>
---
 source4/torture/smb2/compound.c | 73 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 73 insertions(+)

diff --git a/source4/torture/smb2/compound.c b/source4/torture/smb2/compound.c
index e501870..c592308 100644
--- a/source4/torture/smb2/compound.c
+++ b/source4/torture/smb2/compound.c
@@ -671,6 +671,77 @@ done:
 	return ret;
 }
 
+static bool test_compound_create_write_close(struct torture_context *tctx,
+					     struct smb2_tree *tree)
+{
+	struct smb2_handle handle = { .data = { UINT64_MAX, UINT64_MAX } };
+	struct smb2_create create;
+	struct smb2_write write;
+	struct smb2_close close;
+	const char *fname = "compound_create_write_close.dat";
+	struct smb2_request *req[3];
+	NTSTATUS status;
+	bool ret = false;
+
+	smb2_util_unlink(tree, fname);
+
+	ZERO_STRUCT(create);
+	create.in.security_flags = 0x00;
+	create.in.oplock_level = 0;
+	create.in.impersonation_level = NTCREATEX_IMPERSONATION_IMPERSONATION;
+	create.in.create_flags = 0x00000000;
+	create.in.reserved = 0x00000000;
+	create.in.desired_access = SEC_RIGHTS_FILE_ALL;
+	create.in.file_attributes = FILE_ATTRIBUTE_NORMAL;
+	create.in.share_access = NTCREATEX_SHARE_ACCESS_READ |
+		NTCREATEX_SHARE_ACCESS_WRITE |
+		NTCREATEX_SHARE_ACCESS_DELETE;
+	create.in.create_disposition = NTCREATEX_DISP_OPEN_IF;
+	create.in.create_options = NTCREATEX_OPTIONS_SEQUENTIAL_ONLY |
+		NTCREATEX_OPTIONS_ASYNC_ALERT |
+		NTCREATEX_OPTIONS_NON_DIRECTORY_FILE |
+		0x00200000;
+	create.in.fname = fname;
+
+	smb2_transport_compound_start(tree->session->transport, 3);
+
+	req[0] = smb2_create_send(tree, &create);
+
+	smb2_transport_compound_set_related(tree->session->transport, true);
+
+	ZERO_STRUCT(write);
+	write.in.file.handle = handle;
+	write.in.offset = 0;
+	write.in.data = data_blob_talloc(tctx, NULL, 1024);
+
+	req[1] = smb2_write_send(tree, &write);
+
+	ZERO_STRUCT(close);
+	close.in.file.handle = handle;
+
+	req[2] = smb2_close_send(tree, &close);
+
+	status = smb2_create_recv(req[0], tree, &create);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"CREATE failed.");
+
+	status = smb2_write_recv(req[1], &write);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"WRITE failed.");
+
+	status = smb2_close_recv(req[2], &close);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"CLOSE failed.");
+
+	status = smb2_util_unlink(tree, fname);
+	torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+					"File deletion failed.");
+
+	ret = true;
+done:
+	return ret;
+}
+
 static bool test_compound_unrelated1(struct torture_context *tctx,
 				     struct smb2_tree *tree)
 {
@@ -1230,6 +1301,8 @@ struct torture_suite *torture_smb2_compound_init(TALLOC_CTX *ctx)
 	torture_suite_add_1smb2_test(suite, "interim2",  test_compound_interim2);
 	torture_suite_add_1smb2_test(suite, "compound-break", test_compound_break);
 	torture_suite_add_1smb2_test(suite, "compound-padding", test_compound_padding);
+	torture_suite_add_1smb2_test(suite, "create-write-close",
+				     test_compound_create_write_close);
 
 	suite->description = talloc_strdup(suite, "SMB2-COMPOUND tests");
 
-- 
1.8.3.1


From a4c59bde1b153d40e44106d7555a1fe543cb101b Mon Sep 17 00:00:00 2001
From: Christof Schmitt <cs at samba.org>
Date: Wed, 20 Sep 2017 16:13:38 -0700
Subject: [PATCH 2/2] selftest: Also run smbtorture smb2.compound with aio
 enabled

Signed-off-by: Christof Schmitt <cs at samba.org>
---
 selftest/knownfail        | 1 +
 source3/selftest/tests.py | 4 ++++
 2 files changed, 5 insertions(+)

diff --git a/selftest/knownfail b/selftest/knownfail
index aa89dab..953b181 100644
--- a/selftest/knownfail
+++ b/selftest/knownfail
@@ -172,6 +172,7 @@
 ^samba3.smb2.setinfo.setinfo
 ^samba3.smb2.session.*reauth5 # some special anonymous checks?
 ^samba3.smb2.compound.interim2 # wrong return code (STATUS_CANCELLED)
+^samba3.smb2.compound.aio.interim2 # wrong return code (STATUS_CANCELLED)
 ^samba3.smb2.replay.channel-sequence
 ^samba3.smb2.replay.replay3
 ^samba3.smb2.replay.replay4
diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py
index 139bba0..73479fc 100755
--- a/source3/selftest/tests.py
+++ b/source3/selftest/tests.py
@@ -518,6 +518,10 @@ for t in tests:
     elif t == "rpc.samr.users.privileges":
         plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD --option=torture:nt4_dc=true')
         plansmbtorture4testsuite(t, "ad_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD')
+    elif t == "smb2.compound":
+        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD')
+        plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/aio -U$USERNAME%$PASSWORD', 'aio')
+        plansmbtorture4testsuite(t, "ad_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD')
     else:
         plansmbtorture4testsuite(t, "nt4_dc", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD')
         plansmbtorture4testsuite(t, "ad_dc", '//$SERVER/tmp -U$USERNAME%$PASSWORD')
-- 
1.8.3.1



More information about the samba-technical mailing list