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