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>
This commit is contained in:
committed by
Linus Torvalds
parent
5568b0e802
commit
786d7e1612
@@ -7,6 +7,8 @@
|
||||
#include <linux/magic.h>
|
||||
#include <asm/atomic.h>
|
||||
|
||||
struct completion;
|
||||
|
||||
/*
|
||||
* The proc filesystem constants/structures
|
||||
*/
|
||||
@@ -56,6 +58,14 @@ struct proc_dir_entry {
|
||||
gid_t gid;
|
||||
loff_t size;
|
||||
const struct inode_operations *proc_iops;
|
||||
/*
|
||||
* NULL ->proc_fops means "PDE is going away RSN" or
|
||||
* "PDE is just created". In either case, e.g. ->read_proc won't be
|
||||
* called because it's too late or too early, respectively.
|
||||
*
|
||||
* If you're allocating ->proc_fops dynamically, save a pointer
|
||||
* somewhere.
|
||||
*/
|
||||
const struct file_operations *proc_fops;
|
||||
get_info_t *get_info;
|
||||
struct module *owner;
|
||||
@@ -66,6 +76,9 @@ struct proc_dir_entry {
|
||||
atomic_t count; /* use count */
|
||||
int deleted; /* delete flag */
|
||||
void *set;
|
||||
int pde_users; /* number of callers into module in progress */
|
||||
spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */
|
||||
struct completion *pde_unload_completion;
|
||||
};
|
||||
|
||||
struct kcore_list {
|
||||
|
||||
Reference in New Issue
Block a user