aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/nfs3xdr.c
diff options
context:
space:
mode:
authorBryan Schumaker <bjschuma@netapp.com>2010-10-20 15:44:29 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2010-10-23 15:27:33 -0400
commitbabddc72a9468884ce1a23db3c3d54b0afa299f0 (patch)
treeb176e5795b47c73c47543acdc546da0c38619ddc /fs/nfs/nfs3xdr.c
parentba8e452a4fe64a51b74d43761e14d99f0666cc45 (diff)
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>
Diffstat (limited to 'fs/nfs/nfs3xdr.c')
-rw-r--r--fs/nfs/nfs3xdr.c93
1 files changed, 87 insertions, 6 deletions
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index 52b2fda66e63..d562c8d9d56e 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -100,6 +100,13 @@ static const umode_t nfs_type2fmt[] = {
100 [NF3FIFO] = S_IFIFO, 100 [NF3FIFO] = S_IFIFO,
101}; 101};
102 102
103static void print_overflow_msg(const char *func, const struct xdr_stream *xdr)
104{
105 dprintk("nfs: %s: prematurely hit end of receive buffer. "
106 "Remaining buffer length is %tu words.\n",
107 func, xdr->end - xdr->p);
108}
109
103/* 110/*
104 * Common NFS XDR functions as inlines 111 * Common NFS XDR functions as inlines
105 */ 112 */
@@ -119,6 +126,29 @@ xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh)
119 return NULL; 126 return NULL;
120} 127}
121 128
129static inline __be32 *
130xdr_decode_fhandle_stream(struct xdr_stream *xdr, struct nfs_fh *fh)
131{
132 __be32 *p;
133 p = xdr_inline_decode(xdr, 4);
134 if (unlikely(!p))
135 goto out_overflow;
136 fh->size = ntohl(*p++);
137
138 if (fh->size <= NFS3_FHSIZE) {
139 p = xdr_inline_decode(xdr, fh->size);
140 if (unlikely(!p))
141 goto out_overflow;
142 memcpy(fh->data, p, fh->size);
143 return p + XDR_QUADLEN(fh->size);
144 }
145 return NULL;
146
147out_overflow:
148 print_overflow_msg(__func__, xdr);
149 return ERR_PTR(-EIO);
150}
151
122/* 152/*
123 * Encode/decode time. 153 * Encode/decode time.
124 */ 154 */
@@ -241,6 +271,26 @@ xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr)
241} 271}
242 272
243static inline __be32 * 273static inline __be32 *
274xdr_decode_post_op_attr_stream(struct xdr_stream *xdr, struct nfs_fattr *fattr)
275{
276 __be32 *p;
277
278 p = xdr_inline_decode(xdr, 4);
279 if (unlikely(!p))
280 goto out_overflow;
281 if (ntohl(*p++)) {
282 p = xdr_inline_decode(xdr, 84);
283 if (unlikely(!p))
284 goto out_overflow;
285 p = xdr_decode_fattr(p, fattr);
286 }
287 return p;
288out_overflow:
289 print_overflow_msg(__func__, xdr);
290 return ERR_PTR(-EIO);
291}
292
293static inline __be32 *
244xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr) 294xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr)
245{ 295{
246 if (*p++) 296 if (*p++)
@@ -616,19 +666,33 @@ err_unmap:
616} 666}
617 667
618__be32 * 668__be32 *
619nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) 669nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, int plus)
620{ 670{
671 __be32 *p;
621 struct nfs_entry old = *entry; 672 struct nfs_entry old = *entry;
622 673
623 if (!*p++) { 674 p = xdr_inline_decode(xdr, 4);
624 if (!*p) 675 if (unlikely(!p))
676 goto out_overflow;
677 if (!ntohl(*p++)) {
678 p = xdr_inline_decode(xdr, 4);
679 if (unlikely(!p))
680 goto out_overflow;
681 if (!ntohl(*p++))
625 return ERR_PTR(-EAGAIN); 682 return ERR_PTR(-EAGAIN);
626 entry->eof = 1; 683 entry->eof = 1;
627 return ERR_PTR(-EBADCOOKIE); 684 return ERR_PTR(-EBADCOOKIE);
628 } 685 }
629 686
687 p = xdr_inline_decode(xdr, 12);
688 if (unlikely(!p))
689 goto out_overflow;
630 p = xdr_decode_hyper(p, &entry->ino); 690 p = xdr_decode_hyper(p, &entry->ino);
631 entry->len = ntohl(*p++); 691 entry->len = ntohl(*p++);
692
693 p = xdr_inline_decode(xdr, entry->len + 8);
694 if (unlikely(!p))
695 goto out_overflow;
632 entry->name = (const char *) p; 696 entry->name = (const char *) p;
633 p += XDR_QUADLEN(entry->len); 697 p += XDR_QUADLEN(entry->len);
634 entry->prev_cookie = entry->cookie; 698 entry->prev_cookie = entry->cookie;
@@ -636,10 +700,17 @@ nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
636 700
637 if (plus) { 701 if (plus) {
638 entry->fattr->valid = 0; 702 entry->fattr->valid = 0;
639 p = xdr_decode_post_op_attr(p, entry->fattr); 703 p = xdr_decode_post_op_attr_stream(xdr, entry->fattr);
704 if (IS_ERR(p))
705 goto out_overflow_exit;
640 /* In fact, a post_op_fh3: */ 706 /* In fact, a post_op_fh3: */
707 p = xdr_inline_decode(xdr, 4);
708 if (unlikely(!p))
709 goto out_overflow;
641 if (*p++) { 710 if (*p++) {
642 p = xdr_decode_fhandle(p, entry->fh); 711 p = xdr_decode_fhandle_stream(xdr, entry->fh);
712 if (IS_ERR(p))
713 goto out_overflow_exit;
643 /* Ugh -- server reply was truncated */ 714 /* Ugh -- server reply was truncated */
644 if (p == NULL) { 715 if (p == NULL) {
645 dprintk("NFS: FH truncated\n"); 716 dprintk("NFS: FH truncated\n");
@@ -650,8 +721,18 @@ nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus)
650 memset((u8*)(entry->fh), 0, sizeof(*entry->fh)); 721 memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
651 } 722 }
652 723
653 entry->eof = !p[0] && p[1]; 724 p = xdr_inline_peek(xdr, 8);
725 if (p != NULL)
726 entry->eof = !p[0] && p[1];
727 else
728 entry->eof = 0;
729
654 return p; 730 return p;
731
732out_overflow:
733 print_overflow_msg(__func__, xdr);
734out_overflow_exit:
735 return ERR_PTR(-EIO);
655} 736}
656 737
657/* 738/*