Commit e0d407564b532d978b03ceccebd224a05d02f111

Authored by Russell King
1 parent a5463cd343

ARM: fix a cockup in 48be69a02 (ARM: move signal handlers into a vdso-like page)

Unfortunately, I never committed the fix to a nasty oops which can
occur as a result of that commit:

------------[ cut here ]------------
kernel BUG at /home/olof/work/batch/include/linux/mm.h:414!
Internal error: Oops - BUG: 0 [#1] PREEMPT SMP ARM
Modules linked in:
CPU: 0 PID: 490 Comm: killall5 Not tainted 3.11.0-rc3-00288-gabe0308 #53
task: e90acac0 ti: e9be8000 task.ti: e9be8000
PC is at special_mapping_fault+0xa4/0xc4
LR is at __do_fault+0x68/0x48c

This doesn't show up unless you do quite a bit of testing; a simple
boot test does not do this, so all my nightly tests were passing fine.

The reason for this is that install_special_mapping() expects the
page array to stick around, and as this was only inserting one page
which was stored on the kernel stack, that's why this was blowing up.

Reported-by: Olof Johansson <olof@lixom.net>
Tested-by: Olof Johansson <olof@lixom.net>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

Showing 2 changed files with 24 additions and 26 deletions Side-by-side Diff

arch/arm/kernel/process.c
... ... @@ -471,17 +471,18 @@
471 471 "[sigpage]" : NULL;
472 472 }
473 473  
  474 +static struct page *signal_page;
474 475 extern struct page *get_signal_page(void);
475 476  
476 477 int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
477 478 {
478 479 struct mm_struct *mm = current->mm;
479   - struct page *page;
480 480 unsigned long addr;
481 481 int ret;
482 482  
483   - page = get_signal_page();
484   - if (!page)
  483 + if (!signal_page)
  484 + signal_page = get_signal_page();
  485 + if (!signal_page)
485 486 return -ENOMEM;
486 487  
487 488 down_write(&mm->mmap_sem);
... ... @@ -493,7 +494,7 @@
493 494  
494 495 ret = install_special_mapping(mm, addr, PAGE_SIZE,
495 496 VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC,
496   - &page);
  497 + &signal_page);
497 498  
498 499 if (ret == 0)
499 500 mm->context.sigpage = addr;
arch/arm/kernel/signal.c
... ... @@ -614,36 +614,33 @@
614 614 return 0;
615 615 }
616 616  
617   -static struct page *signal_page;
618   -
619 617 struct page *get_signal_page(void)
620 618 {
621   - if (!signal_page) {
622   - unsigned long ptr;
623   - unsigned offset;
624   - void *addr;
  619 + unsigned long ptr;
  620 + unsigned offset;
  621 + struct page *page;
  622 + void *addr;
625 623  
626   - signal_page = alloc_pages(GFP_KERNEL, 0);
  624 + page = alloc_pages(GFP_KERNEL, 0);
627 625  
628   - if (!signal_page)
629   - return NULL;
  626 + if (!page)
  627 + return NULL;
630 628  
631   - addr = page_address(signal_page);
  629 + addr = page_address(page);
632 630  
633   - /* Give the signal return code some randomness */
634   - offset = 0x200 + (get_random_int() & 0x7fc);
635   - signal_return_offset = offset;
  631 + /* Give the signal return code some randomness */
  632 + offset = 0x200 + (get_random_int() & 0x7fc);
  633 + signal_return_offset = offset;
636 634  
637   - /*
638   - * Copy signal return handlers into the vector page, and
639   - * set sigreturn to be a pointer to these.
640   - */
641   - memcpy(addr + offset, sigreturn_codes, sizeof(sigreturn_codes));
  635 + /*
  636 + * Copy signal return handlers into the vector page, and
  637 + * set sigreturn to be a pointer to these.
  638 + */
  639 + memcpy(addr + offset, sigreturn_codes, sizeof(sigreturn_codes));
642 640  
643   - ptr = (unsigned long)addr + offset;
644   - flush_icache_range(ptr, ptr + sizeof(sigreturn_codes));
645   - }
  641 + ptr = (unsigned long)addr + offset;
  642 + flush_icache_range(ptr, ptr + sizeof(sigreturn_codes));
646 643  
647   - return signal_page;
  644 + return page;
648 645 }