From 279997974099abbc4a1ac28ed243f62bc039cf65 Mon Sep 17 00:00:00 2001 From: Yannick Cote Date: Mon, 12 Dec 2022 19:34:33 -0500 Subject: [KPATCH CVE-2022-43945] kpatch fixes for CVE-2022-43945 Kernels: 5.14.0-162.6.1.el9_1 Kpatch-MR: https://gitlab.com/redhat/prdsc/rhel/src/kpatch/rhel-9/-/merge_requests/14 Approved-by: Joe Lawrence (@joe.lawrence) Changes since last build: [x86_64]: ax88179_178a.o: changed function: ax88179_rx_fixup callback_xdr.o: changed function: nfs_callback_dispatch intel_gt.o: changed function: intel_gt_invalidate_tlbs nfs3proc.o: changed function: nfsd3_init_dirlist_pages nfs3proc.o: changed function: nfsd3_proc_read nfsproc.o: changed function: nfsd_proc_read nfsproc.o: changed function: nfsd_proc_readdir nfssvc.o: changed function: nfsd_dispatch pipe.o: changed function: pipe_resize_ring svc.o: changed function: nlmsvc_dispatch [ppc64le]: ax88179_178a.o: changed function: ax88179_rx_fixup callback_xdr.o: changed function: nfs_callback_dispatch nfs3proc.o: changed function: nfsd3_init_dirlist_pages nfs3proc.o: changed function: nfsd3_proc_read nfsproc.o: changed function: nfsd_proc_read nfsproc.o: changed function: nfsd_proc_readdir nfssvc.o: changed function: nfsd_dispatch pipe.o: changed function: pipe_resize_ring svc.o: changed function: nlmsvc_dispatch --------------------------- Modifications: - Take out modifications to header routines and roll new kpatch versions of them to be included at all call sites. - Wrapped new routines: svcxdr_init_decode(), svcxdr_init_encode() commit a7e335af244128a81a936cfc8d23509e29fc0b72 Author: Scott Mayhew Date: Thu Nov 10 13:41:53 2022 -0500 SUNRPC: Fix svcxdr_init_decode's end-of-buffer calculation Bugzilla: https://bugzilla.redhat.com/2141769 CVE: CVE-2022-43945 Y-Commit: 63e941917cd28dff8b33ce5c8658aac3b7dc5c75 O-Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2141770 O-CVE: CVE-2022-43945 commit 90bfc37b5ab91c1a6165e3e5cfc49bf04571b762 Author: Chuck Lever Date: Thu Sep 1 15:09:53 2022 -0400 SUNRPC: Fix svcxdr_init_decode's end-of-buffer calculation Ensure that stream-based argument decoding can't go past the actual end of the receive buffer. xdr_init_decode's calculation of the value of xdr->end over-estimates the end of the buffer because the Linux kernel RPC server code does not remove the size of the RPC header from rqstp->rq_arg before calling the upper layer's dispatcher. The server-side still uses the svc_getnl() macros to decode the RPC call header. These macros reduce the length of the head iov but do not update the total length of the message in the buffer (buf->len). A proper fix for this would be to replace the use of svc_getnl() and friends in the RPC header decoder, but that would be a large and invasive change that would be difficult to backport. Fixes: 5191955d6fc6 ("SUNRPC: Prepare for xdr_stream-style decoding on the server-side") Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Scott Mayhew Signed-off-by: Patrick Talbert commit 8d718c29ff4601ff5ca279421f7e2187a38d856f Author: Scott Mayhew Date: Thu Nov 10 13:41:52 2022 -0500 SUNRPC: Fix svcxdr_init_encode's buflen calculation Bugzilla: https://bugzilla.redhat.com/2141769 CVE: CVE-2022-43945 Y-Commit: 02ef2175406bbe8753f6ff08d3dae4d927f88c69 O-Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2141770 O-CVE: CVE-2022-43945 commit 1242a87da0d8cd2a428e96ca68e7ea899b0f4624 Author: Chuck Lever Date: Thu Sep 1 15:09:59 2022 -0400 SUNRPC: Fix svcxdr_init_encode's buflen calculation Commit 2825a7f90753 ("nfsd4: allow encoding across page boundaries") added an explicit computation of the remaining length in the rq_res XDR buffer. The computation appears to suffer from an "off-by-one" bug. Because buflen is too large by one page, XDR encoding can run off the end of the send buffer by eventually trying to use the struct page address in rq_page_end, which always contains NULL. Fixes: bddfdbcddbe2 ("NFSD: Extract the svcxdr_init_encode() helper") Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Scott Mayhew Signed-off-by: Patrick Talbert commit b4648565ad97ee05956cabbb56d89f29932e7a02 Author: Scott Mayhew Date: Thu Nov 10 13:41:52 2022 -0500 NFSD: Protect against send buffer overflow in NFSv2 READDIR Bugzilla: https://bugzilla.redhat.com/2141769 CVE: CVE-2022-43945 Y-Commit: 13ed52506cc7713e48ae64bd11ba867295aed137 O-Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2141770 O-CVE: CVE-2022-43945 commit 00b4492686e0497fdb924a9d4c8f6f99377e176c Author: Chuck Lever Date: Thu Sep 1 15:10:05 2022 -0400 NFSD: Protect against send buffer overflow in NFSv2 READDIR Restore the previous limit on the @count argument to prevent a buffer overflow attack. Fixes: 53b1119a6e50 ("NFSD: Fix READDIR buffer overflow") Signed-off-by: Chuck Lever Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Scott Mayhew Signed-off-by: Patrick Talbert commit 08fca21e5fe53bd2e0ef7ddbe9cb302ccedeba45 Author: Scott Mayhew Date: Thu Nov 10 13:41:52 2022 -0500 NFSD: Protect against send buffer overflow in NFSv3 READDIR Bugzilla: https://bugzilla.redhat.com/2141769 CVE: CVE-2022-43945 Y-Commit: 2a688c7c62dce0bc5e4377d82d830fe2955503cc O-Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2141770 O-CVE: CVE-2022-43945 commit 640f87c190e0d1b2a0fcb2ecf6d2cd53b1c41991 Author: Chuck Lever Date: Thu Sep 1 15:10:12 2022 -0400 NFSD: Protect against send buffer overflow in NFSv3 READDIR Since before the git era, NFSD has conserved the number of pages held by each nfsd thread by combining the RPC receive and send buffers into a single array of pages. This works because there are no cases where an operation needs a large RPC Call message and a large RPC Reply message at the same time. Once an RPC Call has been received, svc_process() updates svc_rqst::rq_res to describe the part of rq_pages that can be used for constructing the Reply. This means that the send buffer (rq_res) shrinks when the received RPC record containing the RPC Call is large. A client can force this shrinkage on TCP by sending a correctly- formed RPC Call header contained in an RPC record that is excessively large. The full maximum payload size cannot be constructed in that case. Thanks to Aleksi Illikainen and Kari Hulkko for uncovering this issue. Reported-by: Ben Ronallo Cc: Signed-off-by: Chuck Lever Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Scott Mayhew Signed-off-by: Patrick Talbert commit 5665aff4c12502c39f372548d1be622d99ad2252 Author: Scott Mayhew Date: Thu Nov 10 13:41:52 2022 -0500 NFSD: Protect against send buffer overflow in NFSv2 READ Bugzilla: https://bugzilla.redhat.com/2141769 CVE: CVE-2022-43945 Y-Commit: 385d7c5a859c93544e7aad1dfe8e955f2035ab5f O-Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2141770 O-CVE: CVE-2022-43945 commit 401bc1f90874280a80b93f23be33a0e7e2d1f912 Author: Chuck Lever Date: Thu Sep 1 15:10:18 2022 -0400 NFSD: Protect against send buffer overflow in NFSv2 READ Since before the git era, NFSD has conserved the number of pages held by each nfsd thread by combining the RPC receive and send buffers into a single array of pages. This works because there are no cases where an operation needs a large RPC Call message and a large RPC Reply at the same time. Once an RPC Call has been received, svc_process() updates svc_rqst::rq_res to describe the part of rq_pages that can be used for constructing the Reply. This means that the send buffer (rq_res) shrinks when the received RPC record containing the RPC Call is large. A client can force this shrinkage on TCP by sending a correctly- formed RPC Call header contained in an RPC record that is excessively large. The full maximum payload size cannot be constructed in that case. Cc: Signed-off-by: Chuck Lever Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Scott Mayhew Signed-off-by: Patrick Talbert commit f036edb9860c89970440f8d524831b11afd399fb Author: Scott Mayhew Date: Thu Nov 10 13:41:52 2022 -0500 NFSD: Protect against send buffer overflow in NFSv3 READ Bugzilla: https://bugzilla.redhat.com/2141769 CVE: CVE-2022-43945 Y-Commit: 45f0034c12711e534531b23c15c04aa060c9ed47 O-Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2141770 O-CVE: CVE-2022-43945 commit fa6be9cc6e80ec79892ddf08a8c10cabab9baf38 Author: Chuck Lever Date: Thu Sep 1 15:10:24 2022 -0400 NFSD: Protect against send buffer overflow in NFSv3 READ Since before the git era, NFSD has conserved the number of pages held by each nfsd thread by combining the RPC receive and send buffers into a single array of pages. This works because there are no cases where an operation needs a large RPC Call message and a large RPC Reply at the same time. Once an RPC Call has been received, svc_process() updates svc_rqst::rq_res to describe the part of rq_pages that can be used for constructing the Reply. This means that the send buffer (rq_res) shrinks when the received RPC record containing the RPC Call is large. A client can force this shrinkage on TCP by sending a correctly- formed RPC Call header contained in an RPC record that is excessively large. The full maximum payload size cannot be constructed in that case. Cc: Signed-off-by: Chuck Lever Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever Signed-off-by: Scott Mayhew Signed-off-by: Patrick Talbert Signed-off-by: Yannick Cote --- fs/lockd/svc.c | 6 ++- fs/nfs/callback_xdr.c | 6 ++- fs/nfsd/nfs3proc.c | 11 +++--- fs/nfsd/nfsproc.c | 6 +-- fs/nfsd/nfssvc.c | 6 ++- include/linux/kpatch_cve_2022_43945.h | 53 +++++++++++++++++++++++++++ 6 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 include/linux/kpatch_cve_2022_43945.h diff --git a/fs/lockd/svc.c b/fs/lockd/svc.c index b220e1b91726..e8b6e09af323 100644 --- a/fs/lockd/svc.c +++ b/fs/lockd/svc.c @@ -768,6 +768,8 @@ static void __exit exit_nlm(void) module_init(init_nlm); module_exit(exit_nlm); +#include + /** * nlmsvc_dispatch - Process an NLM Request * @rqstp: incoming request @@ -781,7 +783,7 @@ static int nlmsvc_dispatch(struct svc_rqst *rqstp, __be32 *statp) { const struct svc_procedure *procp = rqstp->rq_procinfo; - svcxdr_init_decode(rqstp); + kpatch_cve_2022_43945_svcxdr_init_decode(rqstp); if (!procp->pc_decode(rqstp, &rqstp->rq_arg_stream)) goto out_decode_err; @@ -791,7 +793,7 @@ static int nlmsvc_dispatch(struct svc_rqst *rqstp, __be32 *statp) if (*statp != rpc_success) return 1; - svcxdr_init_encode(rqstp); + kpatch_cve_2022_43945_svcxdr_init_encode(rqstp); if (!procp->pc_encode(rqstp, &rqstp->rq_res_stream)) goto out_encode_err; diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index a67c41ec545f..9438f91b9c2d 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -983,13 +983,15 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp) return rpc_success; } +#include + static int nfs_callback_dispatch(struct svc_rqst *rqstp, __be32 *statp) { const struct svc_procedure *procp = rqstp->rq_procinfo; - svcxdr_init_decode(rqstp); - svcxdr_init_encode(rqstp); + kpatch_cve_2022_43945_svcxdr_init_decode(rqstp); + kpatch_cve_2022_43945_svcxdr_init_encode(rqstp); *statp = procp->pc_func(rqstp); return 1; diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index 9d2667a8dbd1..e0eb8b7acec8 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -147,7 +147,6 @@ nfsd3_proc_read(struct svc_rqst *rqstp) { struct nfsd3_readargs *argp = rqstp->rq_argp; struct nfsd3_readres *resp = rqstp->rq_resp; - u32 max_blocksize = svc_max_payload(rqstp); unsigned int len; int v; @@ -156,7 +155,8 @@ nfsd3_proc_read(struct svc_rqst *rqstp) (unsigned long) argp->count, (unsigned long long) argp->offset); - argp->count = min_t(u32, argp->count, max_blocksize); + argp->count = min_t(u32, argp->count, svc_max_payload(rqstp)); + argp->count = min_t(u32, argp->count, rqstp->rq_res.buflen); if (argp->offset > (u64)OFFSET_MAX) argp->offset = (u64)OFFSET_MAX; if (argp->offset + argp->count > (u64)OFFSET_MAX) @@ -554,13 +554,14 @@ static void nfsd3_init_dirlist_pages(struct svc_rqst *rqstp, { struct xdr_buf *buf = &resp->dirlist; struct xdr_stream *xdr = &resp->xdr; - - count = clamp(count, (u32)(XDR_UNIT * 2), svc_max_payload(rqstp)); + unsigned int sendbuf = min_t(unsigned int, rqstp->rq_res.buflen, + svc_max_payload(rqstp)); memset(buf, 0, sizeof(*buf)); /* Reserve room for the NULL ptr & eof flag (-2 words) */ - buf->buflen = count - XDR_UNIT * 2; + buf->buflen = clamp(count, (u32)(XDR_UNIT * 2), sendbuf); + buf->buflen -= XDR_UNIT * 2; buf->pages = rqstp->rq_next_page; rqstp->rq_next_page += (buf->buflen + PAGE_SIZE - 1) >> PAGE_SHIFT; diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c index de282f3273c5..6c15d4ac52e8 100644 --- a/fs/nfsd/nfsproc.c +++ b/fs/nfsd/nfsproc.c @@ -182,6 +182,7 @@ nfsd_proc_read(struct svc_rqst *rqstp) argp->count, argp->offset); argp->count = min_t(u32, argp->count, NFSSVC_MAXBLKSIZE_V2); + argp->count = min_t(u32, argp->count, rqstp->rq_res.buflen); v = 0; len = argp->count; @@ -561,12 +562,11 @@ static void nfsd_init_dirlist_pages(struct svc_rqst *rqstp, struct xdr_buf *buf = &resp->dirlist; struct xdr_stream *xdr = &resp->xdr; - count = clamp(count, (u32)(XDR_UNIT * 2), svc_max_payload(rqstp)); - memset(buf, 0, sizeof(*buf)); /* Reserve room for the NULL ptr & eof flag (-2 words) */ - buf->buflen = count - XDR_UNIT * 2; + buf->buflen = clamp(count, (u32)(XDR_UNIT * 2), (u32)PAGE_SIZE); + buf->buflen -= XDR_UNIT * 2; buf->pages = rqstp->rq_next_page; rqstp->rq_next_page++; diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 80431921e5d7..125f490147df 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -990,6 +990,8 @@ nfsd(void *vrqstp) return 0; } +#include + /** * nfsd_dispatch - Process an NFS or NFSACL Request * @rqstp: incoming request @@ -1011,7 +1013,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) */ rqstp->rq_cachetype = proc->pc_cachetype; - svcxdr_init_decode(rqstp); + kpatch_cve_2022_43945_svcxdr_init_decode(rqstp); if (!proc->pc_decode(rqstp, &rqstp->rq_arg_stream)) goto out_decode_err; @@ -1028,7 +1030,7 @@ int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) * Need to grab the location to store the status, as * NFSv4 does some encoding while processing */ - svcxdr_init_encode(rqstp); + kpatch_cve_2022_43945_svcxdr_init_encode(rqstp); *statp = proc->pc_func(rqstp); if (*statp == rpc_drop_reply || test_bit(RQ_DROPME, &rqstp->rq_flags)) diff --git a/include/linux/kpatch_cve_2022_43945.h b/include/linux/kpatch_cve_2022_43945.h new file mode 100644 index 000000000000..d52392485a57 --- /dev/null +++ b/include/linux/kpatch_cve_2022_43945.h @@ -0,0 +1,53 @@ +#ifndef _KPATCH_CVE_2022_43945_H +#define _KPATCH_CVE_2022_43945_H + +/* + * svcxdr_init_decode - Prepare an xdr_stream for Call decoding + * @rqstp: controlling server RPC transaction context + * + * This function currently assumes the RPC header in rq_arg has + * already been decoded. Upon return, xdr->p points to the + * location of the upper layer header. + */ +static inline void kpatch_cve_2022_43945_svcxdr_init_decode(struct svc_rqst *rqstp) +{ + struct xdr_stream *xdr = &rqstp->rq_arg_stream; + struct xdr_buf *buf = &rqstp->rq_arg; + struct kvec *argv = buf->head; + + /* + * svc_getnl() and friends do not keep the xdr_buf's ::len + * field up to date. Refresh that field before initializing + * the argument decoding stream. + */ + buf->len = buf->head->iov_len + buf->page_len + buf->tail->iov_len; + + xdr_init_decode(xdr, buf, argv->iov_base, NULL); + xdr_set_scratch_page(xdr, rqstp->rq_scratch_page); +} + +/* + * svcxdr_init_encode - Prepare an xdr_stream for svc Reply encoding + * @rqstp: controlling server RPC transaction context + * + */ +static inline void kpatch_cve_2022_43945_svcxdr_init_encode(struct svc_rqst *rqstp) +{ + struct xdr_stream *xdr = &rqstp->rq_res_stream; + struct xdr_buf *buf = &rqstp->rq_res; + struct kvec *resv = buf->head; + + xdr_reset_scratch_buffer(xdr); + + xdr->buf = buf; + xdr->iov = resv; + xdr->p = resv->iov_base + resv->iov_len; + xdr->end = resv->iov_base + PAGE_SIZE - rqstp->rq_auth_slack; + buf->len = resv->iov_len; + xdr->page_ptr = buf->pages - 1; + buf->buflen = PAGE_SIZE * (rqstp->rq_page_end - buf->pages); + buf->buflen -= rqstp->rq_auth_slack; + xdr->rqst = NULL; +} + +#endif /* _KPATCH_CVE_2022_43945_H */ -- 2.39.0