diff options
author | Lucas De Marchi <lucas.demarchi@profusion.mobi> | 2012-03-22 17:42:22 -0400 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2012-03-22 17:46:56 -0400 |
commit | 4e474a00d7ff746ed177ddae14fa8b2d4bad7a00 (patch) | |
tree | 1255e88406191e4daa29e59ebd37fdc8042184e4 /fs/proc/proc_sysctl.c | |
parent | 4e75732035d7e97e001bdf6e3149d3967c0221de (diff) |
sysctl: protect poll() in entries that may go away
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>
Cc: stable@vger.kernel.org
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Diffstat (limited to 'fs/proc/proc_sysctl.c')
-rw-r--r-- | fs/proc/proc_sysctl.c | 17 |
1 files changed, 16 insertions, 1 deletions
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c index a7708b7c957f..47b474b572c1 100644 --- a/fs/proc/proc_sysctl.c +++ b/fs/proc/proc_sysctl.c | |||
@@ -525,20 +525,32 @@ static ssize_t proc_sys_write(struct file *filp, const char __user *buf, | |||
525 | 525 | ||
526 | static int proc_sys_open(struct inode *inode, struct file *filp) | 526 | static int proc_sys_open(struct inode *inode, struct file *filp) |
527 | { | 527 | { |
528 | struct ctl_table_header *head = grab_header(inode); | ||
528 | struct ctl_table *table = PROC_I(inode)->sysctl_entry; | 529 | struct ctl_table *table = PROC_I(inode)->sysctl_entry; |
529 | 530 | ||
531 | /* sysctl was unregistered */ | ||
532 | if (IS_ERR(head)) | ||
533 | return PTR_ERR(head); | ||
534 | |||
530 | if (table->poll) | 535 | if (table->poll) |
531 | filp->private_data = proc_sys_poll_event(table->poll); | 536 | filp->private_data = proc_sys_poll_event(table->poll); |
532 | 537 | ||
538 | sysctl_head_finish(head); | ||
539 | |||
533 | return 0; | 540 | return 0; |
534 | } | 541 | } |
535 | 542 | ||
536 | static unsigned int proc_sys_poll(struct file *filp, poll_table *wait) | 543 | static unsigned int proc_sys_poll(struct file *filp, poll_table *wait) |
537 | { | 544 | { |
538 | struct inode *inode = filp->f_path.dentry->d_inode; | 545 | struct inode *inode = filp->f_path.dentry->d_inode; |
546 | struct ctl_table_header *head = grab_header(inode); | ||
539 | struct ctl_table *table = PROC_I(inode)->sysctl_entry; | 547 | struct ctl_table *table = PROC_I(inode)->sysctl_entry; |
540 | unsigned long event = (unsigned long)filp->private_data; | ||
541 | unsigned int ret = DEFAULT_POLLMASK; | 548 | unsigned int ret = DEFAULT_POLLMASK; |
549 | unsigned long event; | ||
550 | |||
551 | /* sysctl was unregistered */ | ||
552 | if (IS_ERR(head)) | ||
553 | return POLLERR | POLLHUP; | ||
542 | 554 | ||
543 | if (!table->proc_handler) | 555 | if (!table->proc_handler) |
544 | goto out; | 556 | goto out; |
@@ -546,6 +558,7 @@ static unsigned int proc_sys_poll(struct file *filp, poll_table *wait) | |||
546 | if (!table->poll) | 558 | if (!table->poll) |
547 | goto out; | 559 | goto out; |
548 | 560 | ||
561 | event = (unsigned long)filp->private_data; | ||
549 | poll_wait(filp, &table->poll->wait, wait); | 562 | poll_wait(filp, &table->poll->wait, wait); |
550 | 563 | ||
551 | if (event != atomic_read(&table->poll->event)) { | 564 | if (event != atomic_read(&table->poll->event)) { |
@@ -554,6 +567,8 @@ static unsigned int proc_sys_poll(struct file *filp, poll_table *wait) | |||
554 | } | 567 | } |
555 | 568 | ||
556 | out: | 569 | out: |
570 | sysctl_head_finish(head); | ||
571 | |||
557 | return ret; | 572 | return ret; |
558 | } | 573 | } |
559 | 574 | ||