Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e0d40756 authored by Russell King's avatar Russell King
Browse files

ARM: fix a cockup in 48be69a0 (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: default avatarOlof Johansson <olof@lixom.net>
Tested-by: default avatarOlof Johansson <olof@lixom.net>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent a5463cd3
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -471,17 +471,18 @@ const char *arch_vma_name(struct vm_area_struct *vma)
		 "[sigpage]" : NULL;
}

static struct page *signal_page;
extern struct page *get_signal_page(void);

int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
{
	struct mm_struct *mm = current->mm;
	struct page *page;
	unsigned long addr;
	int ret;

	page = get_signal_page();
	if (!page)
	if (!signal_page)
		signal_page = get_signal_page();
	if (!signal_page)
		return -ENOMEM;

	down_write(&mm->mmap_sem);
@@ -493,7 +494,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)

	ret = install_special_mapping(mm, addr, PAGE_SIZE,
		VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC,
		&page);
		&signal_page);

	if (ret == 0)
		mm->context.sigpage = addr;
+19 −22
Original line number Diff line number Diff line
@@ -614,21 +614,19 @@ do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
	return 0;
}

static struct page *signal_page;

struct page *get_signal_page(void)
{
	if (!signal_page) {
	unsigned long ptr;
	unsigned offset;
	struct page *page;
	void *addr;

		signal_page = alloc_pages(GFP_KERNEL, 0);
	page = alloc_pages(GFP_KERNEL, 0);

		if (!signal_page)
	if (!page)
		return NULL;

		addr = page_address(signal_page);
	addr = page_address(page);

	/* Give the signal return code some randomness */
	offset = 0x200 + (get_random_int() & 0x7fc);
@@ -642,7 +640,6 @@ struct page *get_signal_page(void)

	ptr = (unsigned long)addr + offset;
	flush_icache_range(ptr, ptr + sizeof(sigreturn_codes));
	}

	return signal_page;
	return page;
}