From 97374d38583028b33074c69caf296d94cb1b9d5b Mon Sep 17 00:00:00 2001 From: Paul Brook Date: Wed, 16 Jun 2010 13:03:51 +0100 Subject: [PATCH] Usermode exec-stack fix When loading a shared library that requires an executable stack, glibc uses the mprotext PROT_GROWSDOWN flag to achieve this. We don't support PROT_GROWSDOWN. Add a special case to handle changing the stack permissions in this way. Signed-off-by: Paul Brook --- linux-user/elfload.c | 1 + linux-user/flatload.c | 1 + linux-user/qemu.h | 1 + linux-user/syscall.c | 11 +++++++++++ 4 files changed, 14 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 2d920f2017..accb44d9de 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1018,6 +1018,7 @@ static abi_ulong setup_arg_pages(abi_ulong p, struct linux_binprm *bprm, /* we reserve one extra page at the top of the stack as guard */ target_mprotect(error + size, qemu_host_page_size, PROT_NONE); + info->stack_limit = error; stack_base = error + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE; p += stack_base; diff --git a/linux-user/flatload.c b/linux-user/flatload.c index 914de1f888..8ad130a2bd 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -802,6 +802,7 @@ int load_flt_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, info->end_data = libinfo[0].end_data; info->start_brk = libinfo[0].start_brk; info->start_stack = sp; + info->stack_limit = libinfo[0].start_brk; info->entry = start_addr; info->code_offset = info->start_code; info->data_offset = info->start_data - libinfo[0].text_len; diff --git a/linux-user/qemu.h b/linux-user/qemu.h index dab3597cc6..1878d5a61e 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -42,6 +42,7 @@ struct image_info { abi_ulong mmap; abi_ulong rss; abi_ulong start_stack; + abi_ulong stack_limit; abi_ulong entry; abi_ulong code_offset; abi_ulong data_offset; diff --git a/linux-user/syscall.c b/linux-user/syscall.c index e94f1eed5c..0ebe7e1c26 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5400,6 +5400,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(target_munmap(arg1, arg2)); break; case TARGET_NR_mprotect: + { + TaskState *ts = ((CPUState *)cpu_env)->opaque; + /* Special hack to detect libc making the stack executable. */ + if ((arg3 & PROT_GROWSDOWN) + && arg1 >= ts->info->stack_limit + && arg1 <= ts->info->start_stack) { + arg3 &= ~PROT_GROWSDOWN; + arg2 = arg2 + arg1 - ts->info->stack_limit; + arg1 = ts->info->stack_limit; + } + } ret = get_errno(target_mprotect(arg1, arg2, arg3)); break; #ifdef TARGET_NR_mremap