2006-01-16 19:50:04 +03:00
/*
* Copyright ( C ) Sistina Software , Inc . 1997 - 2003 All rights reserved .
2006-05-18 23:09:15 +04:00
* Copyright ( C ) 2004 - 2006 Red Hat , Inc . All rights reserved .
2006-01-16 19:50:04 +03:00
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
2006-09-01 19:05:15 +04:00
* of the GNU General Public License version 2.
2006-01-16 19:50:04 +03:00
*/
# include <linux/spinlock.h>
# include <linux/completion.h>
# include <linux/buffer_head.h>
2007-07-17 15:04:28 +04:00
# include <linux/exportfs.h>
2006-02-28 01:23:27 +03:00
# include <linux/gfs2_ondisk.h>
2006-03-28 23:14:04 +04:00
# include <linux/crc32.h>
2006-01-16 19:50:04 +03:00
# include "gfs2.h"
2006-02-28 01:23:27 +03:00
# include "incore.h"
2006-01-16 19:50:04 +03:00
# include "dir.h"
# include "glock.h"
# include "glops.h"
# include "inode.h"
2008-10-14 19:05:55 +04:00
# include "super.h"
2006-01-16 19:50:04 +03:00
# include "rgrp.h"
2006-03-20 20:30:04 +03:00
# include "util.h"
2006-01-16 19:50:04 +03:00
2007-06-01 17:11:58 +04:00
# define GFS2_SMALL_FH_SIZE 4
2007-06-28 01:07:53 +04:00
# define GFS2_LARGE_FH_SIZE 8
2007-07-10 15:28:27 +04:00
# define GFS2_OLD_FH_SIZE 10
2007-06-01 17:11:58 +04:00
2012-04-02 22:34:06 +04:00
static int gfs2_encode_fh ( struct inode * inode , __u32 * p , int * len ,
struct inode * parent )
2006-01-16 19:50:04 +03:00
{
2006-10-14 18:46:30 +04:00
__be32 * fh = ( __force __be32 * ) p ;
2006-03-01 23:31:02 +03:00
struct super_block * sb = inode - > i_sb ;
2006-06-14 23:32:57 +04:00
struct gfs2_inode * ip = GFS2_I ( inode ) ;
2006-01-16 19:50:04 +03:00
2012-04-02 22:34:06 +04:00
if ( parent & & ( * len < GFS2_LARGE_FH_SIZE ) ) {
2011-01-29 16:13:25 +03:00
* len = GFS2_LARGE_FH_SIZE ;
2013-02-17 10:48:11 +04:00
return FILEID_INVALID ;
2011-01-29 16:13:25 +03:00
} else if ( * len < GFS2_SMALL_FH_SIZE ) {
* len = GFS2_SMALL_FH_SIZE ;
2013-02-17 10:48:11 +04:00
return FILEID_INVALID ;
2011-01-29 16:13:25 +03:00
}
2006-01-16 19:50:04 +03:00
2007-05-15 18:37:50 +04:00
fh [ 0 ] = cpu_to_be32 ( ip - > i_no_formal_ino > > 32 ) ;
fh [ 1 ] = cpu_to_be32 ( ip - > i_no_formal_ino & 0xFFFFFFFF ) ;
fh [ 2 ] = cpu_to_be32 ( ip - > i_no_addr > > 32 ) ;
fh [ 3 ] = cpu_to_be32 ( ip - > i_no_addr & 0xFFFFFFFF ) ;
2006-09-05 00:16:45 +04:00
* len = GFS2_SMALL_FH_SIZE ;
2006-01-16 19:50:04 +03:00
2015-03-18 01:25:59 +03:00
if ( ! parent | | inode = = d_inode ( sb - > s_root ) )
2006-01-16 19:50:04 +03:00
return * len ;
2012-04-02 22:34:06 +04:00
ip = GFS2_I ( parent ) ;
2006-01-16 19:50:04 +03:00
2007-05-15 18:37:50 +04:00
fh [ 4 ] = cpu_to_be32 ( ip - > i_no_formal_ino > > 32 ) ;
fh [ 5 ] = cpu_to_be32 ( ip - > i_no_formal_ino & 0xFFFFFFFF ) ;
fh [ 6 ] = cpu_to_be32 ( ip - > i_no_addr > > 32 ) ;
fh [ 7 ] = cpu_to_be32 ( ip - > i_no_addr & 0xFFFFFFFF ) ;
2006-09-05 00:16:45 +04:00
* len = GFS2_LARGE_FH_SIZE ;
2006-01-16 19:50:04 +03:00
return * len ;
}
struct get_name_filldir {
2013-05-16 22:14:48 +04:00
struct dir_context ctx ;
2006-10-14 06:51:24 +04:00
struct gfs2_inum_host inum ;
2006-01-16 19:50:04 +03:00
char * name ;
} ;
2014-10-30 19:37:34 +03:00
static int get_name_filldir ( struct dir_context * ctx , const char * name ,
int length , loff_t offset , u64 inum ,
unsigned int type )
2006-01-16 19:50:04 +03:00
{
2014-10-30 19:37:34 +03:00
struct get_name_filldir * gnfd =
container_of ( ctx , struct get_name_filldir , ctx ) ;
2006-01-16 19:50:04 +03:00
2007-01-17 18:09:20 +03:00
if ( inum ! = gnfd - > inum . no_addr )
2006-01-16 19:50:04 +03:00
return 0 ;
memcpy ( gnfd - > name , name , length ) ;
gnfd - > name [ length ] = 0 ;
return 1 ;
}
static int gfs2_get_name ( struct dentry * parent , char * name ,
struct dentry * child )
{
2015-03-18 01:25:59 +03:00
struct inode * dir = d_inode ( parent ) ;
struct inode * inode = d_inode ( child ) ;
2006-01-16 19:50:04 +03:00
struct gfs2_inode * dip , * ip ;
2013-05-23 06:22:04 +04:00
struct get_name_filldir gnfd = {
. ctx . actor = get_name_filldir ,
. name = name
} ;
2006-01-16 19:50:04 +03:00
struct gfs2_holder gh ;
int error ;
2011-10-27 20:16:06 +04:00
struct file_ra_state f_ra = { . start = 0 } ;
2006-01-16 19:50:04 +03:00
if ( ! dir )
return - EINVAL ;
if ( ! S_ISDIR ( dir - > i_mode ) | | ! inode )
return - EINVAL ;
2006-06-14 23:32:57 +04:00
dip = GFS2_I ( dir ) ;
ip = GFS2_I ( inode ) ;
2006-01-16 19:50:04 +03:00
* name = 0 ;
2007-05-15 18:37:50 +04:00
gnfd . inum . no_addr = ip - > i_no_addr ;
gnfd . inum . no_formal_ino = ip - > i_no_formal_ino ;
2006-01-16 19:50:04 +03:00
error = gfs2_glock_nq_init ( dip - > i_gl , LM_ST_SHARED , 0 , & gh ) ;
if ( error )
return error ;
2013-05-16 22:14:48 +04:00
error = gfs2_dir_read ( dir , & gnfd . ctx , & f_ra ) ;
2006-01-16 19:50:04 +03:00
gfs2_glock_dq_uninit ( & gh ) ;
if ( ! error & & ! * name )
error = - ENOENT ;
return error ;
}
static struct dentry * gfs2_get_parent ( struct dentry * child )
{
2015-03-18 01:25:59 +03:00
return d_obtain_alias ( gfs2_lookupi ( d_inode ( child ) , & gfs2_qdotdot , 1 ) ) ;
2006-01-16 19:50:04 +03:00
}
2007-10-22 03:42:14 +04:00
static struct dentry * gfs2_get_dentry ( struct super_block * sb ,
2009-09-08 21:00:30 +04:00
struct gfs2_inum_host * inum )
2006-01-16 19:50:04 +03:00
{
2006-02-28 01:23:27 +03:00
struct gfs2_sbd * sdp = sb - > s_fs_info ;
2006-01-16 19:50:04 +03:00
struct inode * inode ;
2010-11-03 23:01:07 +03:00
inode = gfs2_lookup_by_inum ( sdp , inum - > no_addr , & inum - > no_formal_ino ,
GFS2_BLKST_DINODE ) ;
if ( IS_ERR ( inode ) )
return ERR_CAST ( inode ) ;
2010-12-18 20:06:56 +03:00
return d_obtain_alias ( inode ) ;
2006-01-16 19:50:04 +03:00
}
2007-10-22 03:42:14 +04:00
static struct dentry * gfs2_fh_to_dentry ( struct super_block * sb , struct fid * fid ,
int fh_len , int fh_type )
{
struct gfs2_inum_host this ;
__be32 * fh = ( __force __be32 * ) fid - > raw ;
switch ( fh_type ) {
case GFS2_SMALL_FH_SIZE :
case GFS2_LARGE_FH_SIZE :
case GFS2_OLD_FH_SIZE :
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>
2012-10-08 07:32:51 +04:00
if ( fh_len < GFS2_SMALL_FH_SIZE )
return NULL ;
2007-10-22 03:42:14 +04:00
this . no_formal_ino = ( ( u64 ) be32_to_cpu ( fh [ 0 ] ) ) < < 32 ;
this . no_formal_ino | = be32_to_cpu ( fh [ 1 ] ) ;
this . no_addr = ( ( u64 ) be32_to_cpu ( fh [ 2 ] ) ) < < 32 ;
this . no_addr | = be32_to_cpu ( fh [ 3 ] ) ;
return gfs2_get_dentry ( sb , & this ) ;
default :
return NULL ;
}
}
static struct dentry * gfs2_fh_to_parent ( struct super_block * sb , struct fid * fid ,
int fh_len , int fh_type )
{
struct gfs2_inum_host parent ;
__be32 * fh = ( __force __be32 * ) fid - > raw ;
switch ( fh_type ) {
case GFS2_LARGE_FH_SIZE :
case GFS2_OLD_FH_SIZE :
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>
2012-10-08 07:32:51 +04:00
if ( fh_len < GFS2_LARGE_FH_SIZE )
return NULL ;
2007-10-22 03:42:14 +04:00
parent . no_formal_ino = ( ( u64 ) be32_to_cpu ( fh [ 4 ] ) ) < < 32 ;
parent . no_formal_ino | = be32_to_cpu ( fh [ 5 ] ) ;
parent . no_addr = ( ( u64 ) be32_to_cpu ( fh [ 6 ] ) ) < < 32 ;
parent . no_addr | = be32_to_cpu ( fh [ 7 ] ) ;
return gfs2_get_dentry ( sb , & parent ) ;
default :
return NULL ;
}
}
2007-10-22 03:42:17 +04:00
const struct export_operations gfs2_export_ops = {
2006-01-16 19:50:04 +03:00
. encode_fh = gfs2_encode_fh ,
2007-10-22 03:42:14 +04:00
. fh_to_dentry = gfs2_fh_to_dentry ,
. fh_to_parent = gfs2_fh_to_parent ,
2006-01-16 19:50:04 +03:00
. get_name = gfs2_get_name ,
. get_parent = gfs2_get_parent ,
} ;