| From 4e474a00d7ff746ed177ddae14fa8b2d4bad7a00 Mon Sep 17 00:00:00 2001 |
| From: Lucas De Marchi <lucas.demarchi@profusion.mobi> |
| Date: Thu, 22 Mar 2012 14:42:22 -0700 |
| Subject: sysctl: protect poll() in entries that may go away |
| |
| From: Lucas De Marchi <lucas.demarchi@profusion.mobi> |
| |
| commit 4e474a00d7ff746ed177ddae14fa8b2d4bad7a00 upstream. |
| |
| Protect code accessing ctl_table by grabbing the header with grab_header() |
| and after releasing with sysctl_head_finish(). This is needed if poll() |
| is called in entries created by modules: currently only hostname and |
| domainname support poll(), but this bug may be triggered when/if modules |
| use it and if user called poll() in a file that doesn't support it. |
| |
| Dave Jones reported the following when using a syscall fuzzer while |
| hibernating/resuming: |
| |
| RIP: 0010:[<ffffffff81233e3e>] [<ffffffff81233e3e>] proc_sys_poll+0x4e/0x90 |
| RAX: 0000000000000145 RBX: ffff88020cab6940 RCX: 0000000000000000 |
| RDX: ffffffff81233df0 RSI: 6b6b6b6b6b6b6b6b RDI: ffff88020cab6940 |
| [ ... ] |
| Code: 00 48 89 fb 48 89 f1 48 8b 40 30 4c 8b 60 e8 b8 45 01 00 00 49 83 |
| 7c 24 28 00 74 2e 49 8b 74 24 30 48 85 f6 74 24 48 85 c9 75 32 <8b> 16 |
| b8 45 01 00 00 48 63 d2 49 39 d5 74 10 8b 06 48 98 48 89 |
| |
| If an entry goes away while we are polling() it, ctl_table may not exist |
| anymore. |
| |
| Reported-by: Dave Jones <davej@redhat.com> |
| Signed-off-by: Lucas De Marchi <lucas.demarchi@profusion.mobi> |
| Cc: Al Viro <viro@zeniv.linux.org.uk> |
| Cc: Linus Torvalds <torvalds@linux-foundation.org> |
| Cc: Alexey Dobriyan <adobriyan@gmail.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/proc/proc_sysctl.c | 17 ++++++++++++++++- |
| 1 file changed, 16 insertions(+), 1 deletion(-) |
| |
| --- a/fs/proc/proc_sysctl.c |
| +++ b/fs/proc/proc_sysctl.c |
| @@ -188,20 +188,32 @@ static ssize_t proc_sys_write(struct fil |
| |
| static int proc_sys_open(struct inode *inode, struct file *filp) |
| { |
| + struct ctl_table_header *head = grab_header(inode); |
| struct ctl_table *table = PROC_I(inode)->sysctl_entry; |
| |
| + /* sysctl was unregistered */ |
| + if (IS_ERR(head)) |
| + return PTR_ERR(head); |
| + |
| if (table->poll) |
| filp->private_data = proc_sys_poll_event(table->poll); |
| |
| + sysctl_head_finish(head); |
| + |
| return 0; |
| } |
| |
| static unsigned int proc_sys_poll(struct file *filp, poll_table *wait) |
| { |
| struct inode *inode = filp->f_path.dentry->d_inode; |
| + struct ctl_table_header *head = grab_header(inode); |
| struct ctl_table *table = PROC_I(inode)->sysctl_entry; |
| - unsigned long event = (unsigned long)filp->private_data; |
| unsigned int ret = DEFAULT_POLLMASK; |
| + unsigned long event; |
| + |
| + /* sysctl was unregistered */ |
| + if (IS_ERR(head)) |
| + return POLLERR | POLLHUP; |
| |
| if (!table->proc_handler) |
| goto out; |
| @@ -209,6 +221,7 @@ static unsigned int proc_sys_poll(struct |
| if (!table->poll) |
| goto out; |
| |
| + event = (unsigned long)filp->private_data; |
| poll_wait(filp, &table->poll->wait, wait); |
| |
| if (event != atomic_read(&table->poll->event)) { |
| @@ -217,6 +230,8 @@ static unsigned int proc_sys_poll(struct |
| } |
| |
| out: |
| + sysctl_head_finish(head); |
| + |
| return ret; |
| } |
| |