From d3c876b2d70a7b0032980e7091be6d17ad7fc443 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 23 Sep 2020 04:58:22 +0200 Subject: [PATCH 1/3] source4/torture/smb2/lease.c test_lease_timeout_disconnect --- source4/torture/smb2/lease.c | 153 +++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/source4/torture/smb2/lease.c b/source4/torture/smb2/lease.c index d3b8daea3105..ca65c53c8f7c 100644 --- a/source4/torture/smb2/lease.c +++ b/source4/torture/smb2/lease.c @@ -4151,6 +4151,158 @@ done: return ret; } +static bool test_lease_timeout_disconnect(struct torture_context *tctx, + struct smb2_tree *tree1) +{ + TALLOC_CTX *mem_ctx = talloc_new(tctx); + NTSTATUS status; + bool ret = true; + struct smbcli_options transport2_options; + struct smbcli_options transport3_options; + struct smb2_tree *tree2 = NULL; + //struct smb2_session *session2 = NULL; + struct smb2_tree *tree3 = NULL; + //struct smb2_session *session3 = NULL; + //struct smb2_session *session1 = tree1->session; + struct smb2_transport *transport1 = tree1->session->transport; + struct smb2_transport *transport2; + struct smb2_transport *transport3; + //struct smb2_handle h1 = {{ 0 }}; + const char *fname = "lease_timeout_logoff.dat" ; + uint32_t caps; + struct smb2_create io1; + struct smb2_create io2; + struct smb2_request *req2 = NULL; + struct smb2_lease ls1; + + caps = smb2cli_conn_server_capabilities( + tree1->session->transport->conn); + if (!(caps & SMB2_CAP_LEASING)) { + torture_skip(tctx, "leases are not supported"); + } + + smb2_util_unlink(tree1, fname); + + /* Connect 2nd connection */ + torture_comment(tctx, "connect tree2 with the same client_guid\n"); + transport2_options = transport1->options; + if (!torture_smb2_connection_ext(tctx, 0, &transport2_options, &tree2)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + return false; + } + transport2 = tree2->session->transport; + //session2 = tree2->session; + + /* Connect 3rd connection */ + torture_comment(tctx, "connect tree3 with the same client_guid\n"); + transport3_options = transport1->options; + if (!torture_smb2_connection_ext(tctx, 0, &transport3_options, &tree3)) { + torture_warning(tctx, "couldn't reconnect, bailing\n"); + return false; + } + transport3 = tree3->session->transport; + //session3 = tree3->session; + + /* Set lease handlers */ + transport1->lease.handler = torture_lease_handler; + transport1->lease.private_data = tree1; + transport2->lease.handler = torture_lease_handler; + transport2->lease.private_data = tree2; + transport3->lease.handler = torture_lease_handler; + transport3->lease.private_data = tree3; + + smb2_lease_create_share(&io1, &ls1, false, fname, + smb2_util_share_access(""), + LEASE1, + smb2_util_lease_state("RH")); + io1.in.durable_open = true; + smb2_generic_create(&io2, NULL, false, fname, + NTCREATEX_DISP_OPEN_IF, + SMB2_OPLOCK_LEVEL_NONE, 0, 0); + + torture_comment(tctx, "tree1: create file[%s] with durable RH lease (SHARE NONE)\n", fname); + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + status = smb2_create(tree1, mem_ctx, &io1); + CHECK_STATUS(status, NT_STATUS_OK); + //h1 = io1.out.file.handle; + CHECK_CREATED(&io1, CREATED, FILE_ATTRIBUTE_ARCHIVE); + CHECK_LEASE(&io1, "RH", true, LEASE1, 0); + CHECK_VAL(lease_break_info.count, 0); + + torture_comment(tctx, "tree1: skip lease acks\n"); + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + torture_comment(tctx, "tree2: open file[%s] without lease (SHARE RWD)\n", fname); + req2 = smb2_create_send(tree2, &io2); + torture_assert(tctx, req2 != NULL, "req2 started"); + + torture_comment(tctx, "tree1: wait for lease break\n"); + torture_wait_for_lease_break(tctx); + CHECK_VAL(lease_break_info.count, 1); + CHECK_BREAK_INFO("RH", "R", LEASE1); + + torture_comment(tctx, "tree1: reset lease handler\n"); + torture_reset_lease_break_info(tctx, &lease_break_info); + lease_break_info.lease_skip_ack = true; + CHECK_VAL(lease_break_info.count, 0); + + torture_comment(tctx, "tree2: check for SMB2_REQUEST_RECV\n"); + torture_assert_int_equal(tctx, req2->state, + SMB2_REQUEST_RECV, + "SMB2_REQUEST_RECV"); + + torture_comment(tctx, "sleep 1\n"); + smb_msleep(1); + + torture_comment(tctx, "transport1: keepalive\n"); + status = smb2_keepalive(transport1); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "transport2: keepalive\n"); + status = smb2_keepalive(transport2); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "transport3: keepalive\n"); + status = smb2_keepalive(transport3); + CHECK_STATUS(status, NT_STATUS_OK); + + torture_comment(tctx, "tree2: check for SMB2_REQUEST_RECV\n"); + torture_assert_int_equal(tctx, req2->state, + SMB2_REQUEST_RECV, + "SMB2_REQUEST_RECV"); + torture_comment(tctx, "tree2: check for STATUS_PENDING\n"); + torture_assert(tctx, req2->cancel.can_cancel, "STATUS_PENDING"); + + torture_comment(tctx, "sleep 1\n"); + smb_msleep(1000); + torture_comment(tctx, "transport1: keepalive\n"); + status = smb2_keepalive(transport1); + CHECK_STATUS(status, NT_STATUS_OK); + torture_comment(tctx, "transport2: disconnect\n"); + TALLOC_FREE(tree2); + + torture_comment(tctx, "sleep 1\n"); + smb_msleep(1000); + torture_comment(tctx, "transport1: keepalive\n"); + status = smb2_keepalive(transport1); + CHECK_STATUS(status, NT_STATUS_OK); + torture_comment(tctx, "transport1: disconnect\n"); + TALLOC_FREE(tree1); + + torture_comment(tctx, "sleep 1\n"); + smb_msleep(1000); + torture_comment(tctx, "transport3: keepalive\n"); + status = smb2_keepalive(transport3); + CHECK_STATUS(status, NT_STATUS_OK); + torture_comment(tctx, "transport3: disconnect\n"); + TALLOC_FREE(tree3); + +done: + + return ret; +} + struct torture_suite *torture_smb2_lease_init(TALLOC_CTX *ctx) { struct torture_suite *suite = @@ -4192,6 +4344,7 @@ struct torture_suite *torture_smb2_lease_init(TALLOC_CTX *ctx) torture_suite_add_1smb2_test(suite, "dynamic_share", test_lease_dynamic_share); torture_suite_add_1smb2_test(suite, "timeout", test_lease_timeout); torture_suite_add_1smb2_test(suite, "unlink", test_lease_unlink); + torture_suite_add_1smb2_test(suite, "timeout-disconnect", test_lease_timeout_disconnect); suite->description = talloc_strdup(suite, "SMB2-LEASE tests"); -- 2.17.1 From e00e92146986ac084b96f59c8875f7fd42ecc0e0 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 23 Sep 2020 13:07:20 +0200 Subject: [PATCH 2/3] xconn->transport.terminating = true --- source3/smbd/globals.h | 1 + source3/smbd/smb2_server.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index fcf33a699c68..43d329caa8a1 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -367,6 +367,7 @@ struct smbXsrv_connection { struct { NTSTATUS status; + bool terminating; int sock; struct tevent_fd *fde; diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index cf9de185c1f4..ca839c3cfc67 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -1631,6 +1631,14 @@ void smbd_server_connection_terminate_ex(struct smbXsrv_connection *xconn, num_ok = smbXsrv_client_valid_connections(client); + if (xconn->transport.terminating) { + DBG_DEBUG("skip recursion conn[%s] num_ok[%zu] reason[%s] at %s\n", + smbXsrv_connection_dbg(xconn), num_ok, + reason, location); + return; + } + xconn->transport.terminating = true; + DBG_DEBUG("conn[%s] num_ok[%zu] reason[%s] at %s\n", smbXsrv_connection_dbg(xconn), num_ok, reason, location); -- 2.17.1 From ec14e831cd4e79207290107045216c37cec0f3d8 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 23 Sep 2020 13:19:49 +0200 Subject: [PATCH 3/3] assert that xconn is alive for the lifetime of smbXsrv_connection_shutdown_send/recv --- source3/smbd/smb2_server.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index ca839c3cfc67..3f99c86ce253 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -1494,6 +1494,7 @@ size_t smbXsrv_client_valid_connections(struct smbXsrv_client *client) struct smbXsrv_connection_shutdown_state { struct tevent_queue *wait_queue; + struct smbXsrv_connection *xconn; }; static void smbXsrv_connection_shutdown_wait_done(struct tevent_req *subreq); @@ -1514,6 +1515,7 @@ static struct tevent_req *smbXsrv_connection_shutdown_send(TALLOC_CTX *mem_ctx, * smbXsrv_connection_disconnect_transport() before. */ SMB_ASSERT(!NT_STATUS_IS_OK(xconn->transport.status)); + SMB_ASSERT(xconn->transport.terminating); req = tevent_req_create(mem_ctx, &state, struct smbXsrv_connection_shutdown_state); @@ -1521,6 +1523,9 @@ static struct tevent_req *smbXsrv_connection_shutdown_send(TALLOC_CTX *mem_ctx, return NULL; } + state->xconn = xconn; + tevent_req_defer_callback(req, ev); + status = smbXsrv_session_disconnect_xconn(xconn); if (tevent_req_nterror(req, status)) { return tevent_req_post(req, ev); @@ -1584,15 +1589,33 @@ static void smbXsrv_connection_shutdown_wait_done(struct tevent_req *subreq) struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); + struct smbXsrv_connection_shutdown_state *state = + tevent_req_data(req, + struct smbXsrv_connection_shutdown_state); + struct smbXsrv_connection *xconn = state->xconn; tevent_queue_wait_recv(subreq); TALLOC_FREE(subreq); tevent_req_done(req); + /* + * make sure the xconn pointer is still valid, + * it should as we used tevent_req_defer_callback() + */ + SMB_ASSERT(xconn->transport.terminating); } static NTSTATUS smbXsrv_connection_shutdown_recv(struct tevent_req *req) { + struct smbXsrv_connection_shutdown_state *state = + tevent_req_data(req, + struct smbXsrv_connection_shutdown_state); + struct smbXsrv_connection *xconn = state->xconn; + /* + * make sure the xconn pointer is still valid, + * it should as we used tevent_req_defer_callback() + */ + SMB_ASSERT(xconn->transport.terminating); return tevent_req_simple_recv_ntstatus(req); } -- 2.17.1