From: Greg Kroah-Hartman Date: Thu, 30 Nov 2023 13:29:19 +0000 (+0000) Subject: 6.6-stable patches X-Git-Tag: v5.15.141~32 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3a03e71e6844699118b45857ad09285b1fe27d51;p=thirdparty%2Fkernel%2Fstable-queue.git 6.6-stable patches 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 --- 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 index 00000000000..2b16a95983f --- /dev/null +++ b/queue-6.6/nfsd-fix-checksum-mismatches-in-the-duplicate-reply-cache.patch @@ -0,0 +1,190 @@ +From stable+bounces-3081-greg=kroah.com@vger.kernel.org Tue Nov 28 21:58:44 2023 +From: Chuck Lever +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 + +[ 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 +Tested-by: Jeff Layton +Signed-off-by: Chuck Lever +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..77a9589ae5e --- /dev/null +++ b/queue-6.6/nfsd-fix-start-of-nfs-reply-pointer-passed-to-nfsd_cache_update.patch @@ -0,0 +1,69 @@ +From stable+bounces-3080-greg=kroah.com@vger.kernel.org Tue Nov 28 21:58:43 2023 +From: Chuck Lever +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 + +[ 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 +Tested-by: Jeff Layton +Signed-off-by: Chuck Lever +Signed-off-by: Greg Kroah-Hartman +--- + 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; + diff --git a/queue-6.6/series b/queue-6.6/series index 2b902d41b2c..3c78523897a 100644 --- a/queue-6.6/series +++ b/queue-6.6/series @@ -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