]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.6-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 30 Nov 2023 13:29:19 +0000 (13:29 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 30 Nov 2023 13:29:19 +0000 (13:29 +0000)
added patches:
nfsd-fix-checksum-mismatches-in-the-duplicate-reply-cache.patch
nfsd-fix-start-of-nfs-reply-pointer-passed-to-nfsd_cache_update.patch

queue-6.6/nfsd-fix-checksum-mismatches-in-the-duplicate-reply-cache.patch [new file with mode: 0644]
queue-6.6/nfsd-fix-start-of-nfs-reply-pointer-passed-to-nfsd_cache_update.patch [new file with mode: 0644]
queue-6.6/series

diff --git a/queue-6.6/nfsd-fix-checksum-mismatches-in-the-duplicate-reply-cache.patch b/queue-6.6/nfsd-fix-checksum-mismatches-in-the-duplicate-reply-cache.patch
new file mode 100644 (file)
index 0000000..2b16a95
--- /dev/null
@@ -0,0 +1,190 @@
+From stable+bounces-3081-greg=kroah.com@vger.kernel.org Tue Nov 28 21:58:44 2023
+From: Chuck Lever <cel@kernel.org>
+Date: Tue, 28 Nov 2023 16:58:40 -0500
+Subject: NFSD: Fix checksum mismatches in the duplicate reply cache
+To: stable@vger.kernel.org
+Cc: linux-nfs@vger.kernel.org
+Message-ID: <170120872070.1376.18030739131380868662.stgit@klimt.1015granger.net>
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit bf51c52a1f3c238d72c64e14d5e7702d3a245b82 ]
+
+nfsd_cache_csum() currently assumes that the server's RPC layer has
+been advancing rq_arg.head[0].iov_base as it decodes an incoming
+request, because that's the way it used to work. On entry, it
+expects that buf->head[0].iov_base points to the start of the NFS
+header, and excludes the already-decoded RPC header.
+
+These days however, head[0].iov_base now points to the start of the
+RPC header during all processing. It no longer points at the NFS
+Call header when execution arrives at nfsd_cache_csum().
+
+In a retransmitted RPC the XID and the NFS header are supposed to
+be the same as the original message, but the contents of the
+retransmitted RPC header can be different. For example, for krb5,
+the GSS sequence number will be different between the two. Thus if
+the RPC header is always included in the DRC checksum computation,
+the checksum of the retransmitted message might not match the
+checksum of the original message, even though the NFS part of these
+messages is identical.
+
+The result is that, even if a matching XID is found in the DRC,
+the checksum mismatch causes the server to execute the
+retransmitted RPC transaction again.
+
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Tested-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfsd/cache.h    |    4 +--
+ fs/nfsd/nfscache.c |   64 +++++++++++++++++++++++++++++++++++------------------
+ fs/nfsd/nfssvc.c   |   10 +++++++-
+ 3 files changed, 54 insertions(+), 24 deletions(-)
+
+--- a/fs/nfsd/cache.h
++++ b/fs/nfsd/cache.h
+@@ -84,8 +84,8 @@ int  nfsd_net_reply_cache_init(struct nfs
+ void  nfsd_net_reply_cache_destroy(struct nfsd_net *nn);
+ int   nfsd_reply_cache_init(struct nfsd_net *);
+ void  nfsd_reply_cache_shutdown(struct nfsd_net *);
+-int   nfsd_cache_lookup(struct svc_rqst *rqstp,
+-                        struct nfsd_cacherep **cacherep);
++int   nfsd_cache_lookup(struct svc_rqst *rqstp, unsigned int start,
++                        unsigned int len, struct nfsd_cacherep **cacherep);
+ void  nfsd_cache_update(struct svc_rqst *rqstp, struct nfsd_cacherep *rp,
+                         int cachetype, __be32 *statp);
+ int   nfsd_reply_cache_stats_show(struct seq_file *m, void *v);
+--- a/fs/nfsd/nfscache.c
++++ b/fs/nfsd/nfscache.c
+@@ -368,33 +368,52 @@ nfsd_reply_cache_scan(struct shrinker *s
+       return freed;
+ }
+-/*
+- * Walk an xdr_buf and get a CRC for at most the first RC_CSUMLEN bytes
++/**
++ * nfsd_cache_csum - Checksum incoming NFS Call arguments
++ * @buf: buffer containing a whole RPC Call message
++ * @start: starting byte of the NFS Call header
++ * @remaining: size of the NFS Call header, in bytes
++ *
++ * Compute a weak checksum of the leading bytes of an NFS procedure
++ * call header to help verify that a retransmitted Call matches an
++ * entry in the duplicate reply cache.
++ *
++ * To avoid assumptions about how the RPC message is laid out in
++ * @buf and what else it might contain (eg, a GSS MIC suffix), the
++ * caller passes us the exact location and length of the NFS Call
++ * header.
++ *
++ * Returns a 32-bit checksum value, as defined in RFC 793.
+  */
+-static __wsum
+-nfsd_cache_csum(struct svc_rqst *rqstp)
++static __wsum nfsd_cache_csum(struct xdr_buf *buf, unsigned int start,
++                            unsigned int remaining)
+ {
++      unsigned int base, len;
++      struct xdr_buf subbuf;
++      __wsum csum = 0;
++      void *p;
+       int idx;
+-      unsigned int base;
+-      __wsum csum;
+-      struct xdr_buf *buf = &rqstp->rq_arg;
+-      const unsigned char *p = buf->head[0].iov_base;
+-      size_t csum_len = min_t(size_t, buf->head[0].iov_len + buf->page_len,
+-                              RC_CSUMLEN);
+-      size_t len = min(buf->head[0].iov_len, csum_len);
++
++      if (remaining > RC_CSUMLEN)
++              remaining = RC_CSUMLEN;
++      if (xdr_buf_subsegment(buf, &subbuf, start, remaining))
++              return csum;
+       /* rq_arg.head first */
+-      csum = csum_partial(p, len, 0);
+-      csum_len -= len;
++      if (subbuf.head[0].iov_len) {
++              len = min_t(unsigned int, subbuf.head[0].iov_len, remaining);
++              csum = csum_partial(subbuf.head[0].iov_base, len, csum);
++              remaining -= len;
++      }
+       /* Continue into page array */
+-      idx = buf->page_base / PAGE_SIZE;
+-      base = buf->page_base & ~PAGE_MASK;
+-      while (csum_len) {
+-              p = page_address(buf->pages[idx]) + base;
+-              len = min_t(size_t, PAGE_SIZE - base, csum_len);
++      idx = subbuf.page_base / PAGE_SIZE;
++      base = subbuf.page_base & ~PAGE_MASK;
++      while (remaining) {
++              p = page_address(subbuf.pages[idx]) + base;
++              len = min_t(unsigned int, PAGE_SIZE - base, remaining);
+               csum = csum_partial(p, len, csum);
+-              csum_len -= len;
++              remaining -= len;
+               base = 0;
+               ++idx;
+       }
+@@ -465,6 +484,8 @@ out:
+ /**
+  * nfsd_cache_lookup - Find an entry in the duplicate reply cache
+  * @rqstp: Incoming Call to find
++ * @start: starting byte in @rqstp->rq_arg of the NFS Call header
++ * @len: size of the NFS Call header, in bytes
+  * @cacherep: OUT: DRC entry for this request
+  *
+  * Try to find an entry matching the current call in the cache. When none
+@@ -478,7 +499,8 @@ out:
+  *   %RC_REPLY: Reply from cache
+  *   %RC_DROPIT: Do not process the request further
+  */
+-int nfsd_cache_lookup(struct svc_rqst *rqstp, struct nfsd_cacherep **cacherep)
++int nfsd_cache_lookup(struct svc_rqst *rqstp, unsigned int start,
++                    unsigned int len, struct nfsd_cacherep **cacherep)
+ {
+       struct nfsd_net         *nn;
+       struct nfsd_cacherep    *rp, *found;
+@@ -494,7 +516,7 @@ int nfsd_cache_lookup(struct svc_rqst *r
+               goto out;
+       }
+-      csum = nfsd_cache_csum(rqstp);
++      csum = nfsd_cache_csum(&rqstp->rq_arg, start, len);
+       /*
+        * Since the common case is a cache miss followed by an insert,
+--- a/fs/nfsd/nfssvc.c
++++ b/fs/nfsd/nfssvc.c
+@@ -988,6 +988,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp
+       const struct svc_procedure *proc = rqstp->rq_procinfo;
+       __be32 *statp = rqstp->rq_accept_statp;
+       struct nfsd_cacherep *rp;
++      unsigned int start, len;
+       __be32 *nfs_reply;
+       /*
+@@ -996,11 +997,18 @@ int nfsd_dispatch(struct svc_rqst *rqstp
+        */
+       rqstp->rq_cachetype = proc->pc_cachetype;
++      /*
++       * ->pc_decode advances the argument stream past the NFS
++       * Call header, so grab the header's starting location and
++       * size now for the call to nfsd_cache_lookup().
++       */
++      start = xdr_stream_pos(&rqstp->rq_arg_stream);
++      len = xdr_stream_remaining(&rqstp->rq_arg_stream);
+       if (!proc->pc_decode(rqstp, &rqstp->rq_arg_stream))
+               goto out_decode_err;
+       rp = NULL;
+-      switch (nfsd_cache_lookup(rqstp, &rp)) {
++      switch (nfsd_cache_lookup(rqstp, start, len, &rp)) {
+       case RC_DOIT:
+               break;
+       case RC_REPLY:
diff --git a/queue-6.6/nfsd-fix-start-of-nfs-reply-pointer-passed-to-nfsd_cache_update.patch b/queue-6.6/nfsd-fix-start-of-nfs-reply-pointer-passed-to-nfsd_cache_update.patch
new file mode 100644 (file)
index 0000000..77a9589
--- /dev/null
@@ -0,0 +1,69 @@
+From stable+bounces-3080-greg=kroah.com@vger.kernel.org Tue Nov 28 21:58:43 2023
+From: Chuck Lever <cel@kernel.org>
+Date: Tue, 28 Nov 2023 16:58:34 -0500
+Subject: NFSD: Fix "start of NFS reply" pointer passed to nfsd_cache_update()
+To: stable@vger.kernel.org
+Cc: linux-nfs@vger.kernel.org
+Message-ID: <170120871426.1376.10151990384789497254.stgit@klimt.1015granger.net>
+
+From: Chuck Lever <chuck.lever@oracle.com>
+
+[ Upstream commit 1caf5f61dd8430ae5a0b4538afe4953ce7517cbb ]
+
+The "statp + 1" pointer that is passed to nfsd_cache_update() is
+supposed to point to the start of the egress NFS Reply header. In
+fact, it does point there for AUTH_SYS and RPCSEC_GSS_KRB5 requests.
+
+But both krb5i and krb5p add fields between the RPC header's
+accept_stat field and the start of the NFS Reply header. In those
+cases, "statp + 1" points at the extra fields instead of the Reply.
+The result is that nfsd_cache_update() caches what looks to the
+client like garbage.
+
+A connection break can occur for a number of reasons, but the most
+common reason when using krb5i/p is a GSS sequence number window
+underrun. When an underrun is detected, the server is obliged to
+drop the RPC and the connection to force a retransmit with a fresh
+GSS sequence number. The client presents the same XID, it hits in
+the server's DRC, and the server returns the garbage cache entry.
+
+The "statp + 1" argument has been used since the oldest changeset
+in the kernel history repo, so it has been in nfsd_dispatch()
+literally since before history began. The problem arose only when
+the server-side GSS implementation was added twenty years ago.
+
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Tested-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/nfsd/nfssvc.c |    4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/fs/nfsd/nfssvc.c
++++ b/fs/nfsd/nfssvc.c
+@@ -988,6 +988,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp
+       const struct svc_procedure *proc = rqstp->rq_procinfo;
+       __be32 *statp = rqstp->rq_accept_statp;
+       struct nfsd_cacherep *rp;
++      __be32 *nfs_reply;
+       /*
+        * Give the xdr decoder a chance to change this if it wants
+@@ -1008,6 +1009,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp
+               goto out_dropit;
+       }
++      nfs_reply = xdr_inline_decode(&rqstp->rq_res_stream, 0);
+       *statp = proc->pc_func(rqstp);
+       if (test_bit(RQ_DROPME, &rqstp->rq_flags))
+               goto out_update_drop;
+@@ -1015,7 +1017,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp
+       if (!proc->pc_encode(rqstp, &rqstp->rq_res_stream))
+               goto out_encode_err;
+-      nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, statp + 1);
++      nfsd_cache_update(rqstp, rp, rqstp->rq_cachetype, nfs_reply);
+ out_cached_reply:
+       return 1;
index 2b902d41b2c428ab0da81d16db792ccbda350f20..3c78523897a528f390df4256cb268c8edec0d2c5 100644 (file)
@@ -1,3 +1,5 @@
+nfsd-fix-start-of-nfs-reply-pointer-passed-to-nfsd_cache_update.patch
+nfsd-fix-checksum-mismatches-in-the-duplicate-reply-cache.patch
 irqchip-gic-v3-its-flush-its-tables-correctly-in-non.patch
 hv-hv_kvp_daemon-some-small-fixes-for-handling-nm-ke.patch
 sched-eevdf-fix-vruntime-adjustment-on-reweight.patch