2012-08-23 14:43:24 +04:00
# include <linux/sched.h>
# include <linux/errno.h>
# include <linux/dcache.h>
# include <linux/path.h>
# include <linux/fdtable.h>
# include <linux/namei.h>
# include <linux/pid.h>
# include <linux/security.h>
2012-08-26 18:28:20 +04:00
# include <linux/file.h>
# include <linux/seq_file.h>
2015-04-16 12:49:38 -07:00
# include <linux/fs.h>
2012-08-23 14:43:24 +04:00
# include <linux/proc_fs.h>
2014-04-07 15:38:34 -07:00
# include "../mount.h"
2012-08-23 14:43:24 +04:00
# include "internal.h"
# include "fd.h"
2012-08-26 18:28:20 +04:00
static int seq_show ( struct seq_file * m , void * v )
2012-08-23 14:43:24 +04:00
{
struct files_struct * files = NULL ;
2012-08-26 18:28:20 +04:00
int f_flags = 0 , ret = - ENOENT ;
struct file * file = NULL ;
struct task_struct * task ;
task = get_proc_task ( m - > private ) ;
if ( ! task )
return - ENOENT ;
files = get_files_struct ( task ) ;
put_task_struct ( task ) ;
2012-08-23 14:43:24 +04:00
if ( files ) {
2012-08-26 18:28:20 +04:00
int fd = proc_fd ( m - > private ) ;
2012-08-23 14:43:24 +04:00
spin_lock ( & files - > file_lock ) ;
file = fcheck_files ( files , fd ) ;
if ( file ) {
2012-08-26 18:28:20 +04:00
struct fdtable * fdt = files_fdtable ( files ) ;
2012-08-23 14:43:24 +04:00
2012-08-26 11:01:04 -04:00
f_flags = file - > f_flags ;
2012-08-23 14:43:24 +04:00
if ( close_on_exec ( fd , fdt ) )
f_flags | = O_CLOEXEC ;
2012-08-26 18:28:20 +04:00
get_file ( file ) ;
ret = 0 ;
2012-08-23 14:43:24 +04:00
}
spin_unlock ( & files - > file_lock ) ;
put_files_struct ( files ) ;
}
2012-08-26 18:28:20 +04:00
2015-04-16 12:49:38 -07:00
if ( ret )
return ret ;
seq_printf ( m , " pos: \t %lli \n flags: \t 0%o \n mnt_id: \t %i \n " ,
( long long ) file - > f_pos , f_flags ,
real_mount ( file - > f_path . mnt ) - > mnt_id ) ;
show_fd_locks ( m , file , files ) ;
if ( seq_has_overflowed ( m ) )
goto out ;
if ( file - > f_op - > show_fdinfo )
file - > f_op - > show_fdinfo ( m , file ) ;
2012-08-26 18:28:20 +04:00
2015-04-16 12:49:38 -07:00
out :
fput ( file ) ;
return 0 ;
2012-08-23 14:43:24 +04:00
}
2012-08-26 18:28:20 +04:00
static int seq_fdinfo_open ( struct inode * inode , struct file * file )
{
return single_open ( file , seq_show , inode ) ;
}
static const struct file_operations proc_fdinfo_file_operations = {
. open = seq_fdinfo_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2012-08-23 14:43:24 +04:00
static int tid_fd_revalidate ( struct dentry * dentry , unsigned int flags )
{
struct files_struct * files ;
struct task_struct * task ;
const struct cred * cred ;
struct inode * inode ;
int fd ;
if ( flags & LOOKUP_RCU )
return - ECHILD ;
2015-03-17 22:25:59 +00:00
inode = d_inode ( dentry ) ;
2012-08-23 14:43:24 +04:00
task = get_proc_task ( inode ) ;
fd = proc_fd ( inode ) ;
if ( task ) {
files = get_files_struct ( task ) ;
if ( files ) {
struct file * file ;
rcu_read_lock ( ) ;
file = fcheck_files ( files , fd ) ;
if ( file ) {
unsigned f_mode = file - > f_mode ;
rcu_read_unlock ( ) ;
put_files_struct ( files ) ;
if ( task_dumpable ( task ) ) {
rcu_read_lock ( ) ;
cred = __task_cred ( task ) ;
inode - > i_uid = cred - > euid ;
inode - > i_gid = cred - > egid ;
rcu_read_unlock ( ) ;
} else {
inode - > i_uid = GLOBAL_ROOT_UID ;
inode - > i_gid = GLOBAL_ROOT_GID ;
}
if ( S_ISLNK ( inode - > i_mode ) ) {
unsigned i_mode = S_IFLNK ;
if ( f_mode & FMODE_READ )
i_mode | = S_IRUSR | S_IXUSR ;
if ( f_mode & FMODE_WRITE )
i_mode | = S_IWUSR | S_IXUSR ;
inode - > i_mode = i_mode ;
}
security_task_to_inode ( task , inode ) ;
put_task_struct ( task ) ;
return 1 ;
}
rcu_read_unlock ( ) ;
put_files_struct ( files ) ;
}
put_task_struct ( task ) ;
}
return 0 ;
}
static const struct dentry_operations tid_fd_dentry_operations = {
. d_revalidate = tid_fd_revalidate ,
. d_delete = pid_delete_dentry ,
} ;
static int proc_fd_link ( struct dentry * dentry , struct path * path )
{
2012-08-26 18:28:20 +04:00
struct files_struct * files = NULL ;
struct task_struct * task ;
int ret = - ENOENT ;
2015-03-17 22:25:59 +00:00
task = get_proc_task ( d_inode ( dentry ) ) ;
2012-08-26 18:28:20 +04:00
if ( task ) {
files = get_files_struct ( task ) ;
put_task_struct ( task ) ;
}
if ( files ) {
2015-03-17 22:25:59 +00:00
int fd = proc_fd ( d_inode ( dentry ) ) ;
2012-08-26 18:28:20 +04:00
struct file * fd_file ;
spin_lock ( & files - > file_lock ) ;
fd_file = fcheck_files ( files , fd ) ;
if ( fd_file ) {
* path = fd_file - > f_path ;
path_get ( & fd_file - > f_path ) ;
ret = 0 ;
}
spin_unlock ( & files - > file_lock ) ;
put_files_struct ( files ) ;
}
return ret ;
2012-08-23 14:43:24 +04:00
}
2013-06-15 11:15:20 +04:00
static int
2012-08-23 14:43:24 +04:00
proc_fd_instantiate ( struct inode * dir , struct dentry * dentry ,
struct task_struct * task , const void * ptr )
{
unsigned fd = ( unsigned long ) ptr ;
struct proc_inode * ei ;
struct inode * inode ;
inode = proc_pid_make_inode ( dir - > i_sb , task ) ;
if ( ! inode )
goto out ;
ei = PROC_I ( inode ) ;
ei - > fd = fd ;
inode - > i_mode = S_IFLNK ;
inode - > i_op = & proc_pid_link_inode_operations ;
inode - > i_size = 64 ;
ei - > op . proc_get_link = proc_fd_link ;
d_set_d_op ( dentry , & tid_fd_dentry_operations ) ;
d_add ( dentry , inode ) ;
/* Close the race of the process dying before we return the dentry */
if ( tid_fd_revalidate ( dentry , 0 ) )
2013-06-15 11:15:20 +04:00
return 0 ;
2012-08-23 14:43:24 +04:00
out :
2013-06-15 11:15:20 +04:00
return - ENOENT ;
2012-08-23 14:43:24 +04:00
}
static struct dentry * proc_lookupfd_common ( struct inode * dir ,
struct dentry * dentry ,
instantiate_t instantiate )
{
struct task_struct * task = get_proc_task ( dir ) ;
2013-06-15 11:15:20 +04:00
int result = - ENOENT ;
2014-08-08 14:21:25 -07:00
unsigned fd = name_to_int ( & dentry - > d_name ) ;
2012-08-23 14:43:24 +04:00
if ( ! task )
goto out_no_task ;
if ( fd = = ~ 0U )
goto out ;
result = instantiate ( dir , dentry , task , ( void * ) ( unsigned long ) fd ) ;
out :
put_task_struct ( task ) ;
out_no_task :
2013-06-15 11:15:20 +04:00
return ERR_PTR ( result ) ;
2012-08-23 14:43:24 +04:00
}
2013-05-16 12:07:31 -04:00
static int proc_readfd_common ( struct file * file , struct dir_context * ctx ,
instantiate_t instantiate )
2012-08-23 14:43:24 +04:00
{
2013-05-16 12:07:31 -04:00
struct task_struct * p = get_proc_task ( file_inode ( file ) ) ;
2012-08-23 14:43:24 +04:00
struct files_struct * files ;
2013-05-16 12:07:31 -04:00
unsigned int fd ;
2012-08-23 14:43:24 +04:00
if ( ! p )
2013-05-16 12:07:31 -04:00
return - ENOENT ;
2012-08-23 14:43:24 +04:00
2013-05-16 12:07:31 -04:00
if ( ! dir_emit_dots ( file , ctx ) )
goto out ;
files = get_files_struct ( p ) ;
if ( ! files )
goto out ;
rcu_read_lock ( ) ;
for ( fd = ctx - > pos - 2 ;
fd < files_fdtable ( files ) - > max_fds ;
fd + + , ctx - > pos + + ) {
char name [ PROC_NUMBUF ] ;
int len ;
if ( ! fcheck_files ( files , fd ) )
continue ;
rcu_read_unlock ( ) ;
len = snprintf ( name , sizeof ( name ) , " %d " , fd ) ;
if ( ! proc_fill_cache ( file , ctx ,
name , len , instantiate , p ,
( void * ) ( unsigned long ) fd ) )
goto out_fd_loop ;
rcu_read_lock ( ) ;
2012-08-23 14:43:24 +04:00
}
2013-05-16 12:07:31 -04:00
rcu_read_unlock ( ) ;
out_fd_loop :
put_files_struct ( files ) ;
2012-08-23 14:43:24 +04:00
out :
put_task_struct ( p ) ;
2013-05-16 12:07:31 -04:00
return 0 ;
2012-08-23 14:43:24 +04:00
}
2013-05-16 12:07:31 -04:00
static int proc_readfd ( struct file * file , struct dir_context * ctx )
2012-08-23 14:43:24 +04:00
{
2013-05-16 12:07:31 -04:00
return proc_readfd_common ( file , ctx , proc_fd_instantiate ) ;
2012-08-23 14:43:24 +04:00
}
const struct file_operations proc_fd_operations = {
. read = generic_read_dir ,
2013-05-16 12:07:31 -04:00
. iterate = proc_readfd ,
2012-08-23 14:43:24 +04:00
. llseek = default_llseek ,
} ;
static struct dentry * proc_lookupfd ( struct inode * dir , struct dentry * dentry ,
unsigned int flags )
{
return proc_lookupfd_common ( dir , dentry , proc_fd_instantiate ) ;
}
/*
* / proc / pid / fd needs a special permission handler so that a process can still
* access / proc / self / fd after it has executed a setuid ( ) .
*/
int proc_fd_permission ( struct inode * inode , int mask )
{
int rv = generic_permission ( inode , mask ) ;
if ( rv = = 0 )
return 0 ;
proc: make proc_fd_permission() thread-friendly
proc_fd_permission() says "process can still access /proc/self/fd after it
has executed a setuid()", but the "task_pid() = proc_pid() check only
helps if the task is group leader, /proc/self points to
/proc/<leader-pid>.
Change this check to use task_tgid() so that the whole thread group can
access its /proc/self/fd or /proc/<tid-of-sub-thread>/fd.
Notes:
- CLONE_THREAD does not require CLONE_FILES so task->files
can differ, but I don't think this can lead to any security
problem. And this matches same_thread_group() in
__ptrace_may_access().
- /proc/self should probably point to /proc/<thread-tid>, but
it is too late to change the rules. Perhaps it makes sense
to add /proc/thread though.
Test-case:
void *tfunc(void *arg)
{
assert(opendir("/proc/self/fd"));
return NULL;
}
int main(void)
{
pthread_t t;
pthread_create(&t, NULL, tfunc, NULL);
pthread_join(t, NULL);
return 0;
}
fails if, say, this executable is not readable and suid_dumpable = 0.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-09-11 14:24:37 -07:00
if ( task_tgid ( current ) = = proc_pid ( inode ) )
2012-08-23 14:43:24 +04:00
rv = 0 ;
return rv ;
}
const struct inode_operations proc_fd_inode_operations = {
. lookup = proc_lookupfd ,
. permission = proc_fd_permission ,
. setattr = proc_setattr ,
} ;
2013-06-15 11:15:20 +04:00
static int
2012-08-23 14:43:24 +04:00
proc_fdinfo_instantiate ( struct inode * dir , struct dentry * dentry ,
struct task_struct * task , const void * ptr )
{
unsigned fd = ( unsigned long ) ptr ;
struct proc_inode * ei ;
struct inode * inode ;
inode = proc_pid_make_inode ( dir - > i_sb , task ) ;
if ( ! inode )
goto out ;
ei = PROC_I ( inode ) ;
ei - > fd = fd ;
inode - > i_mode = S_IFREG | S_IRUSR ;
inode - > i_fop = & proc_fdinfo_file_operations ;
d_set_d_op ( dentry , & tid_fd_dentry_operations ) ;
d_add ( dentry , inode ) ;
/* Close the race of the process dying before we return the dentry */
if ( tid_fd_revalidate ( dentry , 0 ) )
2013-06-15 11:15:20 +04:00
return 0 ;
2012-08-23 14:43:24 +04:00
out :
2013-06-15 11:15:20 +04:00
return - ENOENT ;
2012-08-23 14:43:24 +04:00
}
static struct dentry *
proc_lookupfdinfo ( struct inode * dir , struct dentry * dentry , unsigned int flags )
{
return proc_lookupfd_common ( dir , dentry , proc_fdinfo_instantiate ) ;
}
2013-05-16 12:07:31 -04:00
static int proc_readfdinfo ( struct file * file , struct dir_context * ctx )
2012-08-23 14:43:24 +04:00
{
2013-05-16 12:07:31 -04:00
return proc_readfd_common ( file , ctx ,
2012-08-23 14:43:24 +04:00
proc_fdinfo_instantiate ) ;
}
const struct inode_operations proc_fdinfo_inode_operations = {
. lookup = proc_lookupfdinfo ,
. setattr = proc_setattr ,
} ;
const struct file_operations proc_fdinfo_operations = {
. read = generic_read_dir ,
2013-05-16 12:07:31 -04:00
. iterate = proc_readfdinfo ,
2012-08-23 14:43:24 +04:00
. llseek = default_llseek ,
} ;