2005-04-17 02:20:36 +04:00
/*
* linux / fs / proc / inode . c
*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
*/
# include <linux/time.h>
# include <linux/proc_fs.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/string.h>
# include <linux/stat.h>
Fix rmmod/read/write races in /proc entries
Fix following races:
===========================================
1. Write via ->write_proc sleeps in copy_from_user(). Module disappears
meanwhile. Or, more generically, system call done on /proc file, method
supplied by module is called, module dissapeares meanwhile.
pde = create_proc_entry()
if (!pde)
return -ENOMEM;
pde->write_proc = ...
open
write
copy_from_user
pde = create_proc_entry();
if (!pde) {
remove_proc_entry();
return -ENOMEM;
/* module unloaded */
}
*boom*
==========================================
2. bogo-revoke aka proc_kill_inodes()
remove_proc_entry vfs_read
proc_kill_inodes [check ->f_op validness]
[check ->f_op->read validness]
[verify_area, security permissions checks]
->f_op = NULL;
if (file->f_op->read)
/* ->f_op dereference, boom */
NOTE, NOTE, NOTE: file_operations are proxied for regular files only. Let's
see how this scheme behaves, then extend if needed for directories.
Directories creators in /proc only set ->owner for them, so proxying for
directories may be unneeded.
NOTE, NOTE, NOTE: methods being proxied are ->llseek, ->read, ->write,
->poll, ->unlocked_ioctl, ->ioctl, ->compat_ioctl, ->open, ->release.
If your in-tree module uses something else, yell on me. Full audit pending.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-16 10:39:00 +04:00
# include <linux/completion.h>
2007-09-12 02:23:55 +04:00
# include <linux/poll.h>
2005-04-17 02:20:36 +04:00
# include <linux/file.h>
# include <linux/limits.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/smp_lock.h>
# include <asm/system.h>
# include <asm/uaccess.h>
2006-01-08 12:04:16 +03:00
# include "internal.h"
2005-04-17 02:20:36 +04:00
2007-05-08 11:25:45 +04:00
struct proc_dir_entry * de_get ( struct proc_dir_entry * de )
2005-04-17 02:20:36 +04:00
{
2008-04-29 12:01:41 +04:00
atomic_inc ( & de - > count ) ;
2005-04-17 02:20:36 +04:00
return de ;
}
/*
* Decrements the use count and checks for deferred deletion .
*/
2007-05-08 11:25:45 +04:00
void de_put ( struct proc_dir_entry * de )
2005-04-17 02:20:36 +04:00
{
2008-04-29 12:01:41 +04:00
lock_kernel ( ) ;
if ( ! atomic_read ( & de - > count ) ) {
printk ( " de_put: entry %s already free! \n " , de - > name ) ;
2005-04-17 02:20:36 +04:00
unlock_kernel ( ) ;
2008-04-29 12:01:41 +04:00
return ;
2005-04-17 02:20:36 +04:00
}
2008-04-29 12:01:41 +04:00
if ( atomic_dec_and_test ( & de - > count ) )
free_proc_entry ( de ) ;
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
}
/*
* Decrement the use count of the proc_dir_entry .
*/
static void proc_delete_inode ( struct inode * inode )
{
struct proc_dir_entry * de ;
2005-09-10 00:01:31 +04:00
truncate_inode_pages ( & inode - > i_data , 0 ) ;
2006-06-26 11:25:55 +04:00
/* Stop tracking associated processes */
2006-06-26 11:25:56 +04:00
put_pid ( PROC_I ( inode ) - > pid ) ;
2005-04-17 02:20:36 +04:00
/* Let go of any associated proc directory entry */
de = PROC_I ( inode ) - > pde ;
if ( de ) {
if ( de - > owner )
module_put ( de - > owner ) ;
de_put ( de ) ;
}
clear_inode ( inode ) ;
}
struct vfsmount * proc_mnt ;
2006-12-07 07:33:20 +03:00
static struct kmem_cache * proc_inode_cachep ;
2005-04-17 02:20:36 +04:00
static struct inode * proc_alloc_inode ( struct super_block * sb )
{
struct proc_inode * ei ;
struct inode * inode ;
2006-12-07 07:33:17 +03:00
ei = ( struct proc_inode * ) kmem_cache_alloc ( proc_inode_cachep , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! ei )
return NULL ;
2006-06-26 11:25:56 +04:00
ei - > pid = NULL ;
2006-06-26 11:25:44 +04:00
ei - > fd = 0 ;
2005-04-17 02:20:36 +04:00
ei - > op . proc_get_link = NULL ;
ei - > pde = NULL ;
inode = & ei - > vfs_inode ;
inode - > i_mtime = inode - > i_atime = inode - > i_ctime = CURRENT_TIME ;
return inode ;
}
static void proc_destroy_inode ( struct inode * inode )
{
kmem_cache_free ( proc_inode_cachep , PROC_I ( inode ) ) ;
}
2007-10-17 10:25:51 +04:00
static void init_once ( struct kmem_cache * cachep , void * foo )
2005-04-17 02:20:36 +04:00
{
struct proc_inode * ei = ( struct proc_inode * ) foo ;
2007-05-17 09:10:57 +04:00
inode_init_once ( & ei - > vfs_inode ) ;
2005-04-17 02:20:36 +04:00
}
2007-07-20 05:11:58 +04:00
2005-04-17 02:20:36 +04:00
int __init proc_init_inodecache ( void )
{
proc_inode_cachep = kmem_cache_create ( " proc_inode_cache " ,
sizeof ( struct proc_inode ) ,
2006-03-24 14:16:06 +03:00
0 , ( SLAB_RECLAIM_ACCOUNT |
2007-10-17 10:26:10 +04:00
SLAB_MEM_SPREAD | SLAB_PANIC ) ,
2007-07-20 05:11:58 +04:00
init_once ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-02-12 11:55:41 +03:00
static const struct super_operations proc_sops = {
2005-04-17 02:20:36 +04:00
. alloc_inode = proc_alloc_inode ,
. destroy_inode = proc_destroy_inode ,
. drop_inode = generic_delete_inode ,
. delete_inode = proc_delete_inode ,
. statfs = simple_statfs ,
} ;
2008-07-25 12:48:29 +04:00
static void __pde_users_dec ( struct proc_dir_entry * pde )
Fix rmmod/read/write races in /proc entries
Fix following races:
===========================================
1. Write via ->write_proc sleeps in copy_from_user(). Module disappears
meanwhile. Or, more generically, system call done on /proc file, method
supplied by module is called, module dissapeares meanwhile.
pde = create_proc_entry()
if (!pde)
return -ENOMEM;
pde->write_proc = ...
open
write
copy_from_user
pde = create_proc_entry();
if (!pde) {
remove_proc_entry();
return -ENOMEM;
/* module unloaded */
}
*boom*
==========================================
2. bogo-revoke aka proc_kill_inodes()
remove_proc_entry vfs_read
proc_kill_inodes [check ->f_op validness]
[check ->f_op->read validness]
[verify_area, security permissions checks]
->f_op = NULL;
if (file->f_op->read)
/* ->f_op dereference, boom */
NOTE, NOTE, NOTE: file_operations are proxied for regular files only. Let's
see how this scheme behaves, then extend if needed for directories.
Directories creators in /proc only set ->owner for them, so proxying for
directories may be unneeded.
NOTE, NOTE, NOTE: methods being proxied are ->llseek, ->read, ->write,
->poll, ->unlocked_ioctl, ->ioctl, ->compat_ioctl, ->open, ->release.
If your in-tree module uses something else, yell on me. Full audit pending.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-16 10:39:00 +04:00
{
pde - > pde_users - - ;
if ( pde - > pde_unload_completion & & pde - > pde_users = = 0 )
complete ( pde - > pde_unload_completion ) ;
2008-07-25 12:48:29 +04:00
}
static void pde_users_dec ( struct proc_dir_entry * pde )
{
spin_lock ( & pde - > pde_unload_lock ) ;
__pde_users_dec ( pde ) ;
Fix rmmod/read/write races in /proc entries
Fix following races:
===========================================
1. Write via ->write_proc sleeps in copy_from_user(). Module disappears
meanwhile. Or, more generically, system call done on /proc file, method
supplied by module is called, module dissapeares meanwhile.
pde = create_proc_entry()
if (!pde)
return -ENOMEM;
pde->write_proc = ...
open
write
copy_from_user
pde = create_proc_entry();
if (!pde) {
remove_proc_entry();
return -ENOMEM;
/* module unloaded */
}
*boom*
==========================================
2. bogo-revoke aka proc_kill_inodes()
remove_proc_entry vfs_read
proc_kill_inodes [check ->f_op validness]
[check ->f_op->read validness]
[verify_area, security permissions checks]
->f_op = NULL;
if (file->f_op->read)
/* ->f_op dereference, boom */
NOTE, NOTE, NOTE: file_operations are proxied for regular files only. Let's
see how this scheme behaves, then extend if needed for directories.
Directories creators in /proc only set ->owner for them, so proxying for
directories may be unneeded.
NOTE, NOTE, NOTE: methods being proxied are ->llseek, ->read, ->write,
->poll, ->unlocked_ioctl, ->ioctl, ->compat_ioctl, ->open, ->release.
If your in-tree module uses something else, yell on me. Full audit pending.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-16 10:39:00 +04:00
spin_unlock ( & pde - > pde_unload_lock ) ;
}
static loff_t proc_reg_llseek ( struct file * file , loff_t offset , int whence )
{
struct proc_dir_entry * pde = PDE ( file - > f_path . dentry - > d_inode ) ;
loff_t rv = - EINVAL ;
loff_t ( * llseek ) ( struct file * , loff_t , int ) ;
spin_lock ( & pde - > pde_unload_lock ) ;
/*
* remove_proc_entry ( ) is going to delete PDE ( as part of module
* cleanup sequence ) . No new callers into module allowed .
*/
if ( ! pde - > proc_fops ) {
spin_unlock ( & pde - > pde_unload_lock ) ;
return rv ;
}
/*
* Bump refcount so that remove_proc_entry will wail for - > llseek to
* complete .
*/
pde - > pde_users + + ;
/*
* Save function pointer under lock , to protect against - > proc_fops
* NULL ' ifying right after - > pde_unload_lock is dropped .
*/
llseek = pde - > proc_fops - > llseek ;
spin_unlock ( & pde - > pde_unload_lock ) ;
if ( ! llseek )
llseek = default_llseek ;
rv = llseek ( file , offset , whence ) ;
pde_users_dec ( pde ) ;
return rv ;
}
static ssize_t proc_reg_read ( struct file * file , char __user * buf , size_t count , loff_t * ppos )
{
struct proc_dir_entry * pde = PDE ( file - > f_path . dentry - > d_inode ) ;
ssize_t rv = - EIO ;
ssize_t ( * read ) ( struct file * , char __user * , size_t , loff_t * ) ;
spin_lock ( & pde - > pde_unload_lock ) ;
if ( ! pde - > proc_fops ) {
spin_unlock ( & pde - > pde_unload_lock ) ;
return rv ;
}
pde - > pde_users + + ;
read = pde - > proc_fops - > read ;
spin_unlock ( & pde - > pde_unload_lock ) ;
if ( read )
rv = read ( file , buf , count , ppos ) ;
pde_users_dec ( pde ) ;
return rv ;
}
static ssize_t proc_reg_write ( struct file * file , const char __user * buf , size_t count , loff_t * ppos )
{
struct proc_dir_entry * pde = PDE ( file - > f_path . dentry - > d_inode ) ;
ssize_t rv = - EIO ;
ssize_t ( * write ) ( struct file * , const char __user * , size_t , loff_t * ) ;
spin_lock ( & pde - > pde_unload_lock ) ;
if ( ! pde - > proc_fops ) {
spin_unlock ( & pde - > pde_unload_lock ) ;
return rv ;
}
pde - > pde_users + + ;
write = pde - > proc_fops - > write ;
spin_unlock ( & pde - > pde_unload_lock ) ;
if ( write )
rv = write ( file , buf , count , ppos ) ;
pde_users_dec ( pde ) ;
return rv ;
}
static unsigned int proc_reg_poll ( struct file * file , struct poll_table_struct * pts )
{
struct proc_dir_entry * pde = PDE ( file - > f_path . dentry - > d_inode ) ;
2007-09-12 02:23:55 +04:00
unsigned int rv = DEFAULT_POLLMASK ;
Fix rmmod/read/write races in /proc entries
Fix following races:
===========================================
1. Write via ->write_proc sleeps in copy_from_user(). Module disappears
meanwhile. Or, more generically, system call done on /proc file, method
supplied by module is called, module dissapeares meanwhile.
pde = create_proc_entry()
if (!pde)
return -ENOMEM;
pde->write_proc = ...
open
write
copy_from_user
pde = create_proc_entry();
if (!pde) {
remove_proc_entry();
return -ENOMEM;
/* module unloaded */
}
*boom*
==========================================
2. bogo-revoke aka proc_kill_inodes()
remove_proc_entry vfs_read
proc_kill_inodes [check ->f_op validness]
[check ->f_op->read validness]
[verify_area, security permissions checks]
->f_op = NULL;
if (file->f_op->read)
/* ->f_op dereference, boom */
NOTE, NOTE, NOTE: file_operations are proxied for regular files only. Let's
see how this scheme behaves, then extend if needed for directories.
Directories creators in /proc only set ->owner for them, so proxying for
directories may be unneeded.
NOTE, NOTE, NOTE: methods being proxied are ->llseek, ->read, ->write,
->poll, ->unlocked_ioctl, ->ioctl, ->compat_ioctl, ->open, ->release.
If your in-tree module uses something else, yell on me. Full audit pending.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-16 10:39:00 +04:00
unsigned int ( * poll ) ( struct file * , struct poll_table_struct * ) ;
spin_lock ( & pde - > pde_unload_lock ) ;
if ( ! pde - > proc_fops ) {
spin_unlock ( & pde - > pde_unload_lock ) ;
return rv ;
}
pde - > pde_users + + ;
poll = pde - > proc_fops - > poll ;
spin_unlock ( & pde - > pde_unload_lock ) ;
if ( poll )
rv = poll ( file , pts ) ;
pde_users_dec ( pde ) ;
return rv ;
}
static long proc_reg_unlocked_ioctl ( struct file * file , unsigned int cmd , unsigned long arg )
{
struct proc_dir_entry * pde = PDE ( file - > f_path . dentry - > d_inode ) ;
long rv = - ENOTTY ;
long ( * unlocked_ioctl ) ( struct file * , unsigned int , unsigned long ) ;
int ( * ioctl ) ( struct inode * , struct file * , unsigned int , unsigned long ) ;
spin_lock ( & pde - > pde_unload_lock ) ;
if ( ! pde - > proc_fops ) {
spin_unlock ( & pde - > pde_unload_lock ) ;
return rv ;
}
pde - > pde_users + + ;
unlocked_ioctl = pde - > proc_fops - > unlocked_ioctl ;
ioctl = pde - > proc_fops - > ioctl ;
spin_unlock ( & pde - > pde_unload_lock ) ;
if ( unlocked_ioctl ) {
rv = unlocked_ioctl ( file , cmd , arg ) ;
if ( rv = = - ENOIOCTLCMD )
rv = - EINVAL ;
} else if ( ioctl ) {
lock_kernel ( ) ;
rv = ioctl ( file - > f_path . dentry - > d_inode , file , cmd , arg ) ;
unlock_kernel ( ) ;
}
pde_users_dec ( pde ) ;
return rv ;
}
# ifdef CONFIG_COMPAT
static long proc_reg_compat_ioctl ( struct file * file , unsigned int cmd , unsigned long arg )
{
struct proc_dir_entry * pde = PDE ( file - > f_path . dentry - > d_inode ) ;
long rv = - ENOTTY ;
long ( * compat_ioctl ) ( struct file * , unsigned int , unsigned long ) ;
spin_lock ( & pde - > pde_unload_lock ) ;
if ( ! pde - > proc_fops ) {
spin_unlock ( & pde - > pde_unload_lock ) ;
return rv ;
}
pde - > pde_users + + ;
compat_ioctl = pde - > proc_fops - > compat_ioctl ;
spin_unlock ( & pde - > pde_unload_lock ) ;
if ( compat_ioctl )
rv = compat_ioctl ( file , cmd , arg ) ;
pde_users_dec ( pde ) ;
return rv ;
}
# endif
static int proc_reg_mmap ( struct file * file , struct vm_area_struct * vma )
{
struct proc_dir_entry * pde = PDE ( file - > f_path . dentry - > d_inode ) ;
int rv = - EIO ;
int ( * mmap ) ( struct file * , struct vm_area_struct * ) ;
spin_lock ( & pde - > pde_unload_lock ) ;
if ( ! pde - > proc_fops ) {
spin_unlock ( & pde - > pde_unload_lock ) ;
return rv ;
}
pde - > pde_users + + ;
mmap = pde - > proc_fops - > mmap ;
spin_unlock ( & pde - > pde_unload_lock ) ;
if ( mmap )
rv = mmap ( file , vma ) ;
pde_users_dec ( pde ) ;
return rv ;
}
static int proc_reg_open ( struct inode * inode , struct file * file )
{
struct proc_dir_entry * pde = PDE ( inode ) ;
int rv = 0 ;
int ( * open ) ( struct inode * , struct file * ) ;
2008-07-25 12:48:29 +04:00
int ( * release ) ( struct inode * , struct file * ) ;
struct pde_opener * pdeo ;
/*
* What for , you ask ? Well , we can have open , rmmod , remove_proc_entry
* sequence . - > release won ' t be called because - > proc_fops will be
* cleared . Depending on complexity of - > release , consequences vary .
*
* We can ' t wait for mercy when close will be done for real , it ' s
* deadlockable : rmmod foo < / proc / foo . So , we ' re going to do - > release
* by hand in remove_proc_entry ( ) . For this , save opener ' s credentials
* for later .
*/
pdeo = kmalloc ( sizeof ( struct pde_opener ) , GFP_KERNEL ) ;
if ( ! pdeo )
return - ENOMEM ;
Fix rmmod/read/write races in /proc entries
Fix following races:
===========================================
1. Write via ->write_proc sleeps in copy_from_user(). Module disappears
meanwhile. Or, more generically, system call done on /proc file, method
supplied by module is called, module dissapeares meanwhile.
pde = create_proc_entry()
if (!pde)
return -ENOMEM;
pde->write_proc = ...
open
write
copy_from_user
pde = create_proc_entry();
if (!pde) {
remove_proc_entry();
return -ENOMEM;
/* module unloaded */
}
*boom*
==========================================
2. bogo-revoke aka proc_kill_inodes()
remove_proc_entry vfs_read
proc_kill_inodes [check ->f_op validness]
[check ->f_op->read validness]
[verify_area, security permissions checks]
->f_op = NULL;
if (file->f_op->read)
/* ->f_op dereference, boom */
NOTE, NOTE, NOTE: file_operations are proxied for regular files only. Let's
see how this scheme behaves, then extend if needed for directories.
Directories creators in /proc only set ->owner for them, so proxying for
directories may be unneeded.
NOTE, NOTE, NOTE: methods being proxied are ->llseek, ->read, ->write,
->poll, ->unlocked_ioctl, ->ioctl, ->compat_ioctl, ->open, ->release.
If your in-tree module uses something else, yell on me. Full audit pending.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-16 10:39:00 +04:00
spin_lock ( & pde - > pde_unload_lock ) ;
if ( ! pde - > proc_fops ) {
spin_unlock ( & pde - > pde_unload_lock ) ;
2008-07-25 12:48:29 +04:00
kfree ( pdeo ) ;
Fix rmmod/read/write races in /proc entries
Fix following races:
===========================================
1. Write via ->write_proc sleeps in copy_from_user(). Module disappears
meanwhile. Or, more generically, system call done on /proc file, method
supplied by module is called, module dissapeares meanwhile.
pde = create_proc_entry()
if (!pde)
return -ENOMEM;
pde->write_proc = ...
open
write
copy_from_user
pde = create_proc_entry();
if (!pde) {
remove_proc_entry();
return -ENOMEM;
/* module unloaded */
}
*boom*
==========================================
2. bogo-revoke aka proc_kill_inodes()
remove_proc_entry vfs_read
proc_kill_inodes [check ->f_op validness]
[check ->f_op->read validness]
[verify_area, security permissions checks]
->f_op = NULL;
if (file->f_op->read)
/* ->f_op dereference, boom */
NOTE, NOTE, NOTE: file_operations are proxied for regular files only. Let's
see how this scheme behaves, then extend if needed for directories.
Directories creators in /proc only set ->owner for them, so proxying for
directories may be unneeded.
NOTE, NOTE, NOTE: methods being proxied are ->llseek, ->read, ->write,
->poll, ->unlocked_ioctl, ->ioctl, ->compat_ioctl, ->open, ->release.
If your in-tree module uses something else, yell on me. Full audit pending.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-16 10:39:00 +04:00
return rv ;
}
pde - > pde_users + + ;
open = pde - > proc_fops - > open ;
2008-07-25 12:48:29 +04:00
release = pde - > proc_fops - > release ;
Fix rmmod/read/write races in /proc entries
Fix following races:
===========================================
1. Write via ->write_proc sleeps in copy_from_user(). Module disappears
meanwhile. Or, more generically, system call done on /proc file, method
supplied by module is called, module dissapeares meanwhile.
pde = create_proc_entry()
if (!pde)
return -ENOMEM;
pde->write_proc = ...
open
write
copy_from_user
pde = create_proc_entry();
if (!pde) {
remove_proc_entry();
return -ENOMEM;
/* module unloaded */
}
*boom*
==========================================
2. bogo-revoke aka proc_kill_inodes()
remove_proc_entry vfs_read
proc_kill_inodes [check ->f_op validness]
[check ->f_op->read validness]
[verify_area, security permissions checks]
->f_op = NULL;
if (file->f_op->read)
/* ->f_op dereference, boom */
NOTE, NOTE, NOTE: file_operations are proxied for regular files only. Let's
see how this scheme behaves, then extend if needed for directories.
Directories creators in /proc only set ->owner for them, so proxying for
directories may be unneeded.
NOTE, NOTE, NOTE: methods being proxied are ->llseek, ->read, ->write,
->poll, ->unlocked_ioctl, ->ioctl, ->compat_ioctl, ->open, ->release.
If your in-tree module uses something else, yell on me. Full audit pending.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-16 10:39:00 +04:00
spin_unlock ( & pde - > pde_unload_lock ) ;
if ( open )
rv = open ( inode , file ) ;
2008-07-25 12:48:29 +04:00
spin_lock ( & pde - > pde_unload_lock ) ;
if ( rv = = 0 & & release ) {
/* To know what to release. */
pdeo - > inode = inode ;
pdeo - > file = file ;
/* Strictly for "too late" ->release in proc_reg_release(). */
pdeo - > release = release ;
list_add ( & pdeo - > lh , & pde - > pde_openers ) ;
} else
kfree ( pdeo ) ;
__pde_users_dec ( pde ) ;
spin_unlock ( & pde - > pde_unload_lock ) ;
Fix rmmod/read/write races in /proc entries
Fix following races:
===========================================
1. Write via ->write_proc sleeps in copy_from_user(). Module disappears
meanwhile. Or, more generically, system call done on /proc file, method
supplied by module is called, module dissapeares meanwhile.
pde = create_proc_entry()
if (!pde)
return -ENOMEM;
pde->write_proc = ...
open
write
copy_from_user
pde = create_proc_entry();
if (!pde) {
remove_proc_entry();
return -ENOMEM;
/* module unloaded */
}
*boom*
==========================================
2. bogo-revoke aka proc_kill_inodes()
remove_proc_entry vfs_read
proc_kill_inodes [check ->f_op validness]
[check ->f_op->read validness]
[verify_area, security permissions checks]
->f_op = NULL;
if (file->f_op->read)
/* ->f_op dereference, boom */
NOTE, NOTE, NOTE: file_operations are proxied for regular files only. Let's
see how this scheme behaves, then extend if needed for directories.
Directories creators in /proc only set ->owner for them, so proxying for
directories may be unneeded.
NOTE, NOTE, NOTE: methods being proxied are ->llseek, ->read, ->write,
->poll, ->unlocked_ioctl, ->ioctl, ->compat_ioctl, ->open, ->release.
If your in-tree module uses something else, yell on me. Full audit pending.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-16 10:39:00 +04:00
return rv ;
}
2008-07-25 12:48:29 +04:00
static struct pde_opener * find_pde_opener ( struct proc_dir_entry * pde ,
struct inode * inode , struct file * file )
{
struct pde_opener * pdeo ;
list_for_each_entry ( pdeo , & pde - > pde_openers , lh ) {
if ( pdeo - > inode = = inode & & pdeo - > file = = file )
return pdeo ;
}
return NULL ;
}
Fix rmmod/read/write races in /proc entries
Fix following races:
===========================================
1. Write via ->write_proc sleeps in copy_from_user(). Module disappears
meanwhile. Or, more generically, system call done on /proc file, method
supplied by module is called, module dissapeares meanwhile.
pde = create_proc_entry()
if (!pde)
return -ENOMEM;
pde->write_proc = ...
open
write
copy_from_user
pde = create_proc_entry();
if (!pde) {
remove_proc_entry();
return -ENOMEM;
/* module unloaded */
}
*boom*
==========================================
2. bogo-revoke aka proc_kill_inodes()
remove_proc_entry vfs_read
proc_kill_inodes [check ->f_op validness]
[check ->f_op->read validness]
[verify_area, security permissions checks]
->f_op = NULL;
if (file->f_op->read)
/* ->f_op dereference, boom */
NOTE, NOTE, NOTE: file_operations are proxied for regular files only. Let's
see how this scheme behaves, then extend if needed for directories.
Directories creators in /proc only set ->owner for them, so proxying for
directories may be unneeded.
NOTE, NOTE, NOTE: methods being proxied are ->llseek, ->read, ->write,
->poll, ->unlocked_ioctl, ->ioctl, ->compat_ioctl, ->open, ->release.
If your in-tree module uses something else, yell on me. Full audit pending.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-16 10:39:00 +04:00
static int proc_reg_release ( struct inode * inode , struct file * file )
{
struct proc_dir_entry * pde = PDE ( inode ) ;
int rv = 0 ;
int ( * release ) ( struct inode * , struct file * ) ;
2008-07-25 12:48:29 +04:00
struct pde_opener * pdeo ;
Fix rmmod/read/write races in /proc entries
Fix following races:
===========================================
1. Write via ->write_proc sleeps in copy_from_user(). Module disappears
meanwhile. Or, more generically, system call done on /proc file, method
supplied by module is called, module dissapeares meanwhile.
pde = create_proc_entry()
if (!pde)
return -ENOMEM;
pde->write_proc = ...
open
write
copy_from_user
pde = create_proc_entry();
if (!pde) {
remove_proc_entry();
return -ENOMEM;
/* module unloaded */
}
*boom*
==========================================
2. bogo-revoke aka proc_kill_inodes()
remove_proc_entry vfs_read
proc_kill_inodes [check ->f_op validness]
[check ->f_op->read validness]
[verify_area, security permissions checks]
->f_op = NULL;
if (file->f_op->read)
/* ->f_op dereference, boom */
NOTE, NOTE, NOTE: file_operations are proxied for regular files only. Let's
see how this scheme behaves, then extend if needed for directories.
Directories creators in /proc only set ->owner for them, so proxying for
directories may be unneeded.
NOTE, NOTE, NOTE: methods being proxied are ->llseek, ->read, ->write,
->poll, ->unlocked_ioctl, ->ioctl, ->compat_ioctl, ->open, ->release.
If your in-tree module uses something else, yell on me. Full audit pending.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-16 10:39:00 +04:00
spin_lock ( & pde - > pde_unload_lock ) ;
2008-07-25 12:48:29 +04:00
pdeo = find_pde_opener ( pde , inode , file ) ;
Fix rmmod/read/write races in /proc entries
Fix following races:
===========================================
1. Write via ->write_proc sleeps in copy_from_user(). Module disappears
meanwhile. Or, more generically, system call done on /proc file, method
supplied by module is called, module dissapeares meanwhile.
pde = create_proc_entry()
if (!pde)
return -ENOMEM;
pde->write_proc = ...
open
write
copy_from_user
pde = create_proc_entry();
if (!pde) {
remove_proc_entry();
return -ENOMEM;
/* module unloaded */
}
*boom*
==========================================
2. bogo-revoke aka proc_kill_inodes()
remove_proc_entry vfs_read
proc_kill_inodes [check ->f_op validness]
[check ->f_op->read validness]
[verify_area, security permissions checks]
->f_op = NULL;
if (file->f_op->read)
/* ->f_op dereference, boom */
NOTE, NOTE, NOTE: file_operations are proxied for regular files only. Let's
see how this scheme behaves, then extend if needed for directories.
Directories creators in /proc only set ->owner for them, so proxying for
directories may be unneeded.
NOTE, NOTE, NOTE: methods being proxied are ->llseek, ->read, ->write,
->poll, ->unlocked_ioctl, ->ioctl, ->compat_ioctl, ->open, ->release.
If your in-tree module uses something else, yell on me. Full audit pending.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-16 10:39:00 +04:00
if ( ! pde - > proc_fops ) {
2008-07-25 12:48:29 +04:00
/*
* Can ' t simply exit , __fput ( ) will think that everything is OK ,
* and move on to freeing struct file . remove_proc_entry ( ) will
* find slacker in opener ' s list and will try to do non - trivial
* things with struct file . Therefore , remove opener from list .
*
* But if opener is removed from list , who will - > release it ?
*/
if ( pdeo ) {
list_del ( & pdeo - > lh ) ;
spin_unlock ( & pde - > pde_unload_lock ) ;
rv = pdeo - > release ( inode , file ) ;
kfree ( pdeo ) ;
} else
spin_unlock ( & pde - > pde_unload_lock ) ;
Fix rmmod/read/write races in /proc entries
Fix following races:
===========================================
1. Write via ->write_proc sleeps in copy_from_user(). Module disappears
meanwhile. Or, more generically, system call done on /proc file, method
supplied by module is called, module dissapeares meanwhile.
pde = create_proc_entry()
if (!pde)
return -ENOMEM;
pde->write_proc = ...
open
write
copy_from_user
pde = create_proc_entry();
if (!pde) {
remove_proc_entry();
return -ENOMEM;
/* module unloaded */
}
*boom*
==========================================
2. bogo-revoke aka proc_kill_inodes()
remove_proc_entry vfs_read
proc_kill_inodes [check ->f_op validness]
[check ->f_op->read validness]
[verify_area, security permissions checks]
->f_op = NULL;
if (file->f_op->read)
/* ->f_op dereference, boom */
NOTE, NOTE, NOTE: file_operations are proxied for regular files only. Let's
see how this scheme behaves, then extend if needed for directories.
Directories creators in /proc only set ->owner for them, so proxying for
directories may be unneeded.
NOTE, NOTE, NOTE: methods being proxied are ->llseek, ->read, ->write,
->poll, ->unlocked_ioctl, ->ioctl, ->compat_ioctl, ->open, ->release.
If your in-tree module uses something else, yell on me. Full audit pending.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-16 10:39:00 +04:00
return rv ;
}
pde - > pde_users + + ;
release = pde - > proc_fops - > release ;
2008-07-25 12:48:29 +04:00
if ( pdeo ) {
list_del ( & pdeo - > lh ) ;
kfree ( pdeo ) ;
}
Fix rmmod/read/write races in /proc entries
Fix following races:
===========================================
1. Write via ->write_proc sleeps in copy_from_user(). Module disappears
meanwhile. Or, more generically, system call done on /proc file, method
supplied by module is called, module dissapeares meanwhile.
pde = create_proc_entry()
if (!pde)
return -ENOMEM;
pde->write_proc = ...
open
write
copy_from_user
pde = create_proc_entry();
if (!pde) {
remove_proc_entry();
return -ENOMEM;
/* module unloaded */
}
*boom*
==========================================
2. bogo-revoke aka proc_kill_inodes()
remove_proc_entry vfs_read
proc_kill_inodes [check ->f_op validness]
[check ->f_op->read validness]
[verify_area, security permissions checks]
->f_op = NULL;
if (file->f_op->read)
/* ->f_op dereference, boom */
NOTE, NOTE, NOTE: file_operations are proxied for regular files only. Let's
see how this scheme behaves, then extend if needed for directories.
Directories creators in /proc only set ->owner for them, so proxying for
directories may be unneeded.
NOTE, NOTE, NOTE: methods being proxied are ->llseek, ->read, ->write,
->poll, ->unlocked_ioctl, ->ioctl, ->compat_ioctl, ->open, ->release.
If your in-tree module uses something else, yell on me. Full audit pending.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-16 10:39:00 +04:00
spin_unlock ( & pde - > pde_unload_lock ) ;
if ( release )
rv = release ( inode , file ) ;
pde_users_dec ( pde ) ;
return rv ;
}
static const struct file_operations proc_reg_file_ops = {
. llseek = proc_reg_llseek ,
. read = proc_reg_read ,
. write = proc_reg_write ,
. poll = proc_reg_poll ,
. unlocked_ioctl = proc_reg_unlocked_ioctl ,
# ifdef CONFIG_COMPAT
. compat_ioctl = proc_reg_compat_ioctl ,
# endif
. mmap = proc_reg_mmap ,
. open = proc_reg_open ,
. release = proc_reg_release ,
} ;
2007-07-28 09:58:37 +04:00
# ifdef CONFIG_COMPAT
static const struct file_operations proc_reg_file_ops_no_compat = {
. llseek = proc_reg_llseek ,
. read = proc_reg_read ,
. write = proc_reg_write ,
. poll = proc_reg_poll ,
. unlocked_ioctl = proc_reg_unlocked_ioctl ,
. mmap = proc_reg_mmap ,
. open = proc_reg_open ,
. release = proc_reg_release ,
} ;
# endif
2005-04-17 02:20:36 +04:00
struct inode * proc_get_inode ( struct super_block * sb , unsigned int ino ,
struct proc_dir_entry * de )
{
struct inode * inode ;
2008-04-29 12:01:41 +04:00
if ( ! try_module_get ( de - > owner ) )
2005-10-31 02:02:26 +03:00
goto out_mod ;
2008-02-07 11:15:45 +03:00
inode = iget_locked ( sb , ino ) ;
2005-04-17 02:20:36 +04:00
if ( ! inode )
2005-10-31 02:02:26 +03:00
goto out_ino ;
2008-02-07 11:15:45 +03:00
if ( inode - > i_state & I_NEW ) {
inode - > i_mtime = inode - > i_atime = inode - > i_ctime = CURRENT_TIME ;
PROC_I ( inode ) - > fd = 0 ;
PROC_I ( inode ) - > pde = de ;
2008-04-29 12:01:41 +04:00
if ( de - > mode ) {
inode - > i_mode = de - > mode ;
inode - > i_uid = de - > uid ;
inode - > i_gid = de - > gid ;
}
if ( de - > size )
inode - > i_size = de - > size ;
if ( de - > nlink )
inode - > i_nlink = de - > nlink ;
if ( de - > proc_iops )
inode - > i_op = de - > proc_iops ;
if ( de - > proc_fops ) {
if ( S_ISREG ( inode - > i_mode ) ) {
2007-07-28 09:58:37 +04:00
# ifdef CONFIG_COMPAT
2008-04-29 12:01:41 +04:00
if ( ! de - > proc_fops - > compat_ioctl )
inode - > i_fop =
& proc_reg_file_ops_no_compat ;
else
2007-07-28 09:58:37 +04:00
# endif
2008-04-29 12:01:41 +04:00
inode - > i_fop = & proc_reg_file_ops ;
} else {
inode - > i_fop = de - > proc_fops ;
2007-07-28 09:58:37 +04:00
}
Fix rmmod/read/write races in /proc entries
Fix following races:
===========================================
1. Write via ->write_proc sleeps in copy_from_user(). Module disappears
meanwhile. Or, more generically, system call done on /proc file, method
supplied by module is called, module dissapeares meanwhile.
pde = create_proc_entry()
if (!pde)
return -ENOMEM;
pde->write_proc = ...
open
write
copy_from_user
pde = create_proc_entry();
if (!pde) {
remove_proc_entry();
return -ENOMEM;
/* module unloaded */
}
*boom*
==========================================
2. bogo-revoke aka proc_kill_inodes()
remove_proc_entry vfs_read
proc_kill_inodes [check ->f_op validness]
[check ->f_op->read validness]
[verify_area, security permissions checks]
->f_op = NULL;
if (file->f_op->read)
/* ->f_op dereference, boom */
NOTE, NOTE, NOTE: file_operations are proxied for regular files only. Let's
see how this scheme behaves, then extend if needed for directories.
Directories creators in /proc only set ->owner for them, so proxying for
directories may be unneeded.
NOTE, NOTE, NOTE: methods being proxied are ->llseek, ->read, ->write,
->poll, ->unlocked_ioctl, ->ioctl, ->compat_ioctl, ->open, ->release.
If your in-tree module uses something else, yell on me. Full audit pending.
[akpm@linux-foundation.org: build fix]
Signed-off-by: Alexey Dobriyan <adobriyan@sw.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-16 10:39:00 +04:00
}
2008-02-07 11:15:45 +03:00
unlock_new_inode ( inode ) ;
2008-05-24 00:04:47 +04:00
} else
module_put ( de - > owner ) ;
2005-04-17 02:20:36 +04:00
return inode ;
2005-10-31 02:02:26 +03:00
out_ino :
2008-04-29 12:01:41 +04:00
module_put ( de - > owner ) ;
2005-10-31 02:02:26 +03:00
out_mod :
return NULL ;
2005-04-17 02:20:36 +04:00
}
2007-10-19 10:40:08 +04:00
int proc_fill_super ( struct super_block * s )
2005-04-17 02:20:36 +04:00
{
struct inode * root_inode ;
2006-07-15 23:20:05 +04:00
s - > s_flags | = MS_NODIRATIME | MS_NOSUID | MS_NOEXEC ;
2005-04-17 02:20:36 +04:00
s - > s_blocksize = 1024 ;
s - > s_blocksize_bits = 10 ;
s - > s_magic = PROC_SUPER_MAGIC ;
s - > s_op = & proc_sops ;
s - > s_time_gran = 1 ;
2007-05-08 11:25:45 +04:00
de_get ( & proc_root ) ;
2005-04-17 02:20:36 +04:00
root_inode = proc_get_inode ( s , PROC_ROOT_INO , & proc_root ) ;
if ( ! root_inode )
goto out_no_root ;
root_inode - > i_uid = 0 ;
root_inode - > i_gid = 0 ;
s - > s_root = d_alloc_root ( root_inode ) ;
if ( ! s - > s_root )
goto out_no_root ;
return 0 ;
out_no_root :
printk ( " proc_read_super: get root inode failed \n " ) ;
iput ( root_inode ) ;
2007-05-08 11:25:45 +04:00
de_put ( & proc_root ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}