]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
s4: torture: Add an async SMB2_OP_FLUSH + SMB2_OP_CLOSE test to smb2.compound_async.
authorJeremy Allison <jra@samba.org>
Tue, 18 Oct 2022 23:22:33 +0000 (16:22 -0700)
committerJule Anger <janger@samba.org>
Mon, 16 Jan 2023 09:40:17 +0000 (09:40 +0000)
Shows we fail sending an SMB2_OP_FLUSH + SMB2_OP_CLOSE
compound. Internally the flush goes async and
we free the req, then we process the close.
When the flush completes it tries to access
already freed data.

Found using the Apple MacOSX client at SNIA SDC 2022.

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>
(back-ported from commit 17a110c1b58196eb8ecf3c76eb97e8508976c544)

selftest/knownfail.d/compound_async [new file with mode: 0644]
source3/selftest/tests.py
source4/torture/smb2/compound.c
source4/torture/smb2/smb2.c

diff --git a/selftest/knownfail.d/compound_async b/selftest/knownfail.d/compound_async
new file mode 100644 (file)
index 0000000..c18465b
--- /dev/null
@@ -0,0 +1 @@
+^samba3.smb2.compound_async.flush_close\(fileserver\)
index 31a80b82e877501e6f14efa0bb23c734b77b42c5..2865657a91218a383bb84c441f11951cb8681d72 100755 (executable)
@@ -999,6 +999,8 @@ for t in tests:
         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')
+    elif t == "smb2.compound_async":
+        plansmbtorture4testsuite(t, "fileserver", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD')
     elif t == "rpc.samba3.netlogon" or t == "rpc.samba3.sessionkey":
         plansmbtorture4testsuite(t, "nt4_dc_smb1", '//$SERVER_IP/tmp -U$USERNAME%$PASSWORD --option=torture:wksname=samba3rpctest')
         plansmbtorture4testsuite(t, "ad_dc_smb1", '//$SERVER/tmp -U$USERNAME%$PASSWORD --option=torture:wksname=samba3rpctest')
index cf19361130f50fd3dad9588083be4ab4067dbc96..e78d78e3a985fd1f53ff4f0283159d6718bfe25d 100644 (file)
@@ -2057,6 +2057,110 @@ done:
        return ret;
 }
 
+static bool test_compound_async_flush_close(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_close cl;
+       struct smb2_flush fl;
+       const char *fname = "compound_async_flush_close";
+       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 + close handle. */
+       smb2_transport_compound_start(tree->session->transport, 2);
+
+       ZERO_STRUCT(fl);
+       fl.in.file.handle = fhandle;
+
+       req[0] = smb2_flush_send(tree, &fl);
+       torture_assert_not_null_goto(tctx, req[0], ret, done,
+               "smb2_flush_send failed\n");
+
+       smb2_transport_compound_set_related(tree->session->transport, true);
+
+       ZERO_STRUCT(cl);
+       cl.in.file.handle = relhandle;
+       req[1] = smb2_close_send(tree, &cl);
+       torture_assert_not_null_goto(tctx, req[1], ret, done,
+               "smb2_close_send failed\n");
+
+       status = smb2_flush_recv(req[0], &fl);
+       /*
+        * 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 didn't return "
+                       "NT_STATUS_INTERNAL_ERROR.\n");
+       }
+       status = smb2_close_recv(req[1], &cl);
+       torture_assert_ntstatus_ok_goto(tctx, status, ret, done,
+                       "smb2_close_recv 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");
@@ -2107,3 +2211,16 @@ struct torture_suite *torture_smb2_compound_find_init(TALLOC_CTX *ctx)
 
        return suite;
 }
+
+struct torture_suite *torture_smb2_compound_async_init(TALLOC_CTX *ctx)
+{
+       struct torture_suite *suite = torture_suite_create(ctx,
+                                       "compound_async");
+
+       torture_suite_add_1smb2_test(suite, "flush_close",
+               test_compound_async_flush_close);
+
+       suite->description = talloc_strdup(suite, "SMB2-COMPOUND-ASYNC tests");
+
+       return suite;
+}
index 1d9b7d5573f4dd68c7a0b37239cc6c7f328613b4..c0057d595e491021f218bdd8afbebc94788c8ae0 100644 (file)
@@ -173,6 +173,7 @@ NTSTATUS torture_smb2_init(TALLOC_CTX *ctx)
        torture_suite_add_suite(suite, torture_smb2_lease_init(suite));
        torture_suite_add_suite(suite, torture_smb2_compound_init(suite));
        torture_suite_add_suite(suite, torture_smb2_compound_find_init(suite));
+       torture_suite_add_suite(suite, torture_smb2_compound_async_init(suite));
        torture_suite_add_suite(suite, torture_smb2_oplocks_init(suite));
        torture_suite_add_suite(suite, torture_smb2_kernel_oplocks_init(suite));
        torture_suite_add_suite(suite, torture_smb2_streams_init(suite));