]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
s4:torture/smb2: test FSCTL_QUERY_ALLOCATED_RANGES truncation
authorDavid Disseldorp <ddiss@samba.org>
Fri, 23 Aug 2024 13:01:24 +0000 (13:01 +0000)
committerJule Anger <janger@samba.org>
Mon, 2 Sep 2024 08:56:12 +0000 (08:56 +0000)
FSCTL_QUERY_ALLOCATED_RANGES responses with more than one range should
be truncated to account for a ioctl.smb2.in.max_output_response limit.
Add a test for this.

Flag the new test knownfail; fix in subsequent commit.

Signed-off-by: David Disseldorp <ddiss@samba.org>
Reviewed-by: Noel Power <npower@samba.org>
(cherry picked from commit 5cf57f1f539021f1490285516d8cfb2a2ab483e0)

selftest/knownfail
source4/torture/smb2/ioctl.c

index 4e34effbbd14af41a2d175d97d265b55ed152acd..5f3d4032c7007e29aeb68fd5b961767f3428bb0c 100644 (file)
 ^samba3.smb2.lease.statopen3
 ^samba3.smb2.lease.unlink # we currently do not downgrade RH lease to R after unlink
 ^samba4.smb2.ioctl.compress_notsup.*\(ad_dc_ntvfs\)
+^samba4.smb2.ioctl.sparse_qar_truncated # bug 15699
 ^samba3.raw.session.*reauth2 # maybe fix this?
 ^samba3.rpc.lsa.secrets.seal # This gives NT_STATUS_LOCAL_USER_SESSION_KEY
 ^samba3.rpc.samr.passwords.badpwdcount.samr.badPwdCount\(nt4_dc\) # We fail this test currently
index 624155584235bf861a1867d5185b5cad44a0277d..07dbf06036a59850e2796cf460deb776c7db0271 100644 (file)
@@ -3,7 +3,7 @@
 
    test suite for SMB2 ioctl operations
 
-   Copyright (C) David Disseldorp 2011-2016
+   Copyright (C) David Disseldorp 2011-2024
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -3838,6 +3838,151 @@ static bool test_ioctl_sparse_qar_malformed(struct torture_context *torture,
        return true;
 }
 
+static bool test_ioctl_sparse_qar_truncated(struct torture_context *torture,
+                                           struct smb2_tree *tree)
+{
+       struct smb2_handle fh;
+       union smb_ioctl ioctl;
+       struct file_alloced_range_buf far_buf;
+       NTSTATUS status;
+       enum ndr_err_code ndr_ret;
+       TALLOC_CTX *tmp_ctx = talloc_new(tree);
+       bool ok;
+       struct file_alloced_range_buf far_rsp;
+
+       ok = test_setup_create_fill(torture, tree, tmp_ctx,
+                                   FNAME, &fh, 0, SEC_RIGHTS_FILE_ALL,
+                                   FILE_ATTRIBUTE_NORMAL);
+       torture_assert(torture, ok, "setup file");
+
+       status = test_ioctl_fs_supported(torture, tree, tmp_ctx, &fh,
+                                        FILE_SUPPORTS_SPARSE_FILES, &ok);
+       torture_assert_ntstatus_ok(torture, status, "SMB2_GETINFO_FS");
+       if (!ok) {
+               smb2_util_close(tree, fh);
+               torture_skip(torture, "Sparse files not supported\n");
+       }
+
+       status = test_ioctl_sparse_req(torture, tmp_ctx, tree, fh, true);
+       torture_assert_ntstatus_ok(torture, status, "FSCTL_SET_SPARSE");
+
+       /*
+        * Write 0 and 1M offsets as (hopefully) two separate extents.
+        * XXX this test assumes that these ranges will be recorded as separate
+        * FSCTL_QUERY_ALLOCATED_RANGES extents, which isn't strictly required:
+        * the spec basically says the FS can do what it wants as long as
+        * non-zeroed data ranges aren't reported as sparse holes.
+        */
+       ok = write_pattern(torture, tree, tmp_ctx, fh,
+                          0,           /* off */
+                          1024,        /* len */
+                          0);          /* pattern offset */
+       torture_assert(torture, ok, "write pattern");
+       ok = write_pattern(torture, tree, tmp_ctx, fh,
+                          1024 * 1024, /* off */
+                          1024,        /* len */
+                          0);          /* pattern offset */
+       torture_assert(torture, ok, "write pattern");
+
+       /* qar max output enough to carry one range, should be truncated */
+       ZERO_STRUCT(ioctl);
+       ioctl.smb2.level = RAW_IOCTL_SMB2;
+       ioctl.smb2.in.file.handle = fh;
+       ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
+       ioctl.smb2.in.max_output_response = sizeof(struct file_alloced_range_buf);
+       ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
+
+       far_buf.file_off = 0;
+       far_buf.len = 2048 * 1024;
+       ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
+                                      &far_buf,
+                       (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
+       torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
+
+       status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
+       torture_assert_ntstatus_equal(torture, status,
+                                     STATUS_BUFFER_OVERFLOW, "qar truncated");
+       torture_assert_u64_equal(torture,
+                                 ioctl.smb2.out.out.length, sizeof(far_buf),
+                                 "qar outlen");
+       ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
+                                      &far_rsp,
+                       (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
+       torture_assert_ndr_success(torture, ndr_ret, "pull far range");
+       torture_assert_u64_equal(torture, far_rsp.file_off, 0, "far offset");
+       /* length depends on allocation behaviour of FS, so allow range */
+       torture_assert(torture, far_rsp.len >= 1024, "far len");
+
+       /* qar max output for just under 2 ranges, should be truncated */
+       ZERO_STRUCT(ioctl);
+       ioctl.smb2.level = RAW_IOCTL_SMB2;
+       ioctl.smb2.in.file.handle = fh;
+       ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
+       ioctl.smb2.in.max_output_response = 31;
+       ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
+
+       far_buf.file_off = 0;
+       far_buf.len = 2048 * 1024;
+       ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
+                                      &far_buf,
+                       (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
+       torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
+
+       status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
+       torture_assert_ntstatus_equal(torture, status,
+                                     STATUS_BUFFER_OVERFLOW, "qar truncated");
+       torture_assert_u64_equal(torture,
+                                 ioctl.smb2.out.out.length, sizeof(far_buf),
+                                 "qar outlen");
+       ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
+                                      &far_rsp,
+                       (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
+       torture_assert_ndr_success(torture, ndr_ret, "pull far range");
+       torture_assert_u64_equal(torture, far_rsp.file_off, 0, "far offset");
+       torture_assert(torture, far_rsp.len >= 1024, "far len");
+
+       /* qar max output for 2 ranges, should pass */
+       ZERO_STRUCT(ioctl);
+       ioctl.smb2.level = RAW_IOCTL_SMB2;
+       ioctl.smb2.in.file.handle = fh;
+       ioctl.smb2.in.function = FSCTL_QUERY_ALLOCATED_RANGES;
+       ioctl.smb2.in.max_output_response = 32;
+       ioctl.smb2.in.flags = SMB2_IOCTL_FLAG_IS_FSCTL;
+
+       far_buf.file_off = 0;
+       far_buf.len = 2048 * 1024;
+       ndr_ret = ndr_push_struct_blob(&ioctl.smb2.in.out, tmp_ctx,
+                                      &far_buf,
+                       (ndr_push_flags_fn_t)ndr_push_file_alloced_range_buf);
+       torture_assert_ndr_success(torture, ndr_ret, "push far ndr buf");
+
+       status = smb2_ioctl(tree, tmp_ctx, &ioctl.smb2);
+       torture_assert_ntstatus_ok(torture, status, "qar non-truncated");
+       torture_assert_u64_equal(torture,
+                                 ioctl.smb2.out.out.length,
+                                 2 * sizeof(far_buf), "qar outlen");
+       ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
+                                      &far_rsp,
+                       (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
+       torture_assert_ndr_success(torture, ndr_ret, "pull far range");
+       torture_assert_u64_equal(torture, far_rsp.file_off, 0, "far offset");
+       torture_assert(torture, far_rsp.len >= 1024, "far len");
+       /* move to next buffer */
+       ioctl.smb2.out.out.data += sizeof(far_buf);
+       ioctl.smb2.out.out.length -= sizeof(far_buf);
+       ndr_ret = ndr_pull_struct_blob(&ioctl.smb2.out.out, tmp_ctx,
+                                      &far_rsp,
+                       (ndr_pull_flags_fn_t)ndr_pull_file_alloced_range_buf);
+       torture_assert_ndr_success(torture, ndr_ret, "pull far range");
+       torture_assert_u64_equal(torture, far_rsp.file_off, 1024 * 1024,
+                                "far offset");
+       torture_assert(torture, far_rsp.len >= 1024, "far len");
+
+       smb2_util_close(tree, fh);
+       talloc_free(tmp_ctx);
+       return true;
+}
+
 bool test_ioctl_alternate_data_stream(struct torture_context *tctx)
 {
        bool ret = false;
@@ -7548,6 +7693,8 @@ struct torture_suite *torture_smb2_ioctl_init(TALLOC_CTX *ctx)
                                     test_ioctl_sparse_qar);
        torture_suite_add_1smb2_test(suite, "sparse_qar_malformed",
                                     test_ioctl_sparse_qar_malformed);
+       torture_suite_add_1smb2_test(suite, "sparse_qar_truncated",
+                                    test_ioctl_sparse_qar_truncated);
        torture_suite_add_1smb2_test(suite, "sparse_punch",
                                     test_ioctl_sparse_punch);
        torture_suite_add_1smb2_test(suite, "sparse_hole_dealloc",