NFS: decode_dirent should use an xdr_stream

Convert nfs*xdr.c to use an xdr stream in decode_dirent.  This will prevent a
kernel oops that has been occuring when reading a vmapped page.

Signed-off-by: Bryan Schumaker <bjschuma@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
Bryan Schumaker
2010-10-20 15:44:29 -04:00
committed by Trond Myklebust
parent ba8e452a4f
commit babddc72a9
7 changed files with 203 additions and 41 deletions

View File

@@ -100,6 +100,13 @@ static const umode_t nfs_type2fmt[] = {
[NF3FIFO] = S_IFIFO,
};
static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
{
dprintk("nfs: %s: prematurely hit end of receive buffer. "
"Remaining buffer length is %tu words.\n",
func, xdr->end - xdr->p);
}
/*
* Common NFS XDR functions as inlines
*/
@@ -119,6 +126,29 @@ xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh)
return NULL;
}
static inline __be32 *
xdr_decode_fhandle_stream(struct xdr_stream *xdr, struct nfs_fh *fh)
{
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
fh->size = ntohl(*p++);
if (fh->size <= NFS3_FHSIZE) {
p = xdr_inline_decode(xdr, fh->size);
if (unlikely(!p))
goto out_overflow;
memcpy(fh->data, p, fh->size);
return p + XDR_QUADLEN(fh->size);
}
return NULL;
out_overflow:
print_overflow_msg(__func__, xdr);
return ERR_PTR(-EIO);
}
/*
* Encode/decode time.
*/
@@ -240,6 +270,26 @@ xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
return p;
}
static inline __be32 *
xdr_decode_post_op_attr_stream(struct xdr_stream *xdr, struct nfs_fattr *fattr)
{
__be32 *p;
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
if (ntohl(*p++)) {
p = xdr_inline_decode(xdr, 84);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_fattr(p, fattr);
}
return p;
out_overflow:
print_overflow_msg(__func__, xdr);
return ERR_PTR(-EIO);
}
static inline __be32 *
xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr)
{
@@ -616,19 +666,33 @@ err_unmap:
}
__be32 *
nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, int plus)
{
__be32 *p;
struct nfs_entry old = *entry;
if (!*p++) {
if (!*p)
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
if (!ntohl(*p++)) {
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
if (!ntohl(*p++))
return ERR_PTR(-EAGAIN);
entry->eof = 1;
return ERR_PTR(-EBADCOOKIE);
}
p = xdr_inline_decode(xdr, 12);
if (unlikely(!p))
goto out_overflow;
p = xdr_decode_hyper(p, &entry->ino);
entry->len = ntohl(*p++);
p = xdr_inline_decode(xdr, entry->len + 8);
if (unlikely(!p))
goto out_overflow;
entry->name = (const char *) p;
p += XDR_QUADLEN(entry->len);
entry->prev_cookie = entry->cookie;
@@ -636,10 +700,17 @@ nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
if (plus) {
entry->fattr->valid = 0;
p = xdr_decode_post_op_attr(p, entry->fattr);
p = xdr_decode_post_op_attr_stream(xdr, entry->fattr);
if (IS_ERR(p))
goto out_overflow_exit;
/* In fact, a post_op_fh3: */
p = xdr_inline_decode(xdr, 4);
if (unlikely(!p))
goto out_overflow;
if (*p++) {
p = xdr_decode_fhandle(p, entry->fh);
p = xdr_decode_fhandle_stream(xdr, entry->fh);
if (IS_ERR(p))
goto out_overflow_exit;
/* Ugh -- server reply was truncated */
if (p == NULL) {
dprintk("NFS: FH truncated\n");
@@ -650,8 +721,18 @@ nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
}
entry->eof = !p[0] && p[1];
p = xdr_inline_peek(xdr, 8);
if (p != NULL)
entry->eof = !p[0] && p[1];
else
entry->eof = 0;
return p;
out_overflow:
print_overflow_msg(__func__, xdr);
out_overflow_exit:
return ERR_PTR(-EIO);
}
/*