aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLucas De Marchi <lucas.demarchi@profusion.mobi>2012-03-22 17:42:22 -0400
committerEric W. Biederman <ebiederm@xmission.com>2012-03-22 17:46:56 -0400
commit4e474a00d7ff746ed177ddae14fa8b2d4bad7a00 (patch)
tree1255e88406191e4daa29e59ebd37fdc8042184e4
parent4e75732035d7e97e001bdf6e3149d3967c0221de (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>
-rw-r--r--fs/proc/proc_sysctl.c17
1 files changed, 16 insertions, 1 deletions
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index a7708b7c957..47b474b572c 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
526static int proc_sys_open(struct inode *inode, struct file *filp) 526static 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
536static unsigned int proc_sys_poll(struct file *filp, poll_table *wait) 543static 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
556out: 569out:
570 sysctl_head_finish(head);
571
557 return ret; 572 return ret;
558} 573}
559 574