[SRU][N:linux-azure][PATCH 2/2] UBUNTU: SAUCE: cifs: Fix uncached read into ITER_KVEC iterator
John Cabaj
john.cabaj at canonical.com
Wed Nov 19 18:05:01 UTC 2025
From: David Howells <dhowells at redhat.com>
BugLink: https://bugs.launchpad.net/bugs/2131951
If a cifs share is mounted cache=none, internal reads (such as by exec)
will pass a KVEC iterator down from __cifs_readv() to
cifs_send_async_read() which will then call cifs_limit_bvec_subset() upon
it to limit the number of contiguous elements for RDMA purposes. This
doesn't work on non-BVEC iterators, however.
Fix this by extracting a KVEC iterator into a BVEC iterator in
__cifs_readv() (it would be dup'd anyway it async).
This caused the following warning:
WARNING: CPU: 0 PID: 6290 at fs/smb/client/file.c:3549 cifs_limit_bvec_subset+0xe/0xc0
...
Call Trace:
<TASK>
cifs_send_async_read+0x146/0x2e0
__cifs_readv+0x207/0x2d0
__kernel_read+0xf6/0x160
search_binary_handler+0x49/0x210
exec_binprm+0x4a/0x140
bprm_execve.part.0+0xe4/0x170
do_execveat_common.isra.0+0x196/0x1c0
do_execve+0x1f/0x30
Fixes: d08089f649a0 ("cifs: Change the I/O paths to use an iterator rather than a page list")
Acked-by: Bharath SM <bharathsm at microsoft.com>
Tested-by: Bharath SM <bharathsm at microsoft.com>
Signed-off-by: David Howells <dhowells at redhat.com>
cc: stable at kernel.org # v6.6~v6.9
Link: https://lore.kernel.org/stable/CAH2r5mus4Cp0jH4y=_5LDCiDftVOEZwVaJb1ZOq3Uze2G4Evpg@mail.gmail.com/
Signed-off-by: John Cabaj <john.cabaj at canonical.com>
---
fs/smb/client/file.c | 97 ++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 94 insertions(+), 3 deletions(-)
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c
index 1f50e10a14bf..6b12f55024b6 100644
--- a/fs/smb/client/file.c
+++ b/fs/smb/client/file.c
@@ -37,6 +37,81 @@
#include "cifs_ioctl.h"
#include "cached_dir.h"
+/*
+ * Allocate a bio_vec array and extract up to sg_max pages from a KVEC-type
+ * iterator and add them to the array. This can deal with vmalloc'd buffers as
+ * well as kmalloc'd or static buffers. The pages are not pinned.
+ */
+static ssize_t extract_kvec_to_bvec(struct iov_iter *iter, ssize_t maxsize,
+ unsigned int bc_max,
+ struct bio_vec **_bv, unsigned int *_bc)
+{
+ const struct kvec *kv = iter->kvec;
+ struct bio_vec *bv;
+ unsigned long start = iter->iov_offset;
+ unsigned int i, bc = 0;
+ ssize_t ret = 0;
+
+ bc_max = iov_iter_npages(iter, bc_max);
+ if (bc_max == 0) {
+ *_bv = NULL;
+ *_bc = 0;
+ return 0;
+ }
+
+ bv = kvmalloc(array_size(bc_max, sizeof(*bv)), GFP_NOFS);
+ if (!bv) {
+ *_bv = NULL;
+ *_bc = 0;
+ return -ENOMEM;
+ }
+ *_bv = bv;
+
+ for (i = 0; i < iter->nr_segs; i++) {
+ struct page *page;
+ unsigned long kaddr;
+ size_t off, len, seg;
+
+ len = kv[i].iov_len;
+ if (start >= len) {
+ start -= len;
+ continue;
+ }
+
+ kaddr = (unsigned long)kv[i].iov_base + start;
+ off = kaddr & ~PAGE_MASK;
+ len = min_t(size_t, maxsize, len - start);
+ kaddr &= PAGE_MASK;
+
+ maxsize -= len;
+ ret += len;
+ do {
+ seg = umin(len, PAGE_SIZE - off);
+ if (is_vmalloc_or_module_addr((void *)kaddr))
+ page = vmalloc_to_page((void *)kaddr);
+ else
+ page = virt_to_page((void *)kaddr);
+
+ bvec_set_page(bv, page, len, off);
+ bv++;
+ bc++;
+
+ len -= seg;
+ kaddr += PAGE_SIZE;
+ off = 0;
+ } while (len > 0 && bc < bc_max);
+
+ if (maxsize <= 0 || bc >= bc_max)
+ break;
+ start = 0;
+ }
+
+ if (ret > 0)
+ iov_iter_advance(iter, ret);
+ *_bc = bc;
+ return ret;
+}
+
/*
* Remove the dirty flags from a span of pages.
*/
@@ -4289,11 +4364,27 @@ static ssize_t __cifs_readv(
ctx->bv = (void *)ctx->iter.bvec;
ctx->bv_need_unpin = iov_iter_extract_will_pin(to);
ctx->should_dirty = true;
- } else if ((iov_iter_is_bvec(to) || iov_iter_is_kvec(to)) &&
- !is_sync_kiocb(iocb)) {
+ } else if (iov_iter_is_kvec(to)) {
+ /*
+ * Extract a KVEC-type iterator into a BVEC-type iterator. We
+ * assume that the storage will be retained by the caller; in
+ * any case, we may or may not be able to pin the pages, so we
+ * don't try.
+ */
+ unsigned int bc;
+
+ rc = extract_kvec_to_bvec(to, iov_iter_count(to), INT_MAX,
+ &ctx->bv, &bc);
+ if (rc < 0) {
+ kref_put(&ctx->refcount, cifs_aio_ctx_release);
+ return rc;
+ }
+
+ iov_iter_bvec(&ctx->iter, ITER_DEST, ctx->bv, bc, rc);
+ } else if (iov_iter_is_bvec(to) && !is_sync_kiocb(iocb)) {
/*
* If the op is asynchronous, we need to copy the list attached
- * to a BVEC/KVEC-type iterator, but we assume that the storage
+ * to a BVEC-type iterator, but we assume that the storage
* will be retained by the caller; in any case, we may or may
* not be able to pin the pages, so we don't try.
*/
--
2.43.0
More information about the kernel-team
mailing list