summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2022-10-20 14:22:25 -0700
committerJule Anger <janger@samba.org>2023-01-16 09:40:17 +0000
commit9b357c947fd6c36779f49dc37f9c81392c9c81fb (patch)
treeba76b1d8d62e5311afa371b753c265bb15090518
parentc9ed55b39efc7638b898db6df8352b10950c6c93 (diff)
downloadsamba-9b357c947fd6c36779f49dc37f9c81392c9c81fb.tar.gz
s4: torture: Add an async SMB2_OP_FLUSH + SMB2_OP_FLUSH test to smb2.compound_async.
Shows we fail sending an SMB2_OP_FLUSH + SMB2_OP_FLUSH compound if we immediately close the file afterward. Internally the flushes go async and we free the req, then we process the close. When the flushes complete they try to access already freed data. Extra test which will allow me to test when the final component (flush) of the compound goes async and returns NT_STATUS_PENDING. Add knownfail. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15172 Signed-off-by: Jeremy Allison <jra@samba.org> Reviewed-by: Ralph Boehme <slow@samba.org> (cherry picked from commit 6f149dfd9d8d2619a9e18975ebcf5e69df2b7766)
-rw-r--r--selftest/knownfail.d/compound_async1
-rw-r--r--source4/torture/smb2/compound.c115
2 files changed, 116 insertions, 0 deletions
diff --git a/selftest/knownfail.d/compound_async b/selftest/knownfail.d/compound_async
index c18465b7204..e1be97649f3 100644
--- a/selftest/knownfail.d/compound_async
+++ b/selftest/knownfail.d/compound_async
@@ -1 +1,2 @@
^samba3.smb2.compound_async.flush_close\(fileserver\)
+^samba3.smb2.compound_async.flush_flush\(fileserver\)
diff --git a/source4/torture/smb2/compound.c b/source4/torture/smb2/compound.c
index e78d78e3a98..47a550f0873 100644
--- a/source4/torture/smb2/compound.c
+++ b/source4/torture/smb2/compound.c
@@ -2161,6 +2161,119 @@ static bool test_compound_async_flush_close(struct torture_context *tctx,
return ret;
}
+static bool test_compound_async_flush_flush(struct torture_context *tctx,
+ struct smb2_tree *tree)
+{
+ struct smb2_handle fhandle = { .data = { 0, 0 } };
+ struct smb2_handle relhandle = { .data = { UINT64_MAX, UINT64_MAX } };
+ struct smb2_flush fl1;
+ struct smb2_flush fl2;
+ const char *fname = "compound_async_flush_flush";
+ struct smb2_request *req[2];
+ NTSTATUS status;
+ bool ret = false;
+
+ /* Start clean. */
+ smb2_util_unlink(tree, fname);
+
+ /* Create a file. */
+ status = torture_smb2_testfile_access(tree,
+ fname,
+ &fhandle,
+ SEC_RIGHTS_FILE_ALL);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ /* Now do a compound flush + flush handle. */
+ smb2_transport_compound_start(tree->session->transport, 2);
+
+ ZERO_STRUCT(fl1);
+ fl1.in.file.handle = fhandle;
+
+ req[0] = smb2_flush_send(tree, &fl1);
+ torture_assert_not_null_goto(tctx, req[0], ret, done,
+ "smb2_flush_send (1) failed\n");
+
+ smb2_transport_compound_set_related(tree->session->transport, true);
+
+ ZERO_STRUCT(fl2);
+ fl2.in.file.handle = relhandle;
+
+ req[1] = smb2_flush_send(tree, &fl2);
+ torture_assert_not_null_goto(tctx, req[1], ret, done,
+ "smb2_flush_send (2) failed\n");
+
+ status = smb2_flush_recv(req[0], &fl1);
+ /*
+ * On Windows, this flush will usually
+ * succeed as we have nothing to flush,
+ * so allow NT_STATUS_OK. Once bug #15172
+ * is fixed Samba will do the flush synchronously
+ * so allow NT_STATUS_OK.
+ */
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * If we didn't get NT_STATUS_OK, we *must*
+ * get NT_STATUS_INTERNAL_ERROR if the flush
+ * goes async.
+ *
+ * For pre-bugfix #15172 Samba, the flush goes async and
+ * we should get NT_STATUS_INTERNAL_ERROR.
+ */
+ torture_assert_ntstatus_equal_goto(tctx,
+ status,
+ NT_STATUS_INTERNAL_ERROR,
+ ret,
+ done,
+ "smb2_flush_recv (1) didn't return "
+ "NT_STATUS_INTERNAL_ERROR.\n");
+ }
+
+ /*
+ * If the flush is the last entry in a compound,
+ * it should always succeed even if it goes async.
+ */
+ status = smb2_flush_recv(req[1], &fl2);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_flush_recv (2) failed.");
+
+ status = smb2_util_close(tree, fhandle);
+ torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+ "smb2_util_close failed.");
+ ZERO_STRUCT(fhandle);
+
+ /*
+ * Do several more operations on the tree, spaced
+ * out by 1 sec sleeps to make sure the server didn't
+ * crash on the close. The sleeps are required to
+ * make test test for a crash reliable, as we ensure
+ * the pthread fsync internally finishes and accesses
+ * freed memory. Without them the test occassionally
+ * passes as we disconnect before the pthread fsync
+ * finishes.
+ */
+ status = smb2_util_unlink(tree, fname);
+ CHECK_STATUS(status, NT_STATUS_OK);
+
+ sleep(1);
+ status = smb2_util_unlink(tree, fname);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ sleep(1);
+ status = smb2_util_unlink(tree, fname);
+ CHECK_STATUS(status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
+ ret = true;
+
+ done:
+
+ if (fhandle.data[0] != 0) {
+ smb2_util_close(tree, fhandle);
+ }
+
+ smb2_util_unlink(tree, fname);
+ return ret;
+}
+
struct torture_suite *torture_smb2_compound_init(TALLOC_CTX *ctx)
{
struct torture_suite *suite = torture_suite_create(ctx, "compound");
@@ -2219,6 +2332,8 @@ struct torture_suite *torture_smb2_compound_async_init(TALLOC_CTX *ctx)
torture_suite_add_1smb2_test(suite, "flush_close",
test_compound_async_flush_close);
+ torture_suite_add_1smb2_test(suite, "flush_flush",
+ test_compound_async_flush_flush);
suite->description = talloc_strdup(suite, "SMB2-COMPOUND-ASYNC tests");