From 22c2a9cbdfa4ff37bd3caefacf05e94ce65b0865 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 00000000000..c6f11e03d20 --- /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 bc8898cec31..b0eea55d7f1 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.20.1 From fcbe7d229ace515b76b91ea96ec2187f2a8929a2 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 9a6cdcaa606..6fa7ca57365 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 ab456d87b22..70864cb2b74 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.20.1 From e29a94b2aac71976399cde5380508732a6dbae60 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 6fa7ca57365..bfeb2e6b466 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 70864cb2b74..1e0dd3730a6 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.20.1 From c6eec191f4b059d635ebc30f45374532e79d8b12 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 bfeb2e6b466..d74395fc409 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 1e0dd3730a6..b224ecb30ac 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.20.1 From 86f295ae8c7bec7b447c0851695273c57ffdc8c6 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 a4372bf1145..718f0941532 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.20.1 From edf820d18750864238d067e82ded28d6fe40a578 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 7ed2691cfbf..f89ce8537a0 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 6d58bdbbc97..079d414db05 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.20.1 From 3151c3fed479393fe6a03dd66dfbfb91af722127 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 98eb2843c07..ba72fb94e0f 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 9c75ceed6ae..5141da728a7 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.20.1 From 57d85e4b6d7e2b404c1c2de014d1a9dbcaba3d96 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 c6f11e03d20..ac5fe573239 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 c7f2020a9ea..b5fbc0ed5dc 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 f89ce8537a0..f141d673167 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 079d414db05..40c770da8bd 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 5141da728a7..f49b53f4b7c 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.20.1 From c999abfa9fa0e6b354e5d30c107155f7dd511e7e 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 a1fed5c0655..285b331ff9c 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.20.1 From 636f166e4ef18fb23f64deebe14d9373d2dd08a0 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 285b331ff9c..7c6f4b00fd0 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.20.1 From d0c0b46463a98062919656454af67bb2bb37c9a0 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 b5fbc0ed5dc..f76d7c7b918 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.20.1 From 6c14eea20135a2945880b86876f094d1c23a478e 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 f76d7c7b918..ff46966536b 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.20.1 From b8098a590d706d756a6e8728d1c3605bc95b4dc8 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 ff46966536b..bce50b990c1 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.20.1 From a9ae9c0ce65cbbd7d2966da44bfa6e0aae5432d9 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 bce50b990c1..386a34f81d1 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.20.1 From cb43186c0519bceaed7d41fc87a00f25462df2d0 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 378e48d112f..b409d075337 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.20.1 From a12e488fab20a2245dae4012a3808e0caaf7b60e 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 b409d075337..988b309da52 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.20.1 From 4a9a0b679f402ecbeacda3c6d0993a82045a84aa 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 988b309da52..abdd4d16e9f 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.20.1 From 642e7b1675ae17d1ce7f2ff89e6211dee32e86ce 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 abdd4d16e9f..0d8e1833009 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.20.1 From 411493f64cb12718a8acb499e461e370dd723ecc 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 0d8e1833009..a8da341e7b7 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.20.1 From 9bd999aaebd05ceec2b264b0b2fb3c66f29baa95 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 a8da341e7b7..0f560c95b67 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.20.1 From f41be7e706fae7bb165b93c3d2ebe62df058f430 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 0f560c95b67..c7565b8c39d 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.20.1 From e229e6bdebf23412b4c4641e4bb8898be68a0861 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 c7565b8c39d..ee23449c63c 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.20.1 From 2f026af643b87c9513f74f7dfbaf37dd373ac96d 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(). This is hideously complex stuff, so document the hell out of it. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14361 Signed-off-by: Stefan Metzmacher --- source3/modules/vfs_io_uring.c | 93 +++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/source3/modules/vfs_io_uring.c b/source3/modules/vfs_io_uring.c index ee23449c63c..f94453d9995 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -34,6 +34,10 @@ struct vfs_io_uring_request; struct vfs_io_uring_config { struct io_uring uring; struct tevent_fd *fde; + /* recursion guard. See comment above vfs_io_uring_queue_run() */ + bool busy; + /* recursion guard. See comment above vfs_io_uring_queue_run() */ + bool need_retry; struct vfs_io_uring_request *queue; struct vfs_io_uring_request *pending; }; @@ -222,7 +226,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 +284,93 @@ static void vfs_io_uring_queue_run(struct vfs_io_uring_config *config) io_uring_cq_advance(&config->uring, nr); } +/* + * Wrapper function to prevent recursion which could happen + * if we called _vfs_io_uring_queue_run() directly without + * recursion checks. + * + * Looking at the pread call, we can have: + * + * vfs_io_uring_pread_send() + * ->vfs_io_uring_pread_submit() <----------------------------------- + * ->vfs_io_uring_request_submit() | + * ->vfs_io_uring_queue_run() | + * ->_vfs_io_uring_queue_run() | + * | + * But inside _vfs_io_uring_queue_run() looks like: | + * | + * _vfs_io_uring_queue_run() { | + * if (THIS_IO_COMPLETED) { | + * ->vfs_io_uring_finish_req() | + * ->cur->completion_fn() | + * } | + * } | + * | + * cur->completion_fn() for pread is set to vfs_io_uring_pread_completion() | + * | + * vfs_io_uring_pread_completion() { | + * if (READ_TERMINATED) { | + * -> tevent_req_done() - We're done, go back up the stack. | + * return; | + * } | + * | + * We have a short read - adjust the io vectors | + * | + * ->vfs_io_uring_pread_submit() --------------------------------------- + * } + * + * So before calling _vfs_io_uring_queue_run() we backet it with setting + * a flag config->busy, and unset it once _vfs_io_uring_queue_run() finally + * exits the retry loop. + * + * If we end up back into vfs_io_uring_queue_run() we notice we've done so + * as config->busy is set and don't recurse into _vfs_io_uring_queue_run(). + * + * We set the second flag config->need_retry that tells us to loop in the + * vfs_io_uring_queue_run() call above us in the stack and return. + * + * When the outer call to _vfs_io_uring_queue_run() returns we are in + * a loop checking if config->need_retry was set. That happens if + * the short read case occurs and _vfs_io_uring_queue_run() ended up + * recursing into vfs_io_uring_queue_run(). + * + * Once vfs_io_uring_pread_completion() finishes without a short + * read (the READ_TERMINATED case, tevent_req_done() is called) + * then config->need_retry is left as false, we exit the loop, + * set config->busy to false so the next top level call into + * vfs_io_uring_queue_run() won't think it's a recursed call + * and return. + * + */ + +static void vfs_io_uring_queue_run(struct vfs_io_uring_config *config) +{ + if (config->busy) { + /* + * We've recursed due to short read/write. + * Set need_retry to ensure we retry the + * io_uring_submit(). + */ + config->need_retry = true; + return; + } + + /* + * Bracket the loop calling _vfs_io_uring_queue_run() + * with busy = true / busy = false. + * so we can detect recursion above. + */ + + 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.20.1 From 0d455e593e3c0155f8fd94beed7fcdbb3228b4db 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 f94453d9995..1d48bd192fe 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -371,6 +371,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, @@ -436,11 +447,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); @@ -545,11 +552,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); @@ -640,11 +643,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.20.1 From af0382c32e10de54e9bb2aa50acb79bae91ba62b 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 1d48bd192fe..19e268e63db 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -399,10 +399,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); @@ -441,13 +444,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); @@ -457,6 +458,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.20.1 From 3a7d0bbdf6a9dfeb35b317b4ff622395b1ef40fa 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 19e268e63db..3e004f48aa0 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -514,10 +514,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); @@ -556,13 +559,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); @@ -572,6 +573,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.20.1 From fca0016d9dac7cb7e4d15eec094d90689a90cbf4 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 3e004f48aa0..46fab116e9d 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 @@ -472,6 +473,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 @@ -485,6 +489,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.20.1 From 417d63e34033b7ea39c8797836d309d042e07026 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 46fab116e9d..0ea785aae85 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -601,6 +601,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 @@ -614,6 +617,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.20.1 From 64be14ccbf14880b519b4f85eb86a82be8bdac77 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 0ea785aae85..0b1583f962a 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -715,6 +715,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.20.1 From eb7c55b7a7446fcf945bea1d7925421483d06362 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 0b1583f962a..f16c9ae56d3 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -489,6 +489,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! */ @@ -499,8 +507,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.20.1 From 53a945564b418a814094b9b0ad43e084ffe05859 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 f16c9ae56d3..4625e16c37e 100644 --- a/source3/modules/vfs_io_uring.c +++ b/source3/modules/vfs_io_uring.c @@ -637,6 +637,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! */ @@ -647,8 +655,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.20.1