diff options
author | Bryan Schumaker <bjschuma@netapp.com> | 2010-10-20 15:44:29 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-10-23 15:27:33 -0400 |
commit | babddc72a9468884ce1a23db3c3d54b0afa299f0 (patch) | |
tree | b176e5795b47c73c47543acdc546da0c38619ddc /fs/nfs/nfs3xdr.c | |
parent | ba8e452a4fe64a51b74d43761e14d99f0666cc45 (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.c | 93 |
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 | ||
103 | static 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 | ||
129 | static inline __be32 * | ||
130 | xdr_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 | |||
147 | out_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 | ||
243 | static inline __be32 * | 273 | static inline __be32 * |
274 | xdr_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; | ||
288 | out_overflow: | ||
289 | print_overflow_msg(__func__, xdr); | ||
290 | return ERR_PTR(-EIO); | ||
291 | } | ||
292 | |||
293 | static inline __be32 * | ||
244 | xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr) | 294 | xdr_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 * |
619 | nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) | 669 | nfs3_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 | |||
732 | out_overflow: | ||
733 | print_overflow_msg(__func__, xdr); | ||
734 | out_overflow_exit: | ||
735 | return ERR_PTR(-EIO); | ||
655 | } | 736 | } |
656 | 737 | ||
657 | /* | 738 | /* |