From 1f3b7158c37e5205b962d84dcf6e65b24b230c23 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 11 May 2020 12:37:41 +0200 Subject: [PATCH 01/31] s4:torture: add tests to test the SMB2 read/write offset/length boundaries [MS-FSA] 2.1.5.2 Server Requests a Read and 2.1.5.3 Server Requests a Write define some contraints. These tests demonstrate that ((int64_t)offset) < 0) is not allowed for both reads and writes for SMB. Also the special case for writes at offset -2 is not possible nor the append mode with offset < 0. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- selftest/knownfail.d/rw-invalid | 2 + source4/torture/smb2/read_write.c | 189 ++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 selftest/knownfail.d/rw-invalid diff --git a/selftest/knownfail.d/rw-invalid b/selftest/knownfail.d/rw-invalid new file mode 100644 index 000000000000..c6f11e03d20b --- /dev/null +++ b/selftest/knownfail.d/rw-invalid @@ -0,0 +1,2 @@ +samba3.smb2.rw.invalid +samba4.smb2.rw.invalid diff --git a/source4/torture/smb2/read_write.c b/source4/torture/smb2/read_write.c index bc8898cec31a..b0eea55d7f1f 100644 --- a/source4/torture/smb2/read_write.c +++ b/source4/torture/smb2/read_write.c @@ -23,8 +23,19 @@ #include "libcli/smb2/smb2.h" #include "libcli/smb2/smb2_calls.h" #include "torture/torture.h" +#include "torture/util.h" #include "torture/smb2/proto.h" +#define CHECK_STATUS(_status, _expected) \ + torture_assert_ntstatus_equal_goto(torture, _status, _expected, \ + ret, done, "Incorrect status") + +#define CHECK_VALUE(v, correct) \ + torture_assert_int_equal_goto(torture, v, correct, \ + ret, done, "Incorrect value") + +#define FNAME "smb2_writetest.dat" + static bool run_smb2_readwritetest(struct torture_context *tctx, struct smb2_tree *t1, struct smb2_tree *t2) { @@ -150,12 +161,190 @@ static bool run_smb2_wrap_readwritetest(struct torture_context *tctx, return run_smb2_readwritetest(tctx, tree1, tree1); } +static bool test_rw_invalid(struct torture_context *torture, struct smb2_tree *tree) +{ + bool ret = true; + NTSTATUS status; + struct smb2_handle h; + uint8_t buf[64*1024]; + struct smb2_read rd; + struct smb2_write w = {0}; + TALLOC_CTX *tmp_ctx = talloc_new(tree); + + ZERO_STRUCT(buf); + + smb2_util_unlink(tree, FNAME); + + status = torture_smb2_testfile(tree, FNAME, &h); + CHECK_STATUS(status, NT_STATUS_OK); + + status = smb2_util_write(tree, h, buf, 0, ARRAY_SIZE(buf)); + CHECK_STATUS(status, NT_STATUS_OK); + + ZERO_STRUCT(rd); + rd.in.file.handle = h; + rd.in.length = 10; + rd.in.offset = 0; + rd.in.min_count = 1; + + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(rd.out.data.length, 10); + + rd.in.min_count = 0; + rd.in.length = 10; + rd.in.offset = sizeof(buf); + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_END_OF_FILE); + + rd.in.min_count = 0; + rd.in.length = 0; + rd.in.offset = sizeof(buf); + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(rd.out.data.length, 0); + + rd.in.min_count = 0; + rd.in.length = 1; + rd.in.offset = INT64_MAX - 1; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_END_OF_FILE); + + rd.in.min_count = 0; + rd.in.length = 0; + rd.in.offset = INT64_MAX; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(rd.out.data.length, 0); + + rd.in.min_count = 0; + rd.in.length = 1; + rd.in.offset = INT64_MAX; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + rd.in.min_count = 0; + rd.in.length = 0; + rd.in.offset = (uint64_t)INT64_MAX + 1; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + rd.in.min_count = 0; + rd.in.length = 0; + rd.in.offset = (uint64_t)INT64_MIN; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + rd.in.min_count = 0; + rd.in.length = 0; + rd.in.offset = (uint64_t)(int64_t)-1; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + rd.in.min_count = 0; + rd.in.length = 0; + rd.in.offset = (uint64_t)(int64_t)-2; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + rd.in.min_count = 0; + rd.in.length = 0; + rd.in.offset = (uint64_t)(int64_t)-3; + status = smb2_read(tree, tmp_ctx, &rd); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + w.in.file.handle = h; + w.in.offset = (int64_t)-1; + w.in.data.data = buf; + w.in.data.length = ARRAY_SIZE(buf); + + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + w.in.file.handle = h; + w.in.offset = (int64_t)-2; + w.in.data.data = buf; + w.in.data.length = ARRAY_SIZE(buf); + + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + w.in.file.handle = h; + w.in.offset = INT64_MIN; + w.in.data.data = buf; + w.in.data.length = 1; + + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + w.in.file.handle = h; + w.in.offset = INT64_MIN; + w.in.data.data = buf; + w.in.data.length = 0; + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + w.in.file.handle = h; + w.in.offset = INT64_MAX; + w.in.data.data = buf; + w.in.data.length = 0; + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(w.out.nwritten, 0); + + w.in.file.handle = h; + w.in.offset = INT64_MAX; + w.in.data.data = buf; + w.in.data.length = 1; + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + w.in.file.handle = h; + w.in.offset = (uint64_t)INT64_MAX + 1; + w.in.data.data = buf; + w.in.data.length = 0; + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + w.in.file.handle = h; + w.in.offset = 0xfffffff0000; /* MAXFILESIZE */ + w.in.data.data = buf; + w.in.data.length = 1; + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_INVALID_PARAMETER); + + w.in.file.handle = h; + w.in.offset = 0xfffffff0000 - 1; /* MAXFILESIZE - 1 */ + w.in.data.data = buf; + w.in.data.length = 1; + status = smb2_write(tree, &w); + if (TARGET_IS_SAMBA3(torture) || TARGET_IS_SAMBA4(torture)) { + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(w.out.nwritten, 1); + } else { + CHECK_STATUS(status, NT_STATUS_DISK_FULL); + } + + w.in.file.handle = h; + w.in.offset = 0xfffffff0000; /* MAXFILESIZE */ + w.in.data.data = buf; + w.in.data.length = 0; + status = smb2_write(tree, &w); + CHECK_STATUS(status, NT_STATUS_OK); + CHECK_VALUE(w.out.nwritten, 0); + +done: + talloc_free(tmp_ctx); + return ret; +} + struct torture_suite *torture_smb2_readwrite_init(TALLOC_CTX *ctx) { struct torture_suite *suite = torture_suite_create(ctx, "rw"); torture_suite_add_2smb2_test(suite, "rw1", run_smb2_readwritetest); torture_suite_add_2smb2_test(suite, "rw2", run_smb2_wrap_readwritetest); + torture_suite_add_1smb2_test(suite, "invalid", test_rw_invalid); suite->description = talloc_strdup(suite, "SMB2 Samba4 Read/Write"); -- 2.17.1 From 96a22c0f125cc9bc4684394f09d472705340ced2 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 13:06:54 +0200 Subject: [PATCH 02/31] lib: util: Add sys_valid_io_range() This implements the contraints of [MS-FSA] 2.1.5.2 Server Requests a Read. The special handling of [MS-FSA] 2.1.5.3 Server Requests a Write with offset < 0, should be handled by higher layers! Which means the check can also be used for writes. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- lib/util/sys_rw.c | 24 ++++++++++++++++++++++++ lib/util/sys_rw.h | 1 + 2 files changed, 25 insertions(+) diff --git a/lib/util/sys_rw.c b/lib/util/sys_rw.c index 9a6cdcaa6063..6fa7ca573654 100644 --- a/lib/util/sys_rw.c +++ b/lib/util/sys_rw.c @@ -24,6 +24,30 @@ #include "system/filesys.h" #include "lib/util/sys_rw.h" +bool sys_valid_io_range(off_t offset, size_t length) +{ + uint64_t last_byte_ofs; + + if (offset < 0) { + return false; + } + + if (offset > INT64_MAX) { + return false; + } + + if (length > UINT32_MAX) { + return false; + } + + last_byte_ofs = (uint64_t)offset + (uint64_t)length; + if (last_byte_ofs > INT64_MAX) { + return false; + } + + return true; +} + /******************************************************************* A read wrapper that will deal with EINTR/EWOULDBLOCK ********************************************************************/ diff --git a/lib/util/sys_rw.h b/lib/util/sys_rw.h index ab456d87b22e..70864cb2b74a 100644 --- a/lib/util/sys_rw.h +++ b/lib/util/sys_rw.h @@ -27,6 +27,7 @@ struct iovec; +bool sys_valid_io_range(off_t offset, size_t length); ssize_t sys_read(int fd, void *buf, size_t count); void sys_read_v(int fd, void *buf, size_t count); ssize_t sys_write(int fd, const void *buf, size_t count); -- 2.17.1 From 0534b1999f6300ea5f3738188f32a936f8ae5d35 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 7 May 2020 12:32:48 -0700 Subject: [PATCH 03/31] lib: util: Add sys_pread_full(). A pread wrapper that will deal with EINTR and never return a short read unless pread returns zero meaning EOF. Thread-safe so may be used as a replacement for pread inside pread_do() thread functions. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher --- lib/util/sys_rw.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++ lib/util/sys_rw.h | 1 + 2 files changed, 49 insertions(+) diff --git a/lib/util/sys_rw.c b/lib/util/sys_rw.c index 6fa7ca573654..bfeb2e6b4661 100644 --- a/lib/util/sys_rw.c +++ b/lib/util/sys_rw.c @@ -143,6 +143,54 @@ ssize_t sys_pread(int fd, void *buf, size_t count, off_t off) return ret; } +/******************************************************************* + A pread wrapper that will deal with EINTR and never return a short + read unless pread returns zero meaning EOF. +********************************************************************/ + +ssize_t sys_pread_full(int fd, void *buf, size_t count, off_t off) +{ + ssize_t total_read = 0; + uint8_t *curr_buf = (uint8_t *)buf; + size_t curr_count = count; + off_t curr_off = off; + bool ok; + + ok = sys_valid_io_range(off, count); + if (!ok) { + errno = EINVAL; + return -1; + } + + while (curr_count != 0) { + ssize_t ret = sys_pread(fd, + curr_buf, + curr_count, + curr_off); + + if (ret == -1) { + return -1; + } + if (ret == 0) { + /* EOF */ + break; + } + + if (ret > curr_count) { + errno = EIO; + return -1; + } + + curr_buf += ret; + curr_count -= ret; + curr_off += ret; + + total_read += ret; + } + + return total_read; +} + /******************************************************************* A write wrapper that will deal with EINTR ********************************************************************/ diff --git a/lib/util/sys_rw.h b/lib/util/sys_rw.h index 70864cb2b74a..1e0dd3730a60 100644 --- a/lib/util/sys_rw.h +++ b/lib/util/sys_rw.h @@ -34,6 +34,7 @@ ssize_t sys_write(int fd, const void *buf, size_t count); void sys_write_v(int fd, const void *buf, size_t count); ssize_t sys_writev(int fd, const struct iovec *iov, int iovcnt); ssize_t sys_pread(int fd, void *buf, size_t count, off_t off); +ssize_t sys_pread_full(int fd, void *buf, size_t count, off_t off); ssize_t sys_pwrite(int fd, const void *buf, size_t count, off_t off); #endif -- 2.17.1 From 24bf1acbfa180a5ef2b2c43e327374cb4fb92191 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 7 May 2020 12:34:32 -0700 Subject: [PATCH 04/31] lib: util: Add sys_pwrite_full(). A pwrite wrapper that will deal with EINTR and never return a short write unless the file system returns an error. Copes with the unspecified edge condition of pwrite returning zero by changing the return to -1, errno = ENOSPC. Thread-safe so may be used as a replacement for pwrite inside pwrite_do() thread functions. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Pair-Programmed-With: Stefan Metzmacher Signed-off-by: Jeremy Allison Signed-off-by: Stefan Metzmacher --- lib/util/sys_rw.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++ lib/util/sys_rw.h | 1 + 2 files changed, 50 insertions(+) diff --git a/lib/util/sys_rw.c b/lib/util/sys_rw.c index bfeb2e6b4661..d74395fc4091 100644 --- a/lib/util/sys_rw.c +++ b/lib/util/sys_rw.c @@ -204,3 +204,52 @@ ssize_t sys_pwrite(int fd, const void *buf, size_t count, off_t off) } while (ret == -1 && errno == EINTR); return ret; } + +/******************************************************************* + A pwrite wrapper that will deal with EINTR and never allow a short + write unless the file system returns an error. +********************************************************************/ + +ssize_t sys_pwrite_full(int fd, const void *buf, size_t count, off_t off) +{ + ssize_t total_written = 0; + const uint8_t *curr_buf = (const uint8_t *)buf; + size_t curr_count = count; + off_t curr_off = off; + bool ok; + + ok = sys_valid_io_range(off, count); + if (!ok) { + errno = EINVAL; + return -1; + } + + while (curr_count != 0) { + ssize_t ret = sys_pwrite(fd, + curr_buf, + curr_count, + curr_off); + + if (ret == -1) { + return -1; + } + if (ret == 0) { + /* Ensure we can never spin. */ + errno = ENOSPC; + return -1; + } + + if (ret > curr_count) { + errno = EIO; + return -1; + } + + curr_buf += ret; + curr_count -= ret; + curr_off += ret; + + total_written += ret; + } + + return total_written; +} diff --git a/lib/util/sys_rw.h b/lib/util/sys_rw.h index 1e0dd3730a60..b224ecb30ac7 100644 --- a/lib/util/sys_rw.h +++ b/lib/util/sys_rw.h @@ -36,5 +36,6 @@ ssize_t sys_writev(int fd, const struct iovec *iov, int iovcnt); ssize_t sys_pread(int fd, void *buf, size_t count, off_t off); ssize_t sys_pread_full(int fd, void *buf, size_t count, off_t off); ssize_t sys_pwrite(int fd, const void *buf, size_t count, off_t off); +ssize_t sys_pwrite_full(int fd, const void *buf, size_t count, off_t off); #endif -- 2.17.1 From 693e9190e78a6284a7071f73221742ad39f94416 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 11 May 2020 18:18:24 +0200 Subject: [PATCH 05/31] smb2_server: fix smbd_smb2_request_verify_sizes() for SMB2_OP_WRITE Writes with a length of 0 are allowed. The readfile related check we had before was not really useful as min_dyn_len can only every be 0 or 1 (and for SMB2_OP_WRITE it's always 1). So we checked if (unread_bytes > 0) { if (unread_bytes < 1) { return error; } } BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/smbd/smb2_server.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index a4372bf11457..718f09415326 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -2073,16 +2073,8 @@ NTSTATUS smbd_smb2_request_verify_sizes(struct smbd_smb2_request *req, switch (opcode) { case SMB2_OP_IOCTL: case SMB2_OP_GETINFO: - min_dyn_size = 0; - break; case SMB2_OP_WRITE: - if (req->smb1req != NULL && req->smb1req->unread_bytes > 0) { - if (req->smb1req->unread_bytes < min_dyn_size) { - return NT_STATUS_INVALID_PARAMETER; - } - - min_dyn_size = 0; - } + min_dyn_size = 0; break; } -- 2.17.1 From 672a77370113623fb3a87c17f85755d84708cafd Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 11 May 2020 18:18:24 +0200 Subject: [PATCH 06/31] s3:smbd: handle 0 length writes as no-op. They should never touch the SMB_VFS layer and they never trigger an DISK_FULL error. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/smbd/aio.c | 5 +++++ source3/smbd/fileio.c | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/source3/smbd/aio.c b/source3/smbd/aio.c index 7ed2691cfbff..f89ce8537a05 100644 --- a/source3/smbd/aio.c +++ b/source3/smbd/aio.c @@ -337,6 +337,11 @@ static struct tevent_req *pwrite_fsync_send(TALLOC_CTX *mem_ctx, state->fsp = fsp; state->write_through = write_through; + if (n == 0) { + tevent_req_done(req); + return tevent_req_post(req, ev); + } + subreq = SMB_VFS_PWRITE_SEND(state, ev, fsp, data, n, offset); if (tevent_req_nomem(subreq, req)) { return tevent_req_post(req, ev); diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c index 6d58bdbbc97e..079d414db05f 100644 --- a/source3/smbd/fileio.c +++ b/source3/smbd/fileio.c @@ -70,6 +70,10 @@ static ssize_t real_write_file(struct smb_request *req, { ssize_t ret; + if (n == 0) { + return 0; + } + fsp->fh->pos = pos; if (pos && lp_strict_allocate(SNUM(fsp->conn)) && -- 2.17.1 From 02deeda3d9a007480b66e2e1a281d57970775761 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 11 May 2020 22:00:37 +0200 Subject: [PATCH 07/31] s3:smbd: add vfs_valid_{pread,pwrite}_range() helper functions These implement the SMB2 visible behavior of the [MS-FSA] 2.1.5.2 Server Requests a Read and 2.1.5.3 Server Requests a Write constraints. Note that offset < 0 is not allowed over SMB. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/smbd/proto.h | 2 ++ source3/smbd/vfs.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h index 98eb2843c070..ba72fb94e0fc 100644 --- a/source3/smbd/proto.h +++ b/source3/smbd/proto.h @@ -1295,6 +1295,8 @@ void sys_utmp_yield(const char *username, const char *hostname, bool vfs_init_custom(connection_struct *conn, const char *vfs_object); bool smbd_vfs_init(connection_struct *conn); NTSTATUS vfs_file_exist(connection_struct *conn, struct smb_filename *smb_fname); +bool vfs_valid_pread_range(off_t offset, size_t length); +bool vfs_valid_pwrite_range(off_t offset, size_t length); ssize_t vfs_pwrite_data(struct smb_request *req, files_struct *fsp, const char *buffer, diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c index 9c75ceed6aee..5141da728a78 100644 --- a/source3/smbd/vfs.c +++ b/source3/smbd/vfs.c @@ -32,6 +32,7 @@ #include "ntioctl.h" #include "lib/util/tevent_unix.h" #include "lib/util/tevent_ntstatus.h" +#include "lib/util/sys_rw.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_VFS @@ -415,6 +416,37 @@ NTSTATUS vfs_file_exist(connection_struct *conn, struct smb_filename *smb_fname) return NT_STATUS_OBJECT_NAME_NOT_FOUND; } +bool vfs_valid_pread_range(off_t offset, size_t length) +{ + return sys_valid_io_range(offset, length); +} + +bool vfs_valid_pwrite_range(off_t offset, size_t length) +{ + /* + * See MAXFILESIZE in [MS-FSA] 2.1.5.3 Server Requests a Write + */ + static const uint64_t maxfilesize = 0xfffffff0000; + uint64_t last_byte_ofs; + bool ok; + + ok = sys_valid_io_range(offset, length); + if (!ok) { + return false; + } + + if (length == 0) { + return true; + } + + last_byte_ofs = offset + length; + if (last_byte_ofs > maxfilesize) { + return false; + } + + return true; +} + ssize_t vfs_pwrite_data(struct smb_request *req, files_struct *fsp, const char *buffer, -- 2.17.1 From 31c111d3627319afeb7ee87285679e124559e81e Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Mon, 11 May 2020 18:18:24 +0200 Subject: [PATCH 08/31] smbd: add vfs_valid_{pread,pwrite}_range() checks where needed I checked all callers of SMB_VFS_PWRITE[_SEND](), all callers of SMB_VFS_PREAD[_SEND]() and also places where we append to the file and allocate more space. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- selftest/knownfail.d/rw-invalid | 3 +-- source3/modules/vfs_default.c | 7 +++++++ source3/smbd/aio.c | 19 ++++++++++++++++++ source3/smbd/fileio.c | 14 ++++++++++++++ source3/smbd/vfs.c | 34 +++++++++++++++++++++++++++++++-- 5 files changed, 73 insertions(+), 4 deletions(-) diff --git a/selftest/knownfail.d/rw-invalid b/selftest/knownfail.d/rw-invalid index c6f11e03d20b..ac5fe5732390 100644 --- a/selftest/knownfail.d/rw-invalid +++ b/selftest/knownfail.d/rw-invalid @@ -1,2 +1 @@ -samba3.smb2.rw.invalid -samba4.smb2.rw.invalid +samba4.smb2.rw.invalid.ad_dc_ntvfs diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index c7f2020a9ead..b5fbc0ed5dc4 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -2579,6 +2579,13 @@ static int strict_allocate_ftruncate(vfs_handle_struct *handle, files_struct *fs int ret; NTSTATUS status; SMB_STRUCT_STAT *pst; + bool ok; + + ok = vfs_valid_pwrite_range(len, 0); + if (!ok) { + errno = EINVAL; + return -1; + } status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { diff --git a/source3/smbd/aio.c b/source3/smbd/aio.c index f89ce8537a05..f141d6731674 100644 --- a/source3/smbd/aio.c +++ b/source3/smbd/aio.c @@ -164,6 +164,12 @@ NTSTATUS schedule_aio_read_and_X(connection_struct *conn, size_t bufsize; size_t min_aio_read_size = lp_aio_read_size(SNUM(conn)); struct tevent_req *req; + bool ok; + + ok = vfs_valid_pread_range(startpos, smb_maxcnt); + if (!ok) { + return NT_STATUS_INVALID_PARAMETER; + } if (fsp->base_fsp != NULL) { /* No AIO on streams yet */ @@ -328,6 +334,7 @@ static struct tevent_req *pwrite_fsync_send(TALLOC_CTX *mem_ctx, { struct tevent_req *req, *subreq; struct pwrite_fsync_state *state; + bool ok; req = tevent_req_create(mem_ctx, &state, struct pwrite_fsync_state); if (req == NULL) { @@ -337,6 +344,12 @@ static struct tevent_req *pwrite_fsync_send(TALLOC_CTX *mem_ctx, state->fsp = fsp; state->write_through = write_through; + ok = vfs_valid_pwrite_range(offset, n); + if (!ok) { + tevent_req_error(req, EINVAL); + return tevent_req_post(req, ev); + } + if (n == 0) { tevent_req_done(req); return tevent_req_post(req, ev); @@ -664,6 +677,12 @@ NTSTATUS schedule_smb2_aio_read(connection_struct *conn, struct aio_extra *aio_ex; size_t min_aio_read_size = lp_aio_read_size(SNUM(conn)); struct tevent_req *req; + bool ok; + + ok = vfs_valid_pread_range(startpos, smb_maxcnt); + if (!ok) { + return NT_STATUS_INVALID_PARAMETER; + } if (fsp->base_fsp != NULL) { /* No AIO on streams yet */ diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c index 079d414db05f..40c770da8bdc 100644 --- a/source3/smbd/fileio.c +++ b/source3/smbd/fileio.c @@ -32,6 +32,7 @@ ssize_t read_file(files_struct *fsp,char *data,off_t pos,size_t n) { ssize_t ret = 0; + bool ok; /* you can't read from print files */ if (fsp->print_file) { @@ -39,6 +40,12 @@ ssize_t read_file(files_struct *fsp,char *data,off_t pos,size_t n) return -1; } + ok = vfs_valid_pread_range(pos, n); + if (!ok) { + errno = EINVAL; + return -1; + } + fsp->fh->pos = pos; if (n > 0) { @@ -69,6 +76,13 @@ static ssize_t real_write_file(struct smb_request *req, size_t n) { ssize_t ret; + bool ok; + + ok = vfs_valid_pwrite_range(pos, n); + if (!ok) { + errno = EINVAL; + return -1; + } if (n == 0) { return 0; diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c index 5141da728a78..f49b53f4b7c6 100644 --- a/source3/smbd/vfs.c +++ b/source3/smbd/vfs.c @@ -455,6 +455,13 @@ ssize_t vfs_pwrite_data(struct smb_request *req, { size_t total=0; ssize_t ret; + bool ok; + + ok = vfs_valid_pwrite_range(offset, N); + if (!ok) { + errno = EINVAL; + return -1; + } if (req && req->unread_bytes) { int sockfd = req->xconn->transport.sock; @@ -531,6 +538,7 @@ int vfs_allocate_file_space(files_struct *fsp, uint64_t len) uint64_t space_avail; uint64_t bsize,dfree,dsize; NTSTATUS status; + bool ok; /* * Actually try and commit the space on disk.... @@ -539,8 +547,9 @@ int vfs_allocate_file_space(files_struct *fsp, uint64_t len) DEBUG(10,("vfs_allocate_file_space: file %s, len %.0f\n", fsp_str_dbg(fsp), (double)len)); - if (((off_t)len) < 0) { - DEBUG(0,("vfs_allocate_file_space: %s negative len " + ok = vfs_valid_pwrite_range((off_t)len, 0); + if (!ok) { + DEBUG(0,("vfs_allocate_file_space: %s negative/invalid len " "requested.\n", fsp_str_dbg(fsp))); errno = EINVAL; return -1; @@ -625,6 +634,13 @@ int vfs_allocate_file_space(files_struct *fsp, uint64_t len) int vfs_set_filelen(files_struct *fsp, off_t len) { int ret; + bool ok; + + ok = vfs_valid_pwrite_range(len, 0); + if (!ok) { + errno = EINVAL; + return -1; + } contend_level2_oplocks_begin(fsp, LEVEL2_CONTEND_SET_FILE_LEN); @@ -656,6 +672,13 @@ int vfs_slow_fallocate(files_struct *fsp, off_t offset, off_t len) { ssize_t pwrite_ret; size_t total = 0; + bool ok; + + ok = vfs_valid_pwrite_range(offset, len); + if (!ok) { + errno = EINVAL; + return -1; + } if (!sparse_buf) { sparse_buf = SMB_CALLOC_ARRAY(char, SPARSE_BUF_WRITE_SIZE); @@ -696,6 +719,13 @@ int vfs_fill_sparse(files_struct *fsp, off_t len) NTSTATUS status; off_t offset; size_t num_to_write; + bool ok; + + ok = vfs_valid_pwrite_range(len, 0); + if (!ok) { + errno = EINVAL; + return -1; + } status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { -- 2.17.1 From 147d0fe676b5a611c7eb39b6586cccc78148be08 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 7 May 2020 12:42:10 -0700 Subject: [PATCH 09/31] s3: VFS: aio_fork: Change sys_pread() -> sys_pread_full() to protect against short reads. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Jeremy Allison --- source3/modules/vfs_aio_fork.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/modules/vfs_aio_fork.c b/source3/modules/vfs_aio_fork.c index a1fed5c06557..285b331ff9ca 100644 --- a/source3/modules/vfs_aio_fork.c +++ b/source3/modules/vfs_aio_fork.c @@ -342,7 +342,7 @@ static void aio_child_loop(int sockfd, struct mmap_area *map) switch (cmd_struct.cmd) { case READ_CMD: - ret_struct.size = sys_pread( + ret_struct.size = sys_pread_full( fd, discard_const(map->ptr), cmd_struct.n, cmd_struct.offset); #if 0 -- 2.17.1 From 07cf444ca29279aec67e4d7067a7ee35a3242531 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 7 May 2020 12:42:53 -0700 Subject: [PATCH 10/31] s3: VFS: aio_fork: Change sys_pwrite() -> sys_pwrite_full() to protect against short writes. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/modules/vfs_aio_fork.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/modules/vfs_aio_fork.c b/source3/modules/vfs_aio_fork.c index 285b331ff9ca..7c6f4b00fd04 100644 --- a/source3/modules/vfs_aio_fork.c +++ b/source3/modules/vfs_aio_fork.c @@ -353,7 +353,7 @@ static void aio_child_loop(int sockfd, struct mmap_area *map) #endif break; case WRITE_CMD: - ret_struct.size = sys_pwrite( + ret_struct.size = sys_pwrite_full( fd, discard_const(map->ptr), cmd_struct.n, cmd_struct.offset); break; -- 2.17.1 From 777fffa160588496bf11b6e19355bc0cd7f8c958 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 7 May 2020 12:43:34 -0700 Subject: [PATCH 11/31] s3: VFS: default. Change sys_pread() -> sys_pread_full() in SMB_VFS_PREAD() to protect against short reads. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Jeremy Allison --- source3/modules/vfs_default.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index b5fbc0ed5dc4..f76d7c7b918b 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -734,7 +734,7 @@ static ssize_t vfswrap_pread(vfs_handle_struct *handle, files_struct *fsp, void #if defined(HAVE_PREAD) || defined(HAVE_PREAD64) START_PROFILE_BYTES(syscall_pread, n); - result = sys_pread(fsp->fh->fd, data, n, offset); + result = sys_pread_full(fsp->fh->fd, data, n, offset); END_PROFILE_BYTES(syscall_pread); if (result == -1 && errno == ESPIPE) { -- 2.17.1 From fadc02637234662d09f1bc56dbf35201e0c8eec1 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 7 May 2020 12:44:26 -0700 Subject: [PATCH 12/31] s3: VFS: default. Change sys_pwrite() -> sys_pwrite_full() in SMB_VFS_PWRITE() to protect against short writes. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/modules/vfs_default.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index f76d7c7b918b..ff46966536b8 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -758,7 +758,7 @@ static ssize_t vfswrap_pwrite(vfs_handle_struct *handle, files_struct *fsp, cons #if defined(HAVE_PWRITE) || defined(HAVE_PRWITE64) START_PROFILE_BYTES(syscall_pwrite, n); - result = sys_pwrite(fsp->fh->fd, data, n, offset); + result = sys_pwrite_full(fsp->fh->fd, data, n, offset); END_PROFILE_BYTES(syscall_pwrite); if (result == -1 && errno == ESPIPE) { -- 2.17.1 From 86c6e78fd55a03ef753bb5796c4ae8bcfd816261 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 7 May 2020 12:45:10 -0700 Subject: [PATCH 13/31] s3: VFS: default. Change pread() -> sys_pread_full() in SMB_VFS_PREAD_SEND() to protect against short reads. Note that as sys_pread_full() deals with the EINTR case we can remove the do {} while loop here. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Jeremy Allison --- source3/modules/vfs_default.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index ff46966536b8..bce50b990c11 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -838,10 +838,10 @@ static void vfs_pread_do(void *private_data) PROFILE_TIMESTAMP(&start_time); - do { - state->ret = pread(state->fd, state->buf, state->count, - state->offset); - } while ((state->ret == -1) && (errno == EINTR)); + state->ret = sys_pread_full(state->fd, + state->buf, + state->count, + state->offset); if (state->ret == -1) { state->vfs_aio_state.error = errno; -- 2.17.1 From 8e194063c88953f213418d99bc0cee1397dbbd78 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Thu, 7 May 2020 12:48:49 -0700 Subject: [PATCH 14/31] s3: VFS: default. Change pwrite() -> sys_pwrite_full() in SMB_VFS_PWRITE_SEND() to protect against short writes. Note that as sys_pwrite_full() deals with the EINTR case we can remove the do {} while loop here. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Jeremy Allison Reviewed-by: Stefan Metzmacher --- source3/modules/vfs_default.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source3/modules/vfs_default.c b/source3/modules/vfs_default.c index bce50b990c11..386a34f81d12 100644 --- a/source3/modules/vfs_default.c +++ b/source3/modules/vfs_default.c @@ -966,10 +966,10 @@ static void vfs_pwrite_do(void *private_data) PROFILE_TIMESTAMP(&start_time); - do { - state->ret = pwrite(state->fd, state->buf, state->count, - state->offset); - } while ((state->ret == -1) && (errno == EINTR)); + state->ret = sys_pwrite_full(state->fd, + state->buf, + state->count, + state->offset); if (state->ret == -1) { state->vfs_aio_state.error = errno; -- 2.17.1 From 9bb462055192ecc28f115725a1613dc2ef087fe2 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 6 May 2020 03:05:47 -0700 Subject: [PATCH 15/31] vfs_io_uring: fix the prefix for parametric options from 'vfs_io_uring' to 'io_uring' This is what the manpage describes. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index 378e48d112f6..b409d0753379 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -172,13 +172,13 @@ static int vfs_io_uring_connect(vfs_handle_struct *handle, const char *service, } num_entries = lp_parm_ulong(SNUM(handle->conn), - "vfs_io_uring", + "io_uring", "num_entries", 128); num_entries = MAX(num_entries, 1); sqpoll = lp_parm_bool(SNUM(handle->conn), - "vfs_io_uring", + "io_uring", "sqpoll", false); if (sqpoll) { -- 2.17.1 From 4c9cbd31a54a5a3d4e6b0374e61cba9bf054b2ac Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 10:39:52 +0200 Subject: [PATCH 16/31] vfs_io_uring: replace vfs_io_uring_request->state with _tevent_req_data() We don't need a direct pointer to the state... BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index b409d0753379..988b309da525 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -42,7 +42,6 @@ struct vfs_io_uring_request { struct vfs_io_uring_request **list_head; struct vfs_io_uring_config *config; struct tevent_req *req; - void *state; struct io_uring_sqe sqe; struct io_uring_cqe cqe; struct timespec start_time; @@ -58,8 +57,9 @@ static void vfs_io_uring_finish_req(struct vfs_io_uring_request *cur, struct tevent_req *req = talloc_get_type_abort(cur->req, struct tevent_req); + void *state = _tevent_req_data(req); - talloc_set_destructor(cur->state, NULL); + talloc_set_destructor(state, NULL); if (cur->list_head != NULL) { DLIST_REMOVE((*cur->list_head), cur); cur->list_head = NULL; @@ -238,6 +238,7 @@ static void vfs_io_uring_queue_run(struct vfs_io_uring_config *config) for (cur = config->queue; cur != NULL; cur = next) { struct io_uring_sqe *sqe = NULL; + void *state = _tevent_req_data(cur->req); next = cur->next; @@ -246,7 +247,7 @@ static void vfs_io_uring_queue_run(struct vfs_io_uring_config *config) break; } - talloc_set_destructor(cur->state, + talloc_set_destructor(state, vfs_io_uring_request_state_deny_destructor); DLIST_REMOVE(config->queue, cur); *sqe = cur->sqe; @@ -318,7 +319,6 @@ static struct tevent_req *vfs_io_uring_pread_send(struct vfs_handle_struct *hand } state->ur.config = config; state->ur.req = req; - state->ur.state = state; SMBPROFILE_BYTES_ASYNC_START(syscall_asys_pread, profile_p, state->ur.profile_bytes, n); @@ -398,7 +398,6 @@ static struct tevent_req *vfs_io_uring_pwrite_send(struct vfs_handle_struct *han } state->ur.config = config; state->ur.req = req; - state->ur.state = state; SMBPROFILE_BYTES_ASYNC_START(syscall_asys_pwrite, profile_p, state->ur.profile_bytes, n); @@ -475,7 +474,6 @@ static struct tevent_req *vfs_io_uring_fsync_send(struct vfs_handle_struct *hand } state->ur.config = config; state->ur.req = req; - state->ur.state = state; SMBPROFILE_BYTES_ASYNC_START(syscall_asys_fsync, profile_p, state->ur.profile_bytes, 0); -- 2.17.1 From f1f88f7e78bc01a10dacba2194b4f4981cecc7a9 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 10:42:59 +0200 Subject: [PATCH 17/31] vfs_io_uring: introduce vfs_io_uring_request->completion_fn() We'll need to add more logic than a simple _tevent_req_done() BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 49 +++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index 988b309da525..abdd4d16e9f8 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -44,6 +44,8 @@ struct vfs_io_uring_request { struct tevent_req *req; struct io_uring_sqe sqe; struct io_uring_cqe cqe; + void (*completion_fn)(struct vfs_io_uring_request *cur, + const char *location); struct timespec start_time; struct timespec end_time; SMBPROFILE_BYTES_ASYNC_STATE(profile_bytes); @@ -74,7 +76,7 @@ static void vfs_io_uring_finish_req(struct vfs_io_uring_request *cur, * or tevent_req_defer_callback() being called * already. */ - _tevent_req_done(req, location); + cur->completion_fn(cur, location); } static void vfs_io_uring_config_destroy(struct vfs_io_uring_config *config, @@ -297,6 +299,9 @@ struct vfs_io_uring_pread_state { struct iovec iov; }; +static void vfs_io_uring_pread_completion(struct vfs_io_uring_request *cur, + const char *location); + static struct tevent_req *vfs_io_uring_pread_send(struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -319,6 +324,7 @@ static struct tevent_req *vfs_io_uring_pread_send(struct vfs_handle_struct *hand } state->ur.config = config; state->ur.req = req; + state->ur.completion_fn = vfs_io_uring_pread_completion; SMBPROFILE_BYTES_ASYNC_START(syscall_asys_pread, profile_p, state->ur.profile_bytes, n); @@ -344,6 +350,17 @@ static struct tevent_req *vfs_io_uring_pread_send(struct vfs_handle_struct *hand return req; } +static void vfs_io_uring_pread_completion(struct vfs_io_uring_request *cur, + const char *location) +{ + /* + * We rely on being inside the _send() function + * or tevent_req_defer_callback() being called + * already. + */ + _tevent_req_done(cur->req, location); +} + static ssize_t vfs_io_uring_pread_recv(struct tevent_req *req, struct vfs_aio_state *vfs_aio_state) { @@ -376,6 +393,9 @@ struct vfs_io_uring_pwrite_state { struct iovec iov; }; +static void vfs_io_uring_pwrite_completion(struct vfs_io_uring_request *cur, + const char *location); + static struct tevent_req *vfs_io_uring_pwrite_send(struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -398,6 +418,7 @@ static struct tevent_req *vfs_io_uring_pwrite_send(struct vfs_handle_struct *han } state->ur.config = config; state->ur.req = req; + state->ur.completion_fn = vfs_io_uring_pwrite_completion; SMBPROFILE_BYTES_ASYNC_START(syscall_asys_pwrite, profile_p, state->ur.profile_bytes, n); @@ -423,6 +444,17 @@ static struct tevent_req *vfs_io_uring_pwrite_send(struct vfs_handle_struct *han return req; } +static void vfs_io_uring_pwrite_completion(struct vfs_io_uring_request *cur, + const char *location) +{ + /* + * We rely on being inside the _send() function + * or tevent_req_defer_callback() being called + * already. + */ + _tevent_req_done(cur->req, location); +} + static ssize_t vfs_io_uring_pwrite_recv(struct tevent_req *req, struct vfs_aio_state *vfs_aio_state) { @@ -454,6 +486,9 @@ struct vfs_io_uring_fsync_state { struct vfs_io_uring_request ur; }; +static void vfs_io_uring_fsync_completion(struct vfs_io_uring_request *cur, + const char *location); + static struct tevent_req *vfs_io_uring_fsync_send(struct vfs_handle_struct *handle, TALLOC_CTX *mem_ctx, struct tevent_context *ev, @@ -474,6 +509,7 @@ static struct tevent_req *vfs_io_uring_fsync_send(struct vfs_handle_struct *hand } state->ur.config = config; state->ur.req = req; + state->ur.completion_fn = vfs_io_uring_fsync_completion; SMBPROFILE_BYTES_ASYNC_START(syscall_asys_fsync, profile_p, state->ur.profile_bytes, 0); @@ -496,6 +532,17 @@ static struct tevent_req *vfs_io_uring_fsync_send(struct vfs_handle_struct *hand return req; } +static void vfs_io_uring_fsync_completion(struct vfs_io_uring_request *cur, + const char *location) +{ + /* + * We rely on being inside the _send() function + * or tevent_req_defer_callback() being called + * already. + */ + _tevent_req_done(cur->req, location); +} + static int vfs_io_uring_fsync_recv(struct tevent_req *req, struct vfs_aio_state *vfs_aio_state) { -- 2.17.1 From 990193425321d033dfd4c8e1f408d38b3b5b7e5d Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 10:52:52 +0200 Subject: [PATCH 18/31] vfs_io_uring: move error handling out of vfs_io_uring_pread_recv() We should do that as early as possible and that's in vfs_io_uring_pread_completion(). BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index abdd4d16e9f8..0d8e18330092 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -297,6 +297,7 @@ static void vfs_io_uring_fd_handler(struct tevent_context *ev, struct vfs_io_uring_pread_state { struct vfs_io_uring_request ur; struct iovec iov; + size_t nread; }; static void vfs_io_uring_pread_completion(struct vfs_io_uring_request *cur, @@ -353,12 +354,23 @@ static struct tevent_req *vfs_io_uring_pread_send(struct vfs_handle_struct *hand static void vfs_io_uring_pread_completion(struct vfs_io_uring_request *cur, const char *location) { + struct vfs_io_uring_pread_state *state = tevent_req_data( + cur->req, struct vfs_io_uring_pread_state); + /* * We rely on being inside the _send() function * or tevent_req_defer_callback() being called * already. */ - _tevent_req_done(cur->req, location); + + if (cur->cqe.res < 0) { + int err = -cur->cqe.res; + _tevent_req_error(cur->req, err, location); + return; + } + + state->nread = state->ur.cqe.res; + tevent_req_done(cur->req); } static ssize_t vfs_io_uring_pread_recv(struct tevent_req *req, @@ -366,23 +378,19 @@ static ssize_t vfs_io_uring_pread_recv(struct tevent_req *req, { struct vfs_io_uring_pread_state *state = tevent_req_data( req, struct vfs_io_uring_pread_state); - int ret; + ssize_t ret; SMBPROFILE_BYTES_ASYNC_END(state->ur.profile_bytes); vfs_aio_state->duration = nsec_time_diff(&state->ur.end_time, &state->ur.start_time); if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) { + tevent_req_received(req); return -1; } - if (state->ur.cqe.res < 0) { - vfs_aio_state->error = -state->ur.cqe.res; - ret = -1; - } else { - vfs_aio_state->error = 0; - ret = state->ur.cqe.res; - } + vfs_aio_state->error = 0; + ret = state->nread; tevent_req_received(req); return ret; -- 2.17.1 From 249fb7d8487e00376ac8f2486d1293954941c584 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 10:52:52 +0200 Subject: [PATCH 19/31] vfs_io_uring: move error handling out of vfs_io_uring_pwrite_recv() We should do that as early as possible and that's in vfs_io_uring_pwrite_completion(). BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index 0d8e18330092..a8da341e7b7c 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -399,6 +399,7 @@ static ssize_t vfs_io_uring_pread_recv(struct tevent_req *req, struct vfs_io_uring_pwrite_state { struct vfs_io_uring_request ur; struct iovec iov; + size_t nwritten; }; static void vfs_io_uring_pwrite_completion(struct vfs_io_uring_request *cur, @@ -455,12 +456,23 @@ static struct tevent_req *vfs_io_uring_pwrite_send(struct vfs_handle_struct *han static void vfs_io_uring_pwrite_completion(struct vfs_io_uring_request *cur, const char *location) { + struct vfs_io_uring_pwrite_state *state = tevent_req_data( + cur->req, struct vfs_io_uring_pwrite_state); + /* * We rely on being inside the _send() function * or tevent_req_defer_callback() being called * already. */ - _tevent_req_done(cur->req, location); + + if (cur->cqe.res < 0) { + int err = -cur->cqe.res; + _tevent_req_error(cur->req, err, location); + return; + } + + state->nwritten = state->ur.cqe.res; + tevent_req_done(cur->req); } static ssize_t vfs_io_uring_pwrite_recv(struct tevent_req *req, @@ -468,23 +480,19 @@ static ssize_t vfs_io_uring_pwrite_recv(struct tevent_req *req, { struct vfs_io_uring_pwrite_state *state = tevent_req_data( req, struct vfs_io_uring_pwrite_state); - int ret; + ssize_t ret; SMBPROFILE_BYTES_ASYNC_END(state->ur.profile_bytes); vfs_aio_state->duration = nsec_time_diff(&state->ur.end_time, &state->ur.start_time); if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) { + tevent_req_received(req); return -1; } - if (state->ur.cqe.res < 0) { - vfs_aio_state->error = -state->ur.cqe.res; - ret = -1; - } else { - vfs_aio_state->error = 0; - ret = state->ur.cqe.res; - } + vfs_aio_state->error = 0; + ret = state->nwritten; tevent_req_received(req); return ret; -- 2.17.1 From 9a6ff1dd9bd137a20e0310702adde8ffee99e57a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 10:52:52 +0200 Subject: [PATCH 20/31] vfs_io_uring: move error handling out of vfs_io_uring_fsync_recv() We should do that as early as possible and that's in vfs_io_uring_fsync_completion(). BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index a8da341e7b7c..0f560c95b67d 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -556,7 +556,14 @@ static void vfs_io_uring_fsync_completion(struct vfs_io_uring_request *cur, * or tevent_req_defer_callback() being called * already. */ - _tevent_req_done(cur->req, location); + + if (cur->cqe.res < 0) { + int err = -cur->cqe.res; + _tevent_req_error(cur->req, err, location); + return; + } + + tevent_req_done(cur->req); } static int vfs_io_uring_fsync_recv(struct tevent_req *req, @@ -564,26 +571,20 @@ static int vfs_io_uring_fsync_recv(struct tevent_req *req, { struct vfs_io_uring_fsync_state *state = tevent_req_data( req, struct vfs_io_uring_fsync_state); - int ret; SMBPROFILE_BYTES_ASYNC_END(state->ur.profile_bytes); vfs_aio_state->duration = nsec_time_diff(&state->ur.end_time, &state->ur.start_time); if (tevent_req_is_unix_error(req, &vfs_aio_state->error)) { + tevent_req_received(req); return -1; } - if (state->ur.cqe.res < 0) { - vfs_aio_state->error = -state->ur.cqe.res; - ret = -1; - } else { - vfs_aio_state->error = 0; - ret = state->ur.cqe.res; - } + vfs_aio_state->error = 0; tevent_req_received(req); - return ret; + return 0; } static struct vfs_fn_pointers vfs_io_uring_fns = { -- 2.17.1 From d62457404f6358629f24db76d647674709974872 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 13:17:05 +0200 Subject: [PATCH 21/31] vfs_io_uring: make use of sys_valid_io_range() in vfs_io_uring_pread_send() This makes the follow up commits easier as we don't have to care about overflows. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index 0f560c95b67d..c7565b8c39de 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -25,6 +25,7 @@ #include "smbd/smbd.h" #include "smbd/globals.h" #include "lib/util/tevent_unix.h" +#include "lib/util/sys_rw.h" #include "smbprofile.h" #include @@ -313,6 +314,7 @@ static struct tevent_req *vfs_io_uring_pread_send(struct vfs_handle_struct *hand struct tevent_req *req = NULL; struct vfs_io_uring_pread_state *state = NULL; struct vfs_io_uring_config *config = NULL; + bool ok; SMB_VFS_HANDLE_GET_DATA(handle, config, struct vfs_io_uring_config, @@ -331,6 +333,12 @@ static struct tevent_req *vfs_io_uring_pread_send(struct vfs_handle_struct *hand state->ur.profile_bytes, n); SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->ur.profile_bytes); + ok = sys_valid_io_range(offset, n); + if (!ok) { + tevent_req_error(req, EINVAL); + return tevent_req_post(req, ev); + } + state->iov.iov_base = (void *)data; state->iov.iov_len = n; io_uring_prep_readv(&state->ur.sqe, -- 2.17.1 From bb20b2e9db88eeef3d9080e6dba39ff969364ff0 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 13:17:05 +0200 Subject: [PATCH 22/31] vfs_io_uring: make use of sys_valid_io_range() in vfs_io_uring_pwrite_send() This makes the follow up commits easier as we don't have to care about overflows. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index c7565b8c39de..ee23449c63c6 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -423,6 +423,7 @@ static struct tevent_req *vfs_io_uring_pwrite_send(struct vfs_handle_struct *han struct tevent_req *req = NULL; struct vfs_io_uring_pwrite_state *state = NULL; struct vfs_io_uring_config *config = NULL; + bool ok; SMB_VFS_HANDLE_GET_DATA(handle, config, struct vfs_io_uring_config, @@ -441,6 +442,12 @@ static struct tevent_req *vfs_io_uring_pwrite_send(struct vfs_handle_struct *han state->ur.profile_bytes, n); SMBPROFILE_BYTES_ASYNC_SET_IDLE(state->ur.profile_bytes); + ok = sys_valid_io_range(offset, n); + if (!ok) { + tevent_req_error(req, EINVAL); + return tevent_req_post(req, ev); + } + state->iov.iov_base = discard_const(data); state->iov.iov_len = n; io_uring_prep_writev(&state->ur.sqe, -- 2.17.1 From 515e38a802bb273319701759de502a247df4c497 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 21:29:53 +0200 Subject: [PATCH 23/31] vfs_io_uring: avoid stack recursion of vfs_io_uring_queue_run() Instead we remember if recursion was triggered and jump to the start of the function again from the end. This should make it safe to be called from the completion_fn(). BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index ee23449c63c6..df41c74a7953 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -34,6 +34,8 @@ struct vfs_io_uring_request; struct vfs_io_uring_config { struct io_uring uring; struct tevent_fd *fde; + bool busy; + bool need_retry; struct vfs_io_uring_request *queue; struct vfs_io_uring_request *pending; }; @@ -222,7 +224,7 @@ static int vfs_io_uring_connect(vfs_handle_struct *handle, const char *service, return 0; } -static void vfs_io_uring_queue_run(struct vfs_io_uring_config *config) +static void _vfs_io_uring_queue_run(struct vfs_io_uring_config *config) { struct vfs_io_uring_request *cur = NULL, *next = NULL; struct io_uring_cqe *cqe = NULL; @@ -280,6 +282,22 @@ static void vfs_io_uring_queue_run(struct vfs_io_uring_config *config) io_uring_cq_advance(&config->uring, nr); } +static void vfs_io_uring_queue_run(struct vfs_io_uring_config *config) +{ + if (config->busy) { + config->need_retry = true; + return; + } + config->busy = true; + + do { + config->need_retry = false; + _vfs_io_uring_queue_run(config); + } while (config->need_retry); + + config->busy = false; +} + static void vfs_io_uring_fd_handler(struct tevent_context *ev, struct tevent_fd *fde, uint16_t flags, -- 2.17.1 From 91f3176f4221afe9972fcf6a7039d721abdb50eb Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 11:17:51 +0200 Subject: [PATCH 24/31] vfs_io_uring: split out a vfs_io_uring_request_submit() function BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index df41c74a7953..7db32760d976 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -298,6 +298,17 @@ static void vfs_io_uring_queue_run(struct vfs_io_uring_config *config) config->busy = false; } +static void vfs_io_uring_request_submit(struct vfs_io_uring_request *cur) +{ + struct vfs_io_uring_config *config = cur->config; + + io_uring_sqe_set_data(&cur->sqe, cur); + DLIST_ADD_END(config->queue, cur); + cur->list_head = &config->queue; + + vfs_io_uring_queue_run(config); +} + static void vfs_io_uring_fd_handler(struct tevent_context *ev, struct tevent_fd *fde, uint16_t flags, @@ -363,11 +374,7 @@ static struct tevent_req *vfs_io_uring_pread_send(struct vfs_handle_struct *hand fsp->fh->fd, &state->iov, 1, offset); - io_uring_sqe_set_data(&state->ur.sqe, &state->ur); - DLIST_ADD_END(config->queue, &state->ur); - state->ur.list_head = &config->queue; - - vfs_io_uring_queue_run(config); + vfs_io_uring_request_submit(&state->ur); if (!tevent_req_is_in_progress(req)) { return tevent_req_post(req, ev); @@ -472,11 +479,7 @@ static struct tevent_req *vfs_io_uring_pwrite_send(struct vfs_handle_struct *han fsp->fh->fd, &state->iov, 1, offset); - io_uring_sqe_set_data(&state->ur.sqe, &state->ur); - DLIST_ADD_END(config->queue, &state->ur); - state->ur.list_head = &config->queue; - - vfs_io_uring_queue_run(config); + vfs_io_uring_request_submit(&state->ur); if (!tevent_req_is_in_progress(req)) { return tevent_req_post(req, ev); @@ -567,11 +570,7 @@ static struct tevent_req *vfs_io_uring_fsync_send(struct vfs_handle_struct *hand io_uring_prep_fsync(&state->ur.sqe, fsp->fh->fd, 0); /* fsync_flags */ - io_uring_sqe_set_data(&state->ur.sqe, &state->ur); - DLIST_ADD_END(config->queue, &state->ur); - state->ur.list_head = &config->queue; - - vfs_io_uring_queue_run(config); + vfs_io_uring_request_submit(&state->ur); if (!tevent_req_is_in_progress(req)) { return tevent_req_post(req, ev); -- 2.17.1 From 7f1ae17de0426421145e90c9b4e7ac9050e5b8ce Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 11:17:51 +0200 Subject: [PATCH 25/31] vfs_io_uring: split out a vfs_io_uring_pread_submit() function This can be reused when we add handling for short reads. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index 7db32760d976..1f724fbc588e 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -326,10 +326,13 @@ static void vfs_io_uring_fd_handler(struct tevent_context *ev, struct vfs_io_uring_pread_state { struct vfs_io_uring_request ur; + struct files_struct *fsp; + off_t offset; struct iovec iov; size_t nread; }; +static void vfs_io_uring_pread_submit(struct vfs_io_uring_pread_state *state); static void vfs_io_uring_pread_completion(struct vfs_io_uring_request *cur, const char *location); @@ -368,13 +371,11 @@ static struct tevent_req *vfs_io_uring_pread_send(struct vfs_handle_struct *hand return tevent_req_post(req, ev); } + state->fsp = fsp; + state->offset = offset; state->iov.iov_base = (void *)data; state->iov.iov_len = n; - io_uring_prep_readv(&state->ur.sqe, - fsp->fh->fd, - &state->iov, 1, - offset); - vfs_io_uring_request_submit(&state->ur); + vfs_io_uring_pread_submit(state); if (!tevent_req_is_in_progress(req)) { return tevent_req_post(req, ev); @@ -384,6 +385,15 @@ static struct tevent_req *vfs_io_uring_pread_send(struct vfs_handle_struct *hand return req; } +static void vfs_io_uring_pread_submit(struct vfs_io_uring_pread_state *state) +{ + io_uring_prep_readv(&state->ur.sqe, + state->fsp->fh->fd, + &state->iov, 1, + state->offset); + vfs_io_uring_request_submit(&state->ur); +} + static void vfs_io_uring_pread_completion(struct vfs_io_uring_request *cur, const char *location) { -- 2.17.1 From 563b53a5f69fb6a74f1422e68ef944675d181ff9 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 11:17:51 +0200 Subject: [PATCH 26/31] vfs_io_uring: split out a vfs_io_uring_pwrite_submit() function This can be reused when we add handling for short writes. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index 1f724fbc588e..416681333634 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -441,10 +441,13 @@ static ssize_t vfs_io_uring_pread_recv(struct tevent_req *req, struct vfs_io_uring_pwrite_state { struct vfs_io_uring_request ur; + struct files_struct *fsp; + off_t offset; struct iovec iov; size_t nwritten; }; +static void vfs_io_uring_pwrite_submit(struct vfs_io_uring_pwrite_state *state); static void vfs_io_uring_pwrite_completion(struct vfs_io_uring_request *cur, const char *location); @@ -483,13 +486,11 @@ static struct tevent_req *vfs_io_uring_pwrite_send(struct vfs_handle_struct *han return tevent_req_post(req, ev); } + state->fsp = fsp; + state->offset = offset; state->iov.iov_base = discard_const(data); state->iov.iov_len = n; - io_uring_prep_writev(&state->ur.sqe, - fsp->fh->fd, - &state->iov, 1, - offset); - vfs_io_uring_request_submit(&state->ur); + vfs_io_uring_pwrite_submit(state); if (!tevent_req_is_in_progress(req)) { return tevent_req_post(req, ev); @@ -499,6 +500,15 @@ static struct tevent_req *vfs_io_uring_pwrite_send(struct vfs_handle_struct *han return req; } +static void vfs_io_uring_pwrite_submit(struct vfs_io_uring_pwrite_state *state) +{ + io_uring_prep_writev(&state->ur.sqe, + state->fsp->fh->fd, + &state->iov, 1, + state->offset); + vfs_io_uring_request_submit(&state->ur); +} + static void vfs_io_uring_pwrite_completion(struct vfs_io_uring_request *cur, const char *location) { -- 2.17.1 From a160e544906edba617152c8a4807cb751fc06924 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 11:38:56 +0200 Subject: [PATCH 27/31] vfs_io_uring: protect vfs_io_uring_pread_completion() against invalid results We should never get back more than we asked for. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index 416681333634..a9b2db8d3cd3 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -26,6 +26,7 @@ #include "smbd/globals.h" #include "lib/util/tevent_unix.h" #include "lib/util/sys_rw.h" +#include "lib/util/iov_buf.h" #include "smbprofile.h" #include @@ -399,6 +400,9 @@ static void vfs_io_uring_pread_completion(struct vfs_io_uring_request *cur, { struct vfs_io_uring_pread_state *state = tevent_req_data( cur->req, struct vfs_io_uring_pread_state); + struct iovec *iov = &state->iov; + int num_iov = 1; + bool ok; /* * We rely on being inside the _send() function @@ -412,6 +416,16 @@ static void vfs_io_uring_pread_completion(struct vfs_io_uring_request *cur, return; } + ok = iov_advance(&iov, &num_iov, cur->cqe.res); + if (!ok) { + /* This is not expected! */ + DBG_ERR("iov_advance() failed cur->cqe.res=%d > iov_len=%d\n", + (int)cur->cqe.res, + (int)state->iov.iov_len); + tevent_req_error(cur->req, EIO); + return; + } + state->nread = state->ur.cqe.res; tevent_req_done(cur->req); } -- 2.17.1 From c7cb3ead876777bd4a13f28760e9fa1e5b6f6303 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 11:38:56 +0200 Subject: [PATCH 28/31] vfs_io_uring: protect vfs_io_uring_pwrite_completion() against invalid results We should never get more acked than we asked for. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index a9b2db8d3cd3..7994ef17d675 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -528,6 +528,9 @@ static void vfs_io_uring_pwrite_completion(struct vfs_io_uring_request *cur, { struct vfs_io_uring_pwrite_state *state = tevent_req_data( cur->req, struct vfs_io_uring_pwrite_state); + struct iovec *iov = &state->iov; + int num_iov = 1; + bool ok; /* * We rely on being inside the _send() function @@ -541,6 +544,16 @@ static void vfs_io_uring_pwrite_completion(struct vfs_io_uring_request *cur, return; } + ok = iov_advance(&iov, &num_iov, cur->cqe.res); + if (!ok) { + /* This is not expected! */ + DBG_ERR("iov_advance() failed cur->cqe.res=%d > iov_len=%d\n", + (int)cur->cqe.res, + (int)state->iov.iov_len); + tevent_req_error(cur->req, EIO); + return; + } + state->nwritten = state->ur.cqe.res; tevent_req_done(cur->req); } -- 2.17.1 From 88c6311070f574949f55a6e0c1d08706d45a50c6 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 11:38:56 +0200 Subject: [PATCH 29/31] vfs_io_uring: protect vfs_io_uring_fsync_completion() against invalid results We should never get back a value > 0. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index 7994ef17d675..2b5706665cf3 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -642,6 +642,13 @@ static void vfs_io_uring_fsync_completion(struct vfs_io_uring_request *cur, return; } + if (cur->cqe.res > 0) { + /* This is not expected! */ + DBG_ERR("got cur->cqe.res=%d\n", (int)cur->cqe.res); + tevent_req_error(cur->req, EIO); + return; + } + tevent_req_done(cur->req); } -- 2.17.1 From 78a22024ede0764baebe0a136f2f59c5959a7266 Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 13:30:17 +0200 Subject: [PATCH 30/31] vfs_io_uring: retry after a short read in vfs_io_uring_pread_completion() We need to be prepared for short reads from the kernel depending on the state of the page cache. Windows and Mac clients don't expect short reads for files, so we need to retry ourself. For the future we may be able to play with some io_uring flags in order to avoid the retries in userspace, but for now we just fix the data corruption bug... BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index 2b5706665cf3..965f8798ff2a 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -416,6 +416,14 @@ static void vfs_io_uring_pread_completion(struct vfs_io_uring_request *cur, return; } + if (cur->cqe.res == 0) { + /* + * We reached EOF, we're done + */ + tevent_req_done(cur->req); + return; + } + ok = iov_advance(&iov, &num_iov, cur->cqe.res); if (!ok) { /* This is not expected! */ @@ -426,8 +434,20 @@ static void vfs_io_uring_pread_completion(struct vfs_io_uring_request *cur, return; } - state->nread = state->ur.cqe.res; - tevent_req_done(cur->req); + /* sys_valid_io_range() already checked the boundaries */ + state->nread += state->ur.cqe.res; + if (num_iov == 0) { + /* We're done */ + tevent_req_done(cur->req); + return; + } + + /* + * sys_valid_io_range() already checked the boundaries + * now try to get the rest. + */ + state->offset += state->ur.cqe.res; + vfs_io_uring_pread_submit(state); } static ssize_t vfs_io_uring_pread_recv(struct tevent_req *req, -- 2.17.1 From ef782387a974f904fa1ca8181b5e07ec9e34a51a Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Fri, 8 May 2020 13:30:17 +0200 Subject: [PATCH 31/31] vfs_io_uring: retry after a short writes in vfs_io_uring_pwrite_completion() We need to be prepared for short writes from the kernel depending on the state of the page cache. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index 965f8798ff2a..19ffbde8e40a 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -564,6 +564,14 @@ static void vfs_io_uring_pwrite_completion(struct vfs_io_uring_request *cur, return; } + if (cur->cqe.res == 0) { + /* + * Ensure we can never spin. + */ + tevent_req_error(cur->req, ENOSPC); + return; + } + ok = iov_advance(&iov, &num_iov, cur->cqe.res); if (!ok) { /* This is not expected! */ @@ -574,8 +582,20 @@ static void vfs_io_uring_pwrite_completion(struct vfs_io_uring_request *cur, return; } - state->nwritten = state->ur.cqe.res; - tevent_req_done(cur->req); + /* sys_valid_io_range() already checked the boundaries */ + state->nwritten += state->ur.cqe.res; + if (num_iov == 0) { + /* We're done */ + tevent_req_done(cur->req); + return; + } + + /* + * sys_valid_io_range() already checked the boundaries + * now try to write the rest. + */ + state->offset += state->ur.cqe.res; + vfs_io_uring_pwrite_submit(state); } static ssize_t vfs_io_uring_pwrite_recv(struct tevent_req *req, -- 2.17.1