]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - queue-5.15/nfsd-verify-the-opened-dentry-after-setting-a-delega.patch
Fixes for 5.15
[thirdparty/kernel/stable-queue.git] / queue-5.15 / nfsd-verify-the-opened-dentry-after-setting-a-delega.patch
1 From 74b092c9ad5f5a51972d7008b627a97d92c4b46a Mon Sep 17 00:00:00 2001
2 From: Sasha Levin <sashal@kernel.org>
3 Date: Tue, 26 Jul 2022 16:45:30 +1000
4 Subject: NFSD: verify the opened dentry after setting a delegation
5
6 From: Jeff Layton <jlayton@kernel.org>
7
8 [ Upstream commit 876c553cb41026cb6ad3cef970a35e5f69c42a25 ]
9
10 Between opening a file and setting a delegation on it, someone could
11 rename or unlink the dentry. If this happens, we do not want to grant a
12 delegation on the open.
13
14 On a CLAIM_NULL open, we're opening by filename, and we may (in the
15 non-create case) or may not (in the create case) be holding i_rwsem
16 when attempting to set a delegation. The latter case allows a
17 race.
18
19 After getting a lease, redo the lookup of the file being opened and
20 validate that the resulting dentry matches the one in the open file
21 description.
22
23 To properly redo the lookup we need an rqst pointer to pass to
24 nfsd_lookup_dentry(), so make sure that is available.
25
26 Signed-off-by: Jeff Layton <jlayton@kernel.org>
27 Signed-off-by: NeilBrown <neilb@suse.de>
28 Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
29 ---
30 fs/nfsd/nfs4proc.c | 1 +
31 fs/nfsd/nfs4state.c | 54 ++++++++++++++++++++++++++++++++++++++++-----
32 fs/nfsd/xdr4.h | 1 +
33 3 files changed, 51 insertions(+), 5 deletions(-)
34
35 diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
36 index 46ec66f4ec9e7..ae0948271da9c 100644
37 --- a/fs/nfsd/nfs4proc.c
38 +++ b/fs/nfsd/nfs4proc.c
39 @@ -547,6 +547,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
40 open->op_openowner);
41
42 open->op_filp = NULL;
43 + open->op_rqstp = rqstp;
44
45 /* This check required by spec. */
46 if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
47 diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
48 index 2b333f9259a03..7122ebc50a035 100644
49 --- a/fs/nfsd/nfs4state.c
50 +++ b/fs/nfsd/nfs4state.c
51 @@ -5305,11 +5305,44 @@ static int nfsd4_check_conflicting_opens(struct nfs4_client *clp,
52 return 0;
53 }
54
55 +/*
56 + * It's possible that between opening the dentry and setting the delegation,
57 + * that it has been renamed or unlinked. Redo the lookup to verify that this
58 + * hasn't happened.
59 + */
60 +static int
61 +nfsd4_verify_deleg_dentry(struct nfsd4_open *open, struct nfs4_file *fp,
62 + struct svc_fh *parent)
63 +{
64 + struct svc_export *exp;
65 + struct dentry *child;
66 + __be32 err;
67 +
68 + /* parent may already be locked, and it may get unlocked by
69 + * this call, but that is safe.
70 + */
71 + err = nfsd_lookup_dentry(open->op_rqstp, parent,
72 + open->op_fname, open->op_fnamelen,
73 + &exp, &child);
74 +
75 + if (err)
76 + return -EAGAIN;
77 +
78 + dput(child);
79 + if (child != file_dentry(fp->fi_deleg_file->nf_file))
80 + return -EAGAIN;
81 +
82 + return 0;
83 +}
84 +
85 static struct nfs4_delegation *
86 -nfs4_set_delegation(struct nfs4_client *clp,
87 - struct nfs4_file *fp, struct nfs4_clnt_odstate *odstate)
88 +nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
89 + struct svc_fh *parent)
90 {
91 int status = 0;
92 + struct nfs4_client *clp = stp->st_stid.sc_client;
93 + struct nfs4_file *fp = stp->st_stid.sc_file;
94 + struct nfs4_clnt_odstate *odstate = stp->st_clnt_odstate;
95 struct nfs4_delegation *dp;
96 struct nfsd_file *nf;
97 struct file_lock *fl;
98 @@ -5364,6 +5397,13 @@ nfs4_set_delegation(struct nfs4_client *clp,
99 locks_free_lock(fl);
100 if (status)
101 goto out_clnt_odstate;
102 +
103 + if (parent) {
104 + status = nfsd4_verify_deleg_dentry(open, fp, parent);
105 + if (status)
106 + goto out_unlock;
107 + }
108 +
109 status = nfsd4_check_conflicting_opens(clp, fp);
110 if (status)
111 goto out_unlock;
112 @@ -5419,11 +5459,13 @@ static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status)
113 * proper support for them.
114 */
115 static void
116 -nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp)
117 +nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
118 + struct svc_fh *currentfh)
119 {
120 struct nfs4_delegation *dp;
121 struct nfs4_openowner *oo = openowner(stp->st_stateowner);
122 struct nfs4_client *clp = stp->st_stid.sc_client;
123 + struct svc_fh *parent = NULL;
124 int cb_up;
125 int status = 0;
126
127 @@ -5437,6 +5479,8 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp)
128 goto out_no_deleg;
129 break;
130 case NFS4_OPEN_CLAIM_NULL:
131 + parent = currentfh;
132 + fallthrough;
133 case NFS4_OPEN_CLAIM_FH:
134 /*
135 * Let's not give out any delegations till everyone's
136 @@ -5451,7 +5495,7 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp)
137 default:
138 goto out_no_deleg;
139 }
140 - dp = nfs4_set_delegation(clp, stp->st_stid.sc_file, stp->st_clnt_odstate);
141 + dp = nfs4_set_delegation(open, stp, parent);
142 if (IS_ERR(dp))
143 goto out_no_deleg;
144
145 @@ -5583,7 +5627,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
146 * Attempt to hand out a delegation. No error return, because the
147 * OPEN succeeds even if we fail.
148 */
149 - nfs4_open_delegation(open, stp);
150 + nfs4_open_delegation(open, stp, &resp->cstate.current_fh);
151 nodeleg:
152 status = nfs_ok;
153 trace_nfsd_open(&stp->st_stid.sc_stateid);
154 diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
155 index adb9aef26d7f1..466e2786fc976 100644
156 --- a/fs/nfsd/xdr4.h
157 +++ b/fs/nfsd/xdr4.h
158 @@ -279,6 +279,7 @@ struct nfsd4_open {
159 struct nfs4_clnt_odstate *op_odstate; /* used during processing */
160 struct nfs4_acl *op_acl;
161 struct xdr_netobj op_label;
162 + struct svc_rqst *op_rqstp;
163 };
164
165 struct nfsd4_open_confirm {
166 --
167 2.43.0
168