Skip to content

Commit ffeeaad

Browse files
author
Al Viro
committed
nfs: fix ->d_revalidate() UAF on ->d_name accesses
Pass the stable name all the way down to ->rpc_ops->lookup() instances. Note that passing &dentry->d_name is safe in e.g. nfs_lookup() - it *is* stable there, as it is in ->create() et.al. dget_parent() in nfs_instantiate() should be redundant - it'd better be stable there; if it's not, we have more trouble, since ->d_name would also be unsafe in such case. nfs_submount() and nfs4_submount() may or may not require fixes - if they ever get moved on server with fhandle preserved, we are in trouble there... UAF window is fairly narrow here and exfiltration requires the ability to watch the traffic. Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Al Viro <[email protected]>
1 parent 39f644a commit ffeeaad

File tree

6 files changed

+25
-24
lines changed

6 files changed

+25
-24
lines changed

fs/nfs/dir.c

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1672,7 +1672,7 @@ nfs_lookup_revalidate_delegated(struct inode *dir, struct dentry *dentry,
16721672
return nfs_lookup_revalidate_done(dir, dentry, inode, 1);
16731673
}
16741674

1675-
static int nfs_lookup_revalidate_dentry(struct inode *dir,
1675+
static int nfs_lookup_revalidate_dentry(struct inode *dir, const struct qstr *name,
16761676
struct dentry *dentry,
16771677
struct inode *inode, unsigned int flags)
16781678
{
@@ -1690,7 +1690,7 @@ static int nfs_lookup_revalidate_dentry(struct inode *dir,
16901690
goto out;
16911691

16921692
dir_verifier = nfs_save_change_attribute(dir);
1693-
ret = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr);
1693+
ret = NFS_PROTO(dir)->lookup(dir, dentry, name, fhandle, fattr);
16941694
if (ret < 0)
16951695
goto out;
16961696

@@ -1775,7 +1775,7 @@ nfs_do_lookup_revalidate(struct inode *dir, const struct qstr *name,
17751775
if (NFS_STALE(inode))
17761776
goto out_bad;
17771777

1778-
return nfs_lookup_revalidate_dentry(dir, dentry, inode, flags);
1778+
return nfs_lookup_revalidate_dentry(dir, name, dentry, inode, flags);
17791779
out_valid:
17801780
return nfs_lookup_revalidate_done(dir, dentry, inode, 1);
17811781
out_bad:
@@ -1970,7 +1970,8 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
19701970

19711971
dir_verifier = nfs_save_change_attribute(dir);
19721972
trace_nfs_lookup_enter(dir, dentry, flags);
1973-
error = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr);
1973+
error = NFS_PROTO(dir)->lookup(dir, dentry, &dentry->d_name,
1974+
fhandle, fattr);
19741975
if (error == -ENOENT) {
19751976
if (nfs_server_capable(dir, NFS_CAP_CASE_INSENSITIVE))
19761977
dir_verifier = inode_peek_iversion_raw(dir);
@@ -2246,7 +2247,7 @@ nfs4_lookup_revalidate(struct inode *dir, const struct qstr *name,
22462247
reval_dentry:
22472248
if (flags & LOOKUP_RCU)
22482249
return -ECHILD;
2249-
return nfs_lookup_revalidate_dentry(dir, dentry, inode, flags);
2250+
return nfs_lookup_revalidate_dentry(dir, name, dentry, inode, flags);
22502251

22512252
full_reval:
22522253
return nfs_do_lookup_revalidate(dir, name, dentry, flags);
@@ -2305,7 +2306,8 @@ nfs_add_or_obtain(struct dentry *dentry, struct nfs_fh *fhandle,
23052306
d_drop(dentry);
23062307

23072308
if (fhandle->size == 0) {
2308-
error = NFS_PROTO(dir)->lookup(dir, dentry, fhandle, fattr);
2309+
error = NFS_PROTO(dir)->lookup(dir, dentry, &dentry->d_name,
2310+
fhandle, fattr);
23092311
if (error)
23102312
goto out_error;
23112313
}

fs/nfs/namespace.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ int nfs_submount(struct fs_context *fc, struct nfs_server *server)
308308
int err;
309309

310310
/* Look it up again to get its attributes */
311-
err = server->nfs_client->rpc_ops->lookup(d_inode(parent), dentry,
311+
err = server->nfs_client->rpc_ops->lookup(d_inode(parent), dentry, &dentry->d_name,
312312
ctx->mntfh, ctx->clone_data.fattr);
313313
dput(parent);
314314
if (err != 0)

fs/nfs/nfs3proc.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ __nfs3_proc_lookup(struct inode *dir, const char *name, size_t len,
192192
}
193193

194194
static int
195-
nfs3_proc_lookup(struct inode *dir, struct dentry *dentry,
195+
nfs3_proc_lookup(struct inode *dir, struct dentry *dentry, const struct qstr *name,
196196
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
197197
{
198198
unsigned short task_flags = 0;
@@ -202,8 +202,7 @@ nfs3_proc_lookup(struct inode *dir, struct dentry *dentry,
202202
task_flags |= RPC_TASK_TIMEOUT;
203203

204204
dprintk("NFS call lookup %pd2\n", dentry);
205-
return __nfs3_proc_lookup(dir, dentry->d_name.name,
206-
dentry->d_name.len, fhandle, fattr,
205+
return __nfs3_proc_lookup(dir, name->name, name->len, fhandle, fattr,
207206
task_flags);
208207
}
209208

fs/nfs/nfs4proc.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4536,15 +4536,15 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
45364536
}
45374537

45384538
static int _nfs4_proc_lookup(struct rpc_clnt *clnt, struct inode *dir,
4539-
struct dentry *dentry, struct nfs_fh *fhandle,
4540-
struct nfs_fattr *fattr)
4539+
struct dentry *dentry, const struct qstr *name,
4540+
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
45414541
{
45424542
struct nfs_server *server = NFS_SERVER(dir);
45434543
int status;
45444544
struct nfs4_lookup_arg args = {
45454545
.bitmask = server->attr_bitmask,
45464546
.dir_fh = NFS_FH(dir),
4547-
.name = &dentry->d_name,
4547+
.name = name,
45484548
};
45494549
struct nfs4_lookup_res res = {
45504550
.server = server,
@@ -4586,17 +4586,16 @@ static void nfs_fixup_secinfo_attributes(struct nfs_fattr *fattr)
45864586
}
45874587

45884588
static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir,
4589-
struct dentry *dentry, struct nfs_fh *fhandle,
4590-
struct nfs_fattr *fattr)
4589+
struct dentry *dentry, const struct qstr *name,
4590+
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
45914591
{
45924592
struct nfs4_exception exception = {
45934593
.interruptible = true,
45944594
};
45954595
struct rpc_clnt *client = *clnt;
4596-
const struct qstr *name = &dentry->d_name;
45974596
int err;
45984597
do {
4599-
err = _nfs4_proc_lookup(client, dir, dentry, fhandle, fattr);
4598+
err = _nfs4_proc_lookup(client, dir, dentry, name, fhandle, fattr);
46004599
trace_nfs4_lookup(dir, name, err);
46014600
switch (err) {
46024601
case -NFS4ERR_BADNAME:
@@ -4631,13 +4630,13 @@ static int nfs4_proc_lookup_common(struct rpc_clnt **clnt, struct inode *dir,
46314630
return err;
46324631
}
46334632

4634-
static int nfs4_proc_lookup(struct inode *dir, struct dentry *dentry,
4633+
static int nfs4_proc_lookup(struct inode *dir, struct dentry *dentry, const struct qstr *name,
46354634
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
46364635
{
46374636
int status;
46384637
struct rpc_clnt *client = NFS_CLIENT(dir);
46394638

4640-
status = nfs4_proc_lookup_common(&client, dir, dentry, fhandle, fattr);
4639+
status = nfs4_proc_lookup_common(&client, dir, dentry, name, fhandle, fattr);
46414640
if (client != NFS_CLIENT(dir)) {
46424641
rpc_shutdown_client(client);
46434642
nfs_fixup_secinfo_attributes(fattr);
@@ -4652,7 +4651,8 @@ nfs4_proc_lookup_mountpoint(struct inode *dir, struct dentry *dentry,
46524651
struct rpc_clnt *client = NFS_CLIENT(dir);
46534652
int status;
46544653

4655-
status = nfs4_proc_lookup_common(&client, dir, dentry, fhandle, fattr);
4654+
status = nfs4_proc_lookup_common(&client, dir, dentry, &dentry->d_name,
4655+
fhandle, fattr);
46564656
if (status < 0)
46574657
return ERR_PTR(status);
46584658
return (client == NFS_CLIENT(dir)) ? rpc_clone_client(client) : client;

fs/nfs/proc.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,13 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
153153
}
154154

155155
static int
156-
nfs_proc_lookup(struct inode *dir, struct dentry *dentry,
156+
nfs_proc_lookup(struct inode *dir, struct dentry *dentry, const struct qstr *name,
157157
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
158158
{
159159
struct nfs_diropargs arg = {
160160
.fh = NFS_FH(dir),
161-
.name = dentry->d_name.name,
162-
.len = dentry->d_name.len
161+
.name = name->name,
162+
.len = name->len
163163
};
164164
struct nfs_diropok res = {
165165
.fh = fhandle,

include/linux/nfs_xdr.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1785,7 +1785,7 @@ struct nfs_rpc_ops {
17851785
struct nfs_fattr *, struct inode *);
17861786
int (*setattr) (struct dentry *, struct nfs_fattr *,
17871787
struct iattr *);
1788-
int (*lookup) (struct inode *, struct dentry *,
1788+
int (*lookup) (struct inode *, struct dentry *, const struct qstr *,
17891789
struct nfs_fh *, struct nfs_fattr *);
17901790
int (*lookupp) (struct inode *, struct nfs_fh *,
17911791
struct nfs_fattr *);

0 commit comments

Comments
 (0)