]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/4.14.108/nfsd-fix-memory-corruption-caused-by-readdir.patch
Linux 4.14.108
[thirdparty/kernel/stable-queue.git] / releases / 4.14.108 / nfsd-fix-memory-corruption-caused-by-readdir.patch
1 From b602345da6cbb135ba68cf042df8ec9a73da7981 Mon Sep 17 00:00:00 2001
2 From: NeilBrown <neilb@suse.com>
3 Date: Mon, 4 Mar 2019 14:08:22 +1100
4 Subject: nfsd: fix memory corruption caused by readdir
5
6 From: NeilBrown <neilb@suse.com>
7
8 commit b602345da6cbb135ba68cf042df8ec9a73da7981 upstream.
9
10 If the result of an NFSv3 readdir{,plus} request results in the
11 "offset" on one entry having to be split across 2 pages, and is sized
12 so that the next directory entry doesn't fit in the requested size,
13 then memory corruption can happen.
14
15 When encode_entry() is called after encoding the last entry that fits,
16 it notices that ->offset and ->offset1 are set, and so stores the
17 offset value in the two pages as required. It clears ->offset1 but
18 *does not* clear ->offset.
19
20 Normally this omission doesn't matter as encode_entry_baggage() will
21 be called, and will set ->offset to a suitable value (not on a page
22 boundary).
23 But in the case where cd->buflen < elen and nfserr_toosmall is
24 returned, ->offset is not reset.
25
26 This means that nfsd3proc_readdirplus will see ->offset with a value 4
27 bytes before the end of a page, and ->offset1 set to NULL.
28 It will try to write 8bytes to ->offset.
29 If we are lucky, the next page will be read-only, and the system will
30 BUG: unable to handle kernel paging request at...
31
32 If we are unlucky, some innocent page will have the first 4 bytes
33 corrupted.
34
35 nfsd3proc_readdir() doesn't even check for ->offset1, it just blindly
36 writes 8 bytes to the offset wherever it is.
37
38 Fix this by clearing ->offset after it is used, and copying the
39 ->offset handling code from nfsd3_proc_readdirplus into
40 nfsd3_proc_readdir.
41
42 (Note that the commit hash in the Fixes tag is from the 'history'
43 tree - this bug predates git).
44
45 Fixes: 0b1d57cf7654 ("[PATCH] kNFSd: Fix nfs3 dentry encoding")
46 Fixes-URL: https://git.kernel.org/pub/scm/linux/kernel/git/history/history.git/commit/?id=0b1d57cf7654
47 Cc: stable@vger.kernel.org (v2.6.12+)
48 Signed-off-by: NeilBrown <neilb@suse.com>
49 Signed-off-by: J. Bruce Fields <bfields@redhat.com>
50 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
51
52 ---
53 fs/nfsd/nfs3proc.c | 16 ++++++++++++++--
54 fs/nfsd/nfs3xdr.c | 1 +
55 2 files changed, 15 insertions(+), 2 deletions(-)
56
57 --- a/fs/nfsd/nfs3proc.c
58 +++ b/fs/nfsd/nfs3proc.c
59 @@ -446,8 +446,19 @@ nfsd3_proc_readdir(struct svc_rqst *rqst
60 &resp->common, nfs3svc_encode_entry);
61 memcpy(resp->verf, argp->verf, 8);
62 resp->count = resp->buffer - argp->buffer;
63 - if (resp->offset)
64 - xdr_encode_hyper(resp->offset, argp->cookie);
65 + if (resp->offset) {
66 + loff_t offset = argp->cookie;
67 +
68 + if (unlikely(resp->offset1)) {
69 + /* we ended up with offset on a page boundary */
70 + *resp->offset = htonl(offset >> 32);
71 + *resp->offset1 = htonl(offset & 0xffffffff);
72 + resp->offset1 = NULL;
73 + } else {
74 + xdr_encode_hyper(resp->offset, offset);
75 + }
76 + resp->offset = NULL;
77 + }
78
79 RETURN_STATUS(nfserr);
80 }
81 @@ -516,6 +527,7 @@ nfsd3_proc_readdirplus(struct svc_rqst *
82 } else {
83 xdr_encode_hyper(resp->offset, offset);
84 }
85 + resp->offset = NULL;
86 }
87
88 RETURN_STATUS(nfserr);
89 --- a/fs/nfsd/nfs3xdr.c
90 +++ b/fs/nfsd/nfs3xdr.c
91 @@ -922,6 +922,7 @@ encode_entry(struct readdir_cd *ccd, con
92 } else {
93 xdr_encode_hyper(cd->offset, offset64);
94 }
95 + cd->offset = NULL;
96 }
97
98 /*