diff options
author | Alexey Dobriyan <adobriyan@sw.ru> | 2007-07-16 02:39:00 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-16 12:05:39 -0400 |
commit | 786d7e1612f0b0adb6046f19b906609e4fe8b1ba (patch) | |
tree | 9d5f1623c19c9d3f84606ea160d57cd3c8c97ea9 /include | |
parent | 5568b0e8028d966ddb16f0be44a9df1fcbd1dc8d (diff) |
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>
Diffstat (limited to 'include')
-rw-r--r-- | include/linux/proc_fs.h | 13 |
1 files changed, 13 insertions, 0 deletions
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h index 3469f96bc8b2..28e3664fdf1b 100644 --- a/include/linux/proc_fs.h +++ b/include/linux/proc_fs.h | |||
@@ -7,6 +7,8 @@ | |||
7 | #include <linux/magic.h> | 7 | #include <linux/magic.h> |
8 | #include <asm/atomic.h> | 8 | #include <asm/atomic.h> |
9 | 9 | ||
10 | struct completion; | ||
11 | |||
10 | /* | 12 | /* |
11 | * The proc filesystem constants/structures | 13 | * The proc filesystem constants/structures |
12 | */ | 14 | */ |
@@ -56,6 +58,14 @@ struct proc_dir_entry { | |||
56 | gid_t gid; | 58 | gid_t gid; |
57 | loff_t size; | 59 | loff_t size; |
58 | const struct inode_operations *proc_iops; | 60 | const struct inode_operations *proc_iops; |
61 | /* | ||
62 | * NULL ->proc_fops means "PDE is going away RSN" or | ||
63 | * "PDE is just created". In either case, e.g. ->read_proc won't be | ||
64 | * called because it's too late or too early, respectively. | ||
65 | * | ||
66 | * If you're allocating ->proc_fops dynamically, save a pointer | ||
67 | * somewhere. | ||
68 | */ | ||
59 | const struct file_operations *proc_fops; | 69 | const struct file_operations *proc_fops; |
60 | get_info_t *get_info; | 70 | get_info_t *get_info; |
61 | struct module *owner; | 71 | struct module *owner; |
@@ -66,6 +76,9 @@ struct proc_dir_entry { | |||
66 | atomic_t count; /* use count */ | 76 | atomic_t count; /* use count */ |
67 | int deleted; /* delete flag */ | 77 | int deleted; /* delete flag */ |
68 | void *set; | 78 | void *set; |
79 | int pde_users; /* number of callers into module in progress */ | ||
80 | spinlock_t pde_unload_lock; /* proc_fops checks and pde_users bumps */ | ||
81 | struct completion *pde_unload_completion; | ||
69 | }; | 82 | }; |
70 | 83 | ||
71 | struct kcore_list { | 84 | struct kcore_list { |