[patch 21/28] nfsd4: check for negative dentry before use in nfsv4 readdir

From: Greg KH
Date: Thu May 14 2009 - 19:10:14 EST


2.6.27-stable review patch. If anyone has any objections, please let us know.

------------------

From: J. Bruce Fields <bfields@xxxxxxxxxxxxxx>

commit b2c0cea6b1cb210e962f07047df602875564069e upstream.

After 2f9092e1020246168b1309b35e085ecd7ff9ff72 "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: J. Bruce Fields <bfields@xxxxxxxxxxxxxx>
Reviewed-by: J. R. Okajima <hooanon05@xxxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxx>

---
fs/nfsd/nfs4xdr.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)

--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -1833,6 +1833,15 @@ nfsd4_encode_dirent_fattr(struct nfsd4_r
dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen);
if (IS_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);
/*
@@ -1895,6 +1904,7 @@ nfsd4_encode_dirent(void *ccdv, const ch
struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
int buflen;
__be32 *p = cd->buffer;
+ __be32 *cookiep;
__be32 nfserr = nfserr_toosmall;

/* In nfsv4, "." and ".." never make it onto the wire.. */
@@ -1911,7 +1921,7 @@ nfsd4_encode_dirent(void *ccdv, const ch
goto fail;

*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_array(p, name, namlen); /* name length & name */

@@ -1925,6 +1935,8 @@ nfsd4_encode_dirent(void *ccdv, const ch
goto fail;
case nfserr_dropit:
goto fail;
+ case nfserr_noent:
+ goto skip_entry;
default:
/*
* If the client requested the RDATTR_ERROR attribute,
@@ -1943,6 +1955,8 @@ nfsd4_encode_dirent(void *ccdv, const ch
}
cd->buflen -= (p - cd->buffer);
cd->buffer = p;
+ cd->offset = cookiep;
+skip_entry:
cd->common.err = nfs_ok;
return 0;
fail:


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/