Commit b2c0cea6 authored by J. Bruce Fields's avatar J. Bruce Fields

nfsd4: check for negative dentry before use in nfsv4 readdir

After 2f9092e1 "Fix i_mutex vs.  readdir
handling in nfsd" (and 14f7dd63 "Copy XFS readdir hack into nfsd code"),
an entry may be removed between the first mutex_unlock and the second
mutex_lock. In this case, lookup_one_len() will return a negative
dentry.  Check for this case to avoid a NULL dereference.
Signed-off-by: default avatarJ. Bruce Fields <bfields@citi.umich.edu>
Reviewed-by: default avatarJ. R. Okajima <hooanon05@yahoo.co.jp>
Cc: stable@kernel.org
parent ccecee1e
...@@ -2214,6 +2214,15 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd, ...@@ -2214,6 +2214,15 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen); dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
return nfserrno(PTR_ERR(dentry)); return nfserrno(PTR_ERR(dentry));
if (!dentry->d_inode) {
/*
* nfsd_buffered_readdir drops the i_mutex between
* readdir and calling this callback, leaving a window
* where this directory entry could have gone away.
*/
dput(dentry);
return nfserr_noent;
}
exp_get(exp); exp_get(exp);
/* /*
...@@ -2276,6 +2285,7 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen, ...@@ -2276,6 +2285,7 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common); struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
int buflen; int buflen;
__be32 *p = cd->buffer; __be32 *p = cd->buffer;
__be32 *cookiep;
__be32 nfserr = nfserr_toosmall; __be32 nfserr = nfserr_toosmall;
/* In nfsv4, "." and ".." never make it onto the wire.. */ /* In nfsv4, "." and ".." never make it onto the wire.. */
...@@ -2292,7 +2302,7 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen, ...@@ -2292,7 +2302,7 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
goto fail; goto fail;
*p++ = xdr_one; /* mark entry present */ *p++ = xdr_one; /* mark entry present */
cd->offset = p; /* remember pointer */ cookiep = p;
p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */ p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */
p = xdr_encode_array(p, name, namlen); /* name length & name */ p = xdr_encode_array(p, name, namlen); /* name length & name */
...@@ -2306,6 +2316,8 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen, ...@@ -2306,6 +2316,8 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
goto fail; goto fail;
case nfserr_dropit: case nfserr_dropit:
goto fail; goto fail;
case nfserr_noent:
goto skip_entry;
default: default:
/* /*
* If the client requested the RDATTR_ERROR attribute, * If the client requested the RDATTR_ERROR attribute,
...@@ -2324,6 +2336,8 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen, ...@@ -2324,6 +2336,8 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
} }
cd->buflen -= (p - cd->buffer); cd->buflen -= (p - cd->buffer);
cd->buffer = p; cd->buffer = p;
cd->offset = cookiep;
skip_entry:
cd->common.err = nfs_ok; cd->common.err = nfs_ok;
return 0; return 0;
fail: fail:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment