aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHugh Dickins <hughd@google.com>2012-10-07 23:32:51 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2012-10-09 23:33:55 -0400
commit35c2a7f4908d404c9124c2efc6ada4640ca4d5d5 (patch)
tree4ef3fa1953b939fb4bfff935b3d351f76d97252b
parent8e22cc88d68ca1a46d7d582938f979eb640ed30f (diff)
tmpfs,ceph,gfs2,isofs,reiserfs,xfs: fix fh_len checking
Fuzzing with trinity oopsed on the 1st instruction of shmem_fh_to_dentry(), u64 inum = fid->raw[2]; which is unhelpfully reported as at the end of shmem_alloc_inode(): BUG: unable to handle kernel paging request at ffff880061cd3000 IP: [<ffffffff812190d0>] shmem_alloc_inode+0x40/0x40 Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC Call Trace: [<ffffffff81488649>] ? exportfs_decode_fh+0x79/0x2d0 [<ffffffff812d77c3>] do_handle_open+0x163/0x2c0 [<ffffffff812d792c>] sys_open_by_handle_at+0xc/0x10 [<ffffffff83a5f3f8>] tracesys+0xe1/0xe6 Right, tmpfs is being stupid to access fid->raw[2] before validating that fh_len includes it: the buffer kmalloc'ed by do_sys_name_to_handle() may fall at the end of a page, and the next page not be present. But some other filesystems (ceph, gfs2, isofs, reiserfs, xfs) are being careless about fh_len too, in fh_to_dentry() and/or fh_to_parent(), and could oops in the same way: add the missing fh_len checks to those. Reported-by: Sasha Levin <levinsasha928@gmail.com> Signed-off-by: Hugh Dickins <hughd@google.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Cc: Sage Weil <sage@inktank.com> Cc: Steven Whitehouse <swhiteho@redhat.com> Cc: Christoph Hellwig <hch@infradead.org> Cc: stable@vger.kernel.org Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/ceph/export.c18
-rw-r--r--fs/gfs2/export.c4
-rw-r--r--fs/isofs/export.c2
-rw-r--r--fs/reiserfs/inode.c6
-rw-r--r--fs/xfs/xfs_export.c3
-rw-r--r--mm/shmem.c6
6 files changed, 31 insertions, 8 deletions
diff --git a/fs/ceph/export.c b/fs/ceph/export.c
index 8e1b60e557b6..02ce90972d81 100644
--- a/fs/ceph/export.c
+++ b/fs/ceph/export.c
@@ -99,7 +99,7 @@ static int ceph_encode_fh(struct inode *inode, u32 *rawfh, int *max_len,
99 * FIXME: we should try harder by querying the mds for the ino. 99 * FIXME: we should try harder by querying the mds for the ino.
100 */ 100 */
101static struct dentry *__fh_to_dentry(struct super_block *sb, 101static struct dentry *__fh_to_dentry(struct super_block *sb,
102 struct ceph_nfs_fh *fh) 102 struct ceph_nfs_fh *fh, int fh_len)
103{ 103{
104 struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc; 104 struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
105 struct inode *inode; 105 struct inode *inode;
@@ -107,6 +107,9 @@ static struct dentry *__fh_to_dentry(struct super_block *sb,
107 struct ceph_vino vino; 107 struct ceph_vino vino;
108 int err; 108 int err;
109 109
110 if (fh_len < sizeof(*fh) / 4)
111 return ERR_PTR(-ESTALE);
112
110 dout("__fh_to_dentry %llx\n", fh->ino); 113 dout("__fh_to_dentry %llx\n", fh->ino);
111 vino.ino = fh->ino; 114 vino.ino = fh->ino;
112 vino.snap = CEPH_NOSNAP; 115 vino.snap = CEPH_NOSNAP;
@@ -150,7 +153,7 @@ static struct dentry *__fh_to_dentry(struct super_block *sb,
150 * convert connectable fh to dentry 153 * convert connectable fh to dentry
151 */ 154 */
152static struct dentry *__cfh_to_dentry(struct super_block *sb, 155static struct dentry *__cfh_to_dentry(struct super_block *sb,
153 struct ceph_nfs_confh *cfh) 156 struct ceph_nfs_confh *cfh, int fh_len)
154{ 157{
155 struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc; 158 struct ceph_mds_client *mdsc = ceph_sb_to_client(sb)->mdsc;
156 struct inode *inode; 159 struct inode *inode;
@@ -158,6 +161,9 @@ static struct dentry *__cfh_to_dentry(struct super_block *sb,
158 struct ceph_vino vino; 161 struct ceph_vino vino;
159 int err; 162 int err;
160 163
164 if (fh_len < sizeof(*cfh) / 4)
165 return ERR_PTR(-ESTALE);
166
161 dout("__cfh_to_dentry %llx (%llx/%x)\n", 167 dout("__cfh_to_dentry %llx (%llx/%x)\n",
162 cfh->ino, cfh->parent_ino, cfh->parent_name_hash); 168 cfh->ino, cfh->parent_ino, cfh->parent_name_hash);
163 169
@@ -207,9 +213,11 @@ static struct dentry *ceph_fh_to_dentry(struct super_block *sb, struct fid *fid,
207 int fh_len, int fh_type) 213 int fh_len, int fh_type)
208{ 214{
209 if (fh_type == 1) 215 if (fh_type == 1)
210 return __fh_to_dentry(sb, (struct ceph_nfs_fh *)fid->raw); 216 return __fh_to_dentry(sb, (struct ceph_nfs_fh *)fid->raw,
217 fh_len);
211 else 218 else
212 return __cfh_to_dentry(sb, (struct ceph_nfs_confh *)fid->raw); 219 return __cfh_to_dentry(sb, (struct ceph_nfs_confh *)fid->raw,
220 fh_len);
213} 221}
214 222
215/* 223/*
@@ -230,6 +238,8 @@ static struct dentry *ceph_fh_to_parent(struct super_block *sb,
230 238
231 if (fh_type == 1) 239 if (fh_type == 1)
232 return ERR_PTR(-ESTALE); 240 return ERR_PTR(-ESTALE);
241 if (fh_len < sizeof(*cfh) / 4)
242 return ERR_PTR(-ESTALE);
233 243
234 pr_debug("fh_to_parent %llx/%d\n", cfh->parent_ino, 244 pr_debug("fh_to_parent %llx/%d\n", cfh->parent_ino,
235 cfh->parent_name_hash); 245 cfh->parent_name_hash);
diff --git a/fs/gfs2/export.c b/fs/gfs2/export.c
index e8ed6d4a6181..4767774a5f3e 100644
--- a/fs/gfs2/export.c
+++ b/fs/gfs2/export.c
@@ -161,6 +161,8 @@ static struct dentry *gfs2_fh_to_dentry(struct super_block *sb, struct fid *fid,
161 case GFS2_SMALL_FH_SIZE: 161 case GFS2_SMALL_FH_SIZE:
162 case GFS2_LARGE_FH_SIZE: 162 case GFS2_LARGE_FH_SIZE:
163 case GFS2_OLD_FH_SIZE: 163 case GFS2_OLD_FH_SIZE:
164 if (fh_len < GFS2_SMALL_FH_SIZE)
165 return NULL;
164 this.no_formal_ino = ((u64)be32_to_cpu(fh[0])) << 32; 166 this.no_formal_ino = ((u64)be32_to_cpu(fh[0])) << 32;
165 this.no_formal_ino |= be32_to_cpu(fh[1]); 167 this.no_formal_ino |= be32_to_cpu(fh[1]);
166 this.no_addr = ((u64)be32_to_cpu(fh[2])) << 32; 168 this.no_addr = ((u64)be32_to_cpu(fh[2])) << 32;
@@ -180,6 +182,8 @@ static struct dentry *gfs2_fh_to_parent(struct super_block *sb, struct fid *fid,
180 switch (fh_type) { 182 switch (fh_type) {
181 case GFS2_LARGE_FH_SIZE: 183 case GFS2_LARGE_FH_SIZE:
182 case GFS2_OLD_FH_SIZE: 184 case GFS2_OLD_FH_SIZE:
185 if (fh_len < GFS2_LARGE_FH_SIZE)
186 return NULL;
183 parent.no_formal_ino = ((u64)be32_to_cpu(fh[4])) << 32; 187 parent.no_formal_ino = ((u64)be32_to_cpu(fh[4])) << 32;
184 parent.no_formal_ino |= be32_to_cpu(fh[5]); 188 parent.no_formal_ino |= be32_to_cpu(fh[5]);
185 parent.no_addr = ((u64)be32_to_cpu(fh[6])) << 32; 189 parent.no_addr = ((u64)be32_to_cpu(fh[6])) << 32;
diff --git a/fs/isofs/export.c b/fs/isofs/export.c
index 1d3804492aa7..2b4f2358eadb 100644
--- a/fs/isofs/export.c
+++ b/fs/isofs/export.c
@@ -175,7 +175,7 @@ static struct dentry *isofs_fh_to_parent(struct super_block *sb,
175{ 175{
176 struct isofs_fid *ifid = (struct isofs_fid *)fid; 176 struct isofs_fid *ifid = (struct isofs_fid *)fid;
177 177
178 if (fh_type != 2) 178 if (fh_len < 2 || fh_type != 2)
179 return NULL; 179 return NULL;
180 180
181 return isofs_export_iget(sb, 181 return isofs_export_iget(sb,
diff --git a/fs/reiserfs/inode.c b/fs/reiserfs/inode.c
index 46485557cdc6..f27f01a98aa2 100644
--- a/fs/reiserfs/inode.c
+++ b/fs/reiserfs/inode.c
@@ -1573,8 +1573,10 @@ struct dentry *reiserfs_fh_to_dentry(struct super_block *sb, struct fid *fid,
1573 reiserfs_warning(sb, "reiserfs-13077", 1573 reiserfs_warning(sb, "reiserfs-13077",
1574 "nfsd/reiserfs, fhtype=%d, len=%d - odd", 1574 "nfsd/reiserfs, fhtype=%d, len=%d - odd",
1575 fh_type, fh_len); 1575 fh_type, fh_len);
1576 fh_type = 5; 1576 fh_type = fh_len;
1577 } 1577 }
1578 if (fh_len < 2)
1579 return NULL;
1578 1580
1579 return reiserfs_get_dentry(sb, fid->raw[0], fid->raw[1], 1581 return reiserfs_get_dentry(sb, fid->raw[0], fid->raw[1],
1580 (fh_type == 3 || fh_type >= 5) ? fid->raw[2] : 0); 1582 (fh_type == 3 || fh_type >= 5) ? fid->raw[2] : 0);
@@ -1583,6 +1585,8 @@ struct dentry *reiserfs_fh_to_dentry(struct super_block *sb, struct fid *fid,
1583struct dentry *reiserfs_fh_to_parent(struct super_block *sb, struct fid *fid, 1585struct dentry *reiserfs_fh_to_parent(struct super_block *sb, struct fid *fid,
1584 int fh_len, int fh_type) 1586 int fh_len, int fh_type)
1585{ 1587{
1588 if (fh_type > fh_len)
1589 fh_type = fh_len;
1586 if (fh_type < 4) 1590 if (fh_type < 4)
1587 return NULL; 1591 return NULL;
1588 1592
diff --git a/fs/xfs/xfs_export.c b/fs/xfs/xfs_export.c
index 42679223a0fd..8c6d1d70278c 100644
--- a/fs/xfs/xfs_export.c
+++ b/fs/xfs/xfs_export.c
@@ -189,6 +189,9 @@ xfs_fs_fh_to_parent(struct super_block *sb, struct fid *fid,
189 struct xfs_fid64 *fid64 = (struct xfs_fid64 *)fid; 189 struct xfs_fid64 *fid64 = (struct xfs_fid64 *)fid;
190 struct inode *inode = NULL; 190 struct inode *inode = NULL;
191 191
192 if (fh_len < xfs_fileid_length(fileid_type))
193 return NULL;
194
192 switch (fileid_type) { 195 switch (fileid_type) {
193 case FILEID_INO32_GEN_PARENT: 196 case FILEID_INO32_GEN_PARENT:
194 inode = xfs_nfs_get_inode(sb, fid->i32.parent_ino, 197 inode = xfs_nfs_get_inode(sb, fid->i32.parent_ino,
diff --git a/mm/shmem.c b/mm/shmem.c
index cc12072f8787..67afba5117f2 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -2220,12 +2220,14 @@ static struct dentry *shmem_fh_to_dentry(struct super_block *sb,
2220{ 2220{
2221 struct inode *inode; 2221 struct inode *inode;
2222 struct dentry *dentry = NULL; 2222 struct dentry *dentry = NULL;
2223 u64 inum = fid->raw[2]; 2223 u64 inum;
2224 inum = (inum << 32) | fid->raw[1];
2225 2224
2226 if (fh_len < 3) 2225 if (fh_len < 3)
2227 return NULL; 2226 return NULL;
2228 2227
2228 inum = fid->raw[2];
2229 inum = (inum << 32) | fid->raw[1];
2230
2229 inode = ilookup5(sb, (unsigned long)(inum + fid->raw[0]), 2231 inode = ilookup5(sb, (unsigned long)(inum + fid->raw[0]),
2230 shmem_match, fid->raw); 2232 shmem_match, fid->raw);
2231 if (inode) { 2233 if (inode) {