Commit b6a2fea3 authored by Ollie Wild's avatar Ollie Wild Committed by Linus Torvalds

mm: variable length argument support

Remove the arg+env limit of MAX_ARG_PAGES by copying the strings directly from
the old mm into the new mm.

We create the new mm before the binfmt code runs, and place the new stack at
the very top of the address space.  Once the binfmt code runs and figures out
where the stack should be, we move it downwards.

It is a bit peculiar in that we have one task with two mm's, one of which is
inactive.

[a.p.zijlstra@chello.nl: limit stack size]
Signed-off-by: default avatarOllie Wild <aaw@google.com>
Signed-off-by: default avatarPeter Zijlstra <a.p.zijlstra@chello.nl>
Cc: <linux-arch@vger.kernel.org>
Cc: Hugh Dickins <hugh@veritas.com>
[bunk@stusta.de: unexport bprm_mm_init]
Signed-off-by: default avatarAdrian Bunk <bunk@stusta.de>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent bdf4c48a
...@@ -195,62 +195,27 @@ ia64_elf32_init (struct pt_regs *regs) ...@@ -195,62 +195,27 @@ ia64_elf32_init (struct pt_regs *regs)
ia32_load_state(current); ia32_load_state(current);
} }
/*
* Undo the override of setup_arg_pages() without this ia32_setup_arg_pages()
* will suffer infinite self recursion.
*/
#undef setup_arg_pages
int int
ia32_setup_arg_pages (struct linux_binprm *bprm, int executable_stack) ia32_setup_arg_pages (struct linux_binprm *bprm, int executable_stack)
{ {
unsigned long stack_base; int ret;
struct vm_area_struct *mpnt;
struct mm_struct *mm = current->mm; ret = setup_arg_pages(bprm, IA32_STACK_TOP, executable_stack);
int i, ret; if (!ret) {
/*
stack_base = IA32_STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE; * Can't do it in ia64_elf32_init(). Needs to be done before
mm->arg_start = bprm->p + stack_base; * calls to elf32_map()
*/
bprm->p += stack_base; current->thread.ppl = ia32_init_pp_list();
if (bprm->loader)
bprm->loader += stack_base;
bprm->exec += stack_base;
mpnt = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
if (!mpnt)
return -ENOMEM;
down_write(&current->mm->mmap_sem);
{
mpnt->vm_mm = current->mm;
mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;
mpnt->vm_end = IA32_STACK_TOP;
if (executable_stack == EXSTACK_ENABLE_X)
mpnt->vm_flags = VM_STACK_FLAGS | VM_EXEC;
else if (executable_stack == EXSTACK_DISABLE_X)
mpnt->vm_flags = VM_STACK_FLAGS & ~VM_EXEC;
else
mpnt->vm_flags = VM_STACK_FLAGS;
mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC)?
PAGE_COPY_EXEC: PAGE_COPY;
if ((ret = insert_vm_struct(current->mm, mpnt))) {
up_write(&current->mm->mmap_sem);
kmem_cache_free(vm_area_cachep, mpnt);
return ret;
}
current->mm->stack_vm = current->mm->total_vm = vma_pages(mpnt);
} }
for (i = 0 ; i < MAX_ARG_PAGES ; i++) { return ret;
struct page *page = bprm->page[i];
if (page) {
bprm->page[i] = NULL;
install_arg_page(mpnt, page, stack_base);
}
stack_base += PAGE_SIZE;
}
up_write(&current->mm->mmap_sem);
/* Can't do it in ia64_elf32_init(). Needs to be done before calls to
elf32_map() */
current->thread.ppl = ia32_init_pp_list();
return 0;
} }
static void static void
......
...@@ -404,7 +404,7 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs) ...@@ -404,7 +404,7 @@ static int load_aout_binary(struct linux_binprm * bprm, struct pt_regs * regs)
set_brk(current->mm->start_brk, current->mm->brk); set_brk(current->mm->start_brk, current->mm->brk);
retval = ia32_setup_arg_pages(bprm, IA32_STACK_TOP, EXSTACK_DEFAULT); retval = setup_arg_pages(bprm, IA32_STACK_TOP, EXSTACK_DEFAULT);
if (retval < 0) { if (retval < 0) {
/* Someone check-me: is this error path enough? */ /* Someone check-me: is this error path enough? */
send_sig(SIGKILL, current, 0); send_sig(SIGKILL, current, 0);
......
...@@ -232,9 +232,6 @@ do { \ ...@@ -232,9 +232,6 @@ do { \
#define load_elf_binary load_elf32_binary #define load_elf_binary load_elf32_binary
#define ELF_PLAT_INIT(r, load_addr) elf32_init(r) #define ELF_PLAT_INIT(r, load_addr) elf32_init(r)
#define setup_arg_pages(bprm, stack_top, exec_stack) \
ia32_setup_arg_pages(bprm, stack_top, exec_stack)
int ia32_setup_arg_pages(struct linux_binprm *bprm, unsigned long stack_top, int executable_stack);
#undef start_thread #undef start_thread
#define start_thread(regs,new_rip,new_rsp) do { \ #define start_thread(regs,new_rip,new_rsp) do { \
...@@ -286,61 +283,6 @@ static void elf32_init(struct pt_regs *regs) ...@@ -286,61 +283,6 @@ static void elf32_init(struct pt_regs *regs)
me->thread.es = __USER_DS; me->thread.es = __USER_DS;
} }
int ia32_setup_arg_pages(struct linux_binprm *bprm, unsigned long stack_top,
int executable_stack)
{
unsigned long stack_base;
struct vm_area_struct *mpnt;
struct mm_struct *mm = current->mm;
int i, ret;
stack_base = stack_top - MAX_ARG_PAGES * PAGE_SIZE;
mm->arg_start = bprm->p + stack_base;
bprm->p += stack_base;
if (bprm->loader)
bprm->loader += stack_base;
bprm->exec += stack_base;
mpnt = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
if (!mpnt)
return -ENOMEM;
down_write(&mm->mmap_sem);
{
mpnt->vm_mm = mm;
mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;
mpnt->vm_end = stack_top;
if (executable_stack == EXSTACK_ENABLE_X)
mpnt->vm_flags = VM_STACK_FLAGS | VM_EXEC;
else if (executable_stack == EXSTACK_DISABLE_X)
mpnt->vm_flags = VM_STACK_FLAGS & ~VM_EXEC;
else
mpnt->vm_flags = VM_STACK_FLAGS;
mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC) ?
PAGE_COPY_EXEC : PAGE_COPY;
if ((ret = insert_vm_struct(mm, mpnt))) {
up_write(&mm->mmap_sem);
kmem_cache_free(vm_area_cachep, mpnt);
return ret;
}
mm->stack_vm = mm->total_vm = vma_pages(mpnt);
}
for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
struct page *page = bprm->page[i];
if (page) {
bprm->page[i] = NULL;
install_arg_page(mpnt, page, stack_base);
}
stack_base += PAGE_SIZE;
}
up_write(&mm->mmap_sem);
return 0;
}
EXPORT_SYMBOL(ia32_setup_arg_pages);
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
/* Register vsyscall32 into the ABI table */ /* Register vsyscall32 into the ABI table */
#include <linux/sysctl.h> #include <linux/sysctl.h>
......
...@@ -148,6 +148,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, ...@@ -148,6 +148,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
elf_addr_t *elf_info; elf_addr_t *elf_info;
int ei_index = 0; int ei_index = 0;
struct task_struct *tsk = current; struct task_struct *tsk = current;
struct vm_area_struct *vma;
/* /*
* If this architecture has a platform capability string, copy it * If this architecture has a platform capability string, copy it
...@@ -234,6 +235,15 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, ...@@ -234,6 +235,15 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
sp = (elf_addr_t __user *)bprm->p; sp = (elf_addr_t __user *)bprm->p;
#endif #endif
/*
* Grow the stack manually; some architectures have a limit on how
* far ahead a user-space access may be in order to grow the stack.
*/
vma = find_extend_vma(current->mm, bprm->p);
if (!vma)
return -EFAULT;
/* Now, let's put argc (and argv, envp if appropriate) on the stack */ /* Now, let's put argc (and argv, envp if appropriate) on the stack */
if (__put_user(argc, sp++)) if (__put_user(argc, sp++))
return -EFAULT; return -EFAULT;
...@@ -254,8 +264,8 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, ...@@ -254,8 +264,8 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
size_t len; size_t len;
if (__put_user((elf_addr_t)p, argv++)) if (__put_user((elf_addr_t)p, argv++))
return -EFAULT; return -EFAULT;
len = strnlen_user((void __user *)p, PAGE_SIZE*MAX_ARG_PAGES); len = strnlen_user((void __user *)p, MAX_ARG_STRLEN);
if (!len || len > PAGE_SIZE*MAX_ARG_PAGES) if (!len || len > MAX_ARG_STRLEN)
return 0; return 0;
p += len; p += len;
} }
...@@ -266,8 +276,8 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, ...@@ -266,8 +276,8 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
size_t len; size_t len;
if (__put_user((elf_addr_t)p, envp++)) if (__put_user((elf_addr_t)p, envp++))
return -EFAULT; return -EFAULT;
len = strnlen_user((void __user *)p, PAGE_SIZE*MAX_ARG_PAGES); len = strnlen_user((void __user *)p, MAX_ARG_STRLEN);
if (!len || len > PAGE_SIZE*MAX_ARG_PAGES) if (!len || len > MAX_ARG_STRLEN)
return 0; return 0;
p += len; p += len;
} }
...@@ -826,10 +836,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) ...@@ -826,10 +836,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
} }
/* OK, This is the point of no return */ /* OK, This is the point of no return */
current->mm->start_data = 0;
current->mm->end_data = 0;
current->mm->end_code = 0;
current->mm->mmap = NULL;
current->flags &= ~PF_FORKNOEXEC; current->flags &= ~PF_FORKNOEXEC;
current->mm->def_flags = def_flags; current->mm->def_flags = def_flags;
...@@ -1051,9 +1057,13 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) ...@@ -1051,9 +1057,13 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
compute_creds(bprm); compute_creds(bprm);
current->flags &= ~PF_FORKNOEXEC; current->flags &= ~PF_FORKNOEXEC;
create_elf_tables(bprm, &loc->elf_ex, retval = create_elf_tables(bprm, &loc->elf_ex,
(interpreter_type == INTERPRETER_AOUT), (interpreter_type == INTERPRETER_AOUT),
load_addr, interp_load_addr); load_addr, interp_load_addr);
if (retval < 0) {
send_sig(SIGKILL, current, 0);
goto out;
}
/* N.B. passed_fileno might not be initialized? */ /* N.B. passed_fileno might not be initialized? */
if (interpreter_type == INTERPRETER_AOUT) if (interpreter_type == INTERPRETER_AOUT)
current->mm->arg_start += strlen(passed_fileno) + 1; current->mm->arg_start += strlen(passed_fileno) + 1;
......
...@@ -621,8 +621,8 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm, ...@@ -621,8 +621,8 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
p = (char __user *) current->mm->arg_start; p = (char __user *) current->mm->arg_start;
for (loop = bprm->argc; loop > 0; loop--) { for (loop = bprm->argc; loop > 0; loop--) {
__put_user((elf_caddr_t) p, argv++); __put_user((elf_caddr_t) p, argv++);
len = strnlen_user(p, PAGE_SIZE * MAX_ARG_PAGES); len = strnlen_user(p, MAX_ARG_STRLEN);
if (!len || len > PAGE_SIZE * MAX_ARG_PAGES) if (!len || len > MAX_ARG_STRLEN)
return -EINVAL; return -EINVAL;
p += len; p += len;
} }
...@@ -633,8 +633,8 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm, ...@@ -633,8 +633,8 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
current->mm->env_start = (unsigned long) p; current->mm->env_start = (unsigned long) p;
for (loop = bprm->envc; loop > 0; loop--) { for (loop = bprm->envc; loop > 0; loop--) {
__put_user((elf_caddr_t)(unsigned long) p, envp++); __put_user((elf_caddr_t)(unsigned long) p, envp++);
len = strnlen_user(p, PAGE_SIZE * MAX_ARG_PAGES); len = strnlen_user(p, MAX_ARG_STRLEN);
if (!len || len > PAGE_SIZE * MAX_ARG_PAGES) if (!len || len > MAX_ARG_STRLEN)
return -EINVAL; return -EINVAL;
p += len; p += len;
} }
......
...@@ -126,7 +126,9 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs) ...@@ -126,7 +126,9 @@ static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
goto _ret; goto _ret;
if (!(fmt->flags & MISC_FMT_PRESERVE_ARGV0)) { if (!(fmt->flags & MISC_FMT_PRESERVE_ARGV0)) {
remove_arg_zero(bprm); retval = remove_arg_zero(bprm);
if (retval)
goto _ret;
} }
if (fmt->flags & MISC_FMT_OPEN_BINARY) { if (fmt->flags & MISC_FMT_OPEN_BINARY) {
......
...@@ -67,7 +67,9 @@ static int load_script(struct linux_binprm *bprm,struct pt_regs *regs) ...@@ -67,7 +67,9 @@ static int load_script(struct linux_binprm *bprm,struct pt_regs *regs)
* This is done in reverse order, because of how the * This is done in reverse order, because of how the
* user environment and arguments are stored. * user environment and arguments are stored.
*/ */
remove_arg_zero(bprm); retval = remove_arg_zero(bprm);
if (retval)
return retval;
retval = copy_strings_kernel(1, &bprm->interp, bprm); retval = copy_strings_kernel(1, &bprm->interp, bprm);
if (retval < 0) return retval; if (retval < 0) return retval;
bprm->argc++; bprm->argc++;
......
...@@ -1257,6 +1257,7 @@ static int compat_copy_strings(int argc, compat_uptr_t __user *argv, ...@@ -1257,6 +1257,7 @@ static int compat_copy_strings(int argc, compat_uptr_t __user *argv,
{ {
struct page *kmapped_page = NULL; struct page *kmapped_page = NULL;
char *kaddr = NULL; char *kaddr = NULL;
unsigned long kpos = 0;
int ret; int ret;
while (argc-- > 0) { while (argc-- > 0) {
...@@ -1265,92 +1266,84 @@ static int compat_copy_strings(int argc, compat_uptr_t __user *argv, ...@@ -1265,92 +1266,84 @@ static int compat_copy_strings(int argc, compat_uptr_t __user *argv,
unsigned long pos; unsigned long pos;
if (get_user(str, argv+argc) || if (get_user(str, argv+argc) ||
!(len = strnlen_user(compat_ptr(str), bprm->p))) { !(len = strnlen_user(compat_ptr(str), MAX_ARG_STRLEN))) {
ret = -EFAULT; ret = -EFAULT;
goto out; goto out;
} }
if (bprm->p < len) { if (len > MAX_ARG_STRLEN) {
ret = -E2BIG; ret = -E2BIG;
goto out; goto out;
} }
bprm->p -= len; /* We're going to work our way backwords. */
/* XXX: add architecture specific overflow check here. */
pos = bprm->p; pos = bprm->p;
str += len;
bprm->p -= len;
while (len > 0) { while (len > 0) {
int i, new, err;
int offset, bytes_to_copy; int offset, bytes_to_copy;
struct page *page;
offset = pos % PAGE_SIZE; offset = pos % PAGE_SIZE;
i = pos/PAGE_SIZE; if (offset == 0)
page = bprm->page[i]; offset = PAGE_SIZE;
new = 0;
if (!page) { bytes_to_copy = offset;
page = alloc_page(GFP_HIGHUSER); if (bytes_to_copy > len)
bprm->page[i] = page; bytes_to_copy = len;
if (!page) {
ret = -ENOMEM; offset -= bytes_to_copy;
pos -= bytes_to_copy;
str -= bytes_to_copy;
len -= bytes_to_copy;
if (!kmapped_page || kpos != (pos & PAGE_MASK)) {
struct page *page;
#ifdef CONFIG_STACK_GROWSUP
ret = expand_stack_downwards(bprm->vma, pos);
if (ret < 0) {
/* We've exceed the stack rlimit. */
ret = -E2BIG;
goto out;
}
#endif
ret = get_user_pages(current, bprm->mm, pos,
1, 1, 1, &page, NULL);
if (ret <= 0) {
/* We've exceed the stack rlimit. */
ret = -E2BIG;
goto out; goto out;
} }
new = 1;
}
if (page != kmapped_page) { if (kmapped_page) {
if (kmapped_page) flush_kernel_dcache_page(kmapped_page);
kunmap(kmapped_page); kunmap(kmapped_page);
put_page(kmapped_page);
}
kmapped_page = page; kmapped_page = page;
kaddr = kmap(kmapped_page); kaddr = kmap(kmapped_page);
kpos = pos & PAGE_MASK;
flush_cache_page(bprm->vma, kpos,
page_to_pfn(kmapped_page));
} }
if (new && offset) if (copy_from_user(kaddr+offset, compat_ptr(str),
memset(kaddr, 0, offset); bytes_to_copy)) {
bytes_to_copy = PAGE_SIZE - offset;
if (bytes_to_copy > len) {
bytes_to_copy = len;
if (new)
memset(kaddr+offset+len, 0,
PAGE_SIZE-offset-len);
}
err = copy_from_user(kaddr+offset, compat_ptr(str),
bytes_to_copy);
if (err) {
ret = -EFAULT; ret = -EFAULT;
goto out; goto out;
} }
pos += bytes_to_copy;
str += bytes_to_copy;
len -= bytes_to_copy;
} }
} }
ret = 0; ret = 0;
out: out:
if (kmapped_page) if (kmapped_page) {
flush_kernel_dcache_page(kmapped_page);
kunmap(kmapped_page); kunmap(kmapped_page);
return ret; put_page(kmapped_page);
}
#ifdef CONFIG_MMU
#define free_arg_pages(bprm) do { } while (0)
#else
static inline void free_arg_pages(struct linux_binprm *bprm)
{
int i;
for (i = 0; i < MAX_ARG_PAGES; i++) {
if (bprm->page[i])
__free_page(bprm->page[i]);
bprm->page[i] = NULL;
} }
return ret;
} }
#endif /* CONFIG_MMU */
/* /*
* compat_do_execve() is mostly a copy of do_execve(), with the exception * compat_do_execve() is mostly a copy of do_execve(), with the exception
* that it processes 32 bit argv and envp pointers. * that it processes 32 bit argv and envp pointers.
...@@ -1363,7 +1356,6 @@ int compat_do_execve(char * filename, ...@@ -1363,7 +1356,6 @@ int compat_do_execve(char * filename,
struct linux_binprm *bprm; struct linux_binprm *bprm;
struct file *file; struct file *file;
int retval; int retval;
int i;
retval = -ENOMEM; retval = -ENOMEM;
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
...@@ -1377,24 +1369,19 @@ int compat_do_execve(char * filename, ...@@ -1377,24 +1369,19 @@ int compat_do_execve(char * filename,
sched_exec(); sched_exec();
bprm->p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
bprm->file = file; bprm->file = file;
bprm->filename = filename; bprm->filename = filename;
bprm->interp = filename; bprm->interp = filename;
bprm->mm = mm_alloc();
retval = -ENOMEM;
if (!bprm->mm)
goto out_file;
retval = init_new_context(current, bprm->mm); retval = bprm_mm_init(bprm);
if (retval < 0) if (retval)
goto out_mm; goto out_file;
bprm->argc = compat_count(argv, bprm->p / sizeof(compat_uptr_t)); bprm->argc = compat_count(argv, MAX_ARG_STRINGS);
if ((retval = bprm->argc) < 0) if ((retval = bprm->argc) < 0)
goto out_mm; goto out_mm;
bprm->envc = compat_count(envp, bprm->p / sizeof(compat_uptr_t)); bprm->envc = compat_count(envp, MAX_ARG_STRINGS);
if ((retval = bprm->envc) < 0) if ((retval = bprm->envc) < 0)
goto out_mm; goto out_mm;
...@@ -1421,8 +1408,6 @@ int compat_do_execve(char * filename, ...@@ -1421,8 +1408,6 @@ int compat_do_execve(char * filename,
retval = search_binary_handler(bprm, regs); retval = search_binary_handler(bprm, regs);
if (retval >= 0) { if (retval >= 0) {
free_arg_pages(bprm);
/* execve success */ /* execve success */
security_bprm_free(bprm); security_bprm_free(bprm);
acct_update_integrals(current); acct_update_integrals(current);
...@@ -1431,19 +1416,12 @@ int compat_do_execve(char * filename, ...@@ -1431,19 +1416,12 @@ int compat_do_execve(char * filename,
} }
out: out:
/* Something went wrong, return the inode and free the argument pages*/
for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
struct page * page = bprm->page[i];
if (page)
__free_page(page);
}
if (bprm->security) if (bprm->security)
security_bprm_free(bprm); security_bprm_free(bprm);
out_mm: out_mm:
if (bprm->mm) if (bprm->mm)
mmdrop(bprm->mm); mmput(bprm->mm);
out_file: out_file:
if (bprm->file) { if (bprm->file) {
......
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
#include <asm/tlb.h>
#ifdef CONFIG_KMOD #ifdef CONFIG_KMOD
#include <linux/kmod.h> #include <linux/kmod.h>
...@@ -178,6 +179,207 @@ asmlinkage long sys_uselib(const char __user * library) ...@@ -178,6 +179,207 @@ asmlinkage long sys_uselib(const char __user * library)
goto out; goto out;
} }
#ifdef CONFIG_MMU
static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
int write)
{
struct page *page;
int ret;
#ifdef CONFIG_STACK_GROWSUP
if (write) {
ret = expand_stack_downwards(bprm->vma, pos);
if (ret < 0)
return NULL;
}
#endif
ret = get_user_pages(current, bprm->mm, pos,
1, write, 1, &page, NULL);
if (ret <= 0)
return NULL;
if (write) {
struct rlimit *rlim = current->signal->rlim;
unsigned long size = bprm->vma->vm_end - bprm->vma->vm_start;
/*
* Limit to 1/4-th the stack size for the argv+env strings.
* This ensures that:
* - the remaining binfmt code will not run out of stack space,
* - the program will have a reasonable amount of stack left
* to work from.
*/
if (size > rlim[RLIMIT_STACK].rlim_cur / 4) {
put_page(page);
return NULL;
}
}
return page;
}
static void put_arg_page(struct page *page)
{
put_page(page);
}
static void free_arg_page(struct linux_binprm *bprm, int i)
{
}
static void free_arg_pages(struct linux_binprm *bprm)
{
}
static void flush_arg_page(struct linux_binprm *bprm, unsigned long pos,
struct page *page)
{
flush_cache_page(bprm->vma, pos, page_to_pfn(page));
}
static int __bprm_mm_init(struct linux_binprm *bprm)
{
int err = -ENOMEM;
struct vm_area_struct *vma = NULL;
struct mm_struct *mm = bprm->mm;
bprm->vma = vma = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
if (!vma)
goto err;
down_write(&mm->mmap_sem);
vma->vm_mm = mm;
/*
* Place the stack at the largest stack address the architecture
* supports. Later, we'll move this to an appropriate place. We don't
* use STACK_TOP because that can depend on attributes which aren't
* configured yet.
*/
vma->vm_end = STACK_TOP_MAX;
vma->vm_start = vma->vm_end - PAGE_SIZE;
vma->vm_flags = VM_STACK_FLAGS;
vma->vm_page_prot = protection_map[vma->vm_flags & 0x7];
err = insert_vm_struct(mm, vma);
if (err) {
up_write(&mm->mmap_sem);
goto err;
}
mm->stack_vm = mm->total_vm = 1;
up_write(&mm->mmap_sem);
bprm->p = vma->vm_end - sizeof(void *);
return 0;
err:
if (vma) {
bprm->vma = NULL;
kmem_cache_free(vm_area_cachep, vma);
}
return err;
}
static bool valid_arg_len(struct linux_binprm *bprm, long len)
{
return len <= MAX_ARG_STRLEN;
}
#else
static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
int write)
{
struct page *page;
page = bprm->page[pos / PAGE_SIZE];
if (!page && write) {
page = alloc_page(GFP_HIGHUSER|__GFP_ZERO);
if (!page)
return NULL;
bprm->page[pos / PAGE_SIZE] = page;
}
return page;
}
static void put_arg_page(struct page *page)
{
}
static void free_arg_page(struct linux_binprm *bprm, int i)
{
if (bprm->page[i]) {
__free_page(bprm->page[i]);
bprm->page[i] = NULL;
}
}
static void free_arg_pages(struct linux_binprm *bprm)
{
int i;
for (i = 0; i < MAX_ARG_PAGES; i++)
free_arg_page(bprm, i);
}
static void flush_arg_page(struct linux_binprm *bprm, unsigned long pos,
struct page *page)
{
}
static int __bprm_mm_init(struct linux_binprm *bprm)
{
bprm->p = PAGE_SIZE * MAX_ARG_PAGES - sizeof(void *);
return 0;
}
static bool valid_arg_len(struct linux_binprm *bprm, long len)
{
return len <= bprm->p;
}
#endif /* CONFIG_MMU */
/*
* Create a new mm_struct and populate it with a temporary stack
* vm_area_struct. We don't have enough context at this point to set the stack
* flags, permissions, and offset, so we use temporary values. We'll update
* them later in setup_arg_pages().
*/
int bprm_mm_init(struct linux_binprm *bprm)
{
int err;
struct mm_struct *mm = NULL;
bprm->mm = mm = mm_alloc();
err = -ENOMEM;
if (!mm)
goto err;
err = init_new_context(current, mm);
if (err)
goto err;
err = __bprm_mm_init(bprm);
if (err)
goto err;
return 0;
err:
if (mm) {
bprm->mm = NULL;
mmdrop(mm);
}
return err;
}
/* /*
* count() counts the number of strings in array ARGV. * count() counts the number of strings in array ARGV.
*/ */
...@@ -203,15 +405,16 @@ static int count(char __user * __user * argv, int max) ...@@ -203,15 +405,16 @@ static int count(char __user * __user * argv, int max)
} }
/* /*
* 'copy_strings()' copies argument/environment strings from user * 'copy_strings()' copies argument/environment strings from the old
* memory to free pages in kernel mem. These are in a format ready * processes's memory to the new process's stack. The call to get_user_pages()
* to be put directly into the top of new user memory. * ensures the destination page is created and not swapped out.
*/ */
static int copy_strings(int argc, char __user * __user * argv, static int copy_strings(int argc, char __user * __user * argv,
struct linux_binprm *bprm) struct linux_binprm *bprm)
{ {
struct page *kmapped_page = NULL; struct page *kmapped_page = NULL;
char *kaddr = NULL; char *kaddr = NULL;
unsigned long kpos = 0;
int ret; int ret;
while (argc-- > 0) { while (argc-- > 0) {
...@@ -220,69 +423,69 @@ static int copy_strings(int argc, char __user * __user * argv, ...@@ -220,69 +423,69 @@ static int copy_strings(int argc, char __user * __user * argv,
unsigned long pos; unsigned long pos;
if (get_user(str, argv+argc) || if (get_user(str, argv+argc) ||
!(len = strnlen_user(str, bprm->p))) { !(len = strnlen_user(str, MAX_ARG_STRLEN))) {
ret = -EFAULT; ret = -EFAULT;
goto out; goto out;
} }
if (bprm->p < len) { if (!valid_arg_len(bprm, len)) {
ret = -E2BIG; ret = -E2BIG;
goto out; goto out;
} }
bprm->p -= len; /* We're going to work our way backwords. */
/* XXX: add architecture specific overflow check here. */
pos = bprm->p; pos = bprm->p;
str += len;
bprm->p -= len;
while (len > 0) { while (len > 0) {
int i, new, err;
int offset, bytes_to_copy; int offset, bytes_to_copy;
struct page *page;
offset = pos % PAGE_SIZE; offset = pos % PAGE_SIZE;
i = pos/PAGE_SIZE; if (offset == 0)
page = bprm->page[i]; offset = PAGE_SIZE;
new = 0;
if (!page) { bytes_to_copy = offset;
page = alloc_page(GFP_HIGHUSER); if (bytes_to_copy > len)
bprm->page[i] = page; bytes_to_copy = len;
offset -= bytes_to_copy;
pos -= bytes_to_copy;
str -= bytes_to_copy;
len -= bytes_to_copy;
if (!kmapped_page || kpos != (pos & PAGE_MASK)) {
struct page *page;
page = get_arg_page(bprm, pos, 1);
if (!page) { if (!page) {
ret = -ENOMEM; ret = -E2BIG;
goto out; goto out;
} }
new = 1;
}
if (page != kmapped_page) { if (kmapped_page) {
if (kmapped_page) flush_kernel_dcache_page(kmapped_page);
kunmap(kmapped_page); kunmap(kmapped_page);
put_arg_page(kmapped_page);
}
kmapped_page = page; kmapped_page = page;
kaddr = kmap(kmapped_page); kaddr = kmap(kmapped_page);
kpos = pos & PAGE_MASK;
flush_arg_page(bprm, kpos, kmapped_page);
} }
if (new && offset) if (copy_from_user(kaddr+offset, str, bytes_to_copy)) {
memset(kaddr, 0, offset);
bytes_to_copy = PAGE_SIZE - offset;
if (bytes_to_copy > len) {
bytes_to_copy = len;
if (new)
memset(kaddr+offset+len, 0,
PAGE_SIZE-offset-len);
}
err = copy_from_user(kaddr+offset, str, bytes_to_copy);
if (err) {
ret = -EFAULT; ret = -EFAULT;
goto out; goto out;
} }
pos += bytes_to_copy;
str += bytes_to_copy;
len -= bytes_to_copy;
} }
} }
ret = 0; ret = 0;
out: out:
if (kmapped_page) if (kmapped_page) {
flush_kernel_dcache_page(kmapped_page);
kunmap(kmapped_page); kunmap(kmapped_page);
put_arg_page(kmapped_page);
}
return ret; return ret;
} }
...@@ -298,181 +501,172 @@ int copy_strings_kernel(int argc,char ** argv, struct linux_binprm *bprm) ...@@ -298,181 +501,172 @@ int copy_strings_kernel(int argc,char ** argv, struct linux_binprm *bprm)
set_fs(oldfs); set_fs(oldfs);
return r; return r;
} }
EXPORT_SYMBOL(copy_strings_kernel); EXPORT_SYMBOL(copy_strings_kernel);
#ifdef CONFIG_MMU #ifdef CONFIG_MMU
/* /*
* This routine is used to map in a page into an address space: needed by * During bprm_mm_init(), we create a temporary stack at STACK_TOP_MAX. Once
* execve() for the initial stack and environment pages. * the binfmt code determines where the new stack should reside, we shift it to
* its final location. The process proceeds as follows:
* *
* vma->vm_mm->mmap_sem is held for writing. * 1) Use shift to calculate the new vma endpoints.
* 2) Extend vma to cover both the old and new ranges. This ensures the
* arguments passed to subsequent functions are consistent.
* 3) Move vma's page tables to the new range.
* 4) Free up any cleared pgd range.
* 5) Shrink the vma to cover only the new range.
*/ */
void install_arg_page(struct vm_area_struct *vma, static int shift_arg_pages(struct vm_area_struct *vma, unsigned long shift)
struct page *page, unsigned long address)
{ {
struct mm_struct *mm = vma->vm_mm; struct mm_struct *mm = vma->vm_mm;
pte_t * pte; unsigned long old_start = vma->vm_start;
spinlock_t *ptl; unsigned long old_end = vma->vm_end;
unsigned long length = old_end - old_start;
unsigned long new_start = old_start - shift;
unsigned long new_end = old_end - shift;
struct mmu_gather *tlb;
if (unlikely(anon_vma_prepare(vma))) BUG_ON(new_start > new_end);
goto out;
flush_dcache_page(page); /*
pte = get_locked_pte(mm, address, &ptl); * ensure there are no vmas between where we want to go
if (!pte) * and where we are
goto out; */
if (!pte_none(*pte)) { if (vma != find_vma(mm, new_start))
pte_unmap_unlock(pte, ptl); return -EFAULT;
goto out;
/*
* cover the whole range: [new_start, old_end)
*/
vma_adjust(vma, new_start, old_end, vma->vm_pgoff, NULL);
/*
* move the page tables downwards, on failure we rely on
* process cleanup to remove whatever mess we made.
*/
if (length != move_page_tables(vma, old_start,
vma, new_start, length))
return -ENOMEM;
lru_add_drain();
tlb = tlb_gather_mmu(mm, 0);
if (new_end > old_start) {
/*
* when the old and new regions overlap clear from new_end.
*/
free_pgd_range(&tlb, new_end, old_end, new_end,
vma->vm_next ? vma->vm_next->vm_start : 0);
} else {
/*
* otherwise, clean from old_start; this is done to not touch
* the address space in [new_end, old_start) some architectures
* have constraints on va-space that make this illegal (IA64) -
* for the others its just a little faster.
*/
free_pgd_range(&tlb, old_start, old_end, new_end,
vma->vm_next ? vma->vm_next->vm_start : 0);
} }
inc_mm_counter(mm, anon_rss); tlb_finish_mmu(tlb, new_end, old_end);
lru_cache_add_active(page);
set_pte_at(mm, address, pte, pte_mkdirty(pte_mkwrite(mk_pte( /*
page, vma->vm_page_prot)))); * shrink the vma to just the new range.
page_add_new_anon_rmap(page, vma, address); */
pte_unmap_unlock(pte, ptl); vma_adjust(vma, new_start, new_end, vma->vm_pgoff, NULL);
/* no need for flush_tlb */ return 0;
return;
out:
__free_page(page);
force_sig(SIGKILL, current);
} }
#define EXTRA_STACK_VM_PAGES 20 /* random */ #define EXTRA_STACK_VM_PAGES 20 /* random */
/*
* Finalizes the stack vm_area_struct. The flags and permissions are updated,
* the stack is optionally relocated, and some extra space is added.
*/
int setup_arg_pages(struct linux_binprm *bprm, int setup_arg_pages(struct linux_binprm *bprm,
unsigned long stack_top, unsigned long stack_top,
int executable_stack) int executable_stack)
{ {
unsigned long stack_base; unsigned long ret;
struct vm_area_struct *mpnt; unsigned long stack_shift;
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
int i, ret; struct vm_area_struct *vma = bprm->vma;
long arg_size; struct vm_area_struct *prev = NULL;
unsigned long vm_flags;
unsigned long stack_base;
#ifdef CONFIG_STACK_GROWSUP #ifdef CONFIG_STACK_GROWSUP
/* Move the argument and environment strings to the bottom of the
* stack space.
*/
int offset, j;
char *to, *from;
/* Start by shifting all the pages down */
i = 0;
for (j = 0; j < MAX_ARG_PAGES; j++) {
struct page *page = bprm->page[j];
if (!page)
continue;
bprm->page[i++] = page;
}
/* Now move them within their pages */
offset = bprm->p % PAGE_SIZE;
to = kmap(bprm->page[0]);
for (j = 1; j < i; j++) {
memmove(to, to + offset, PAGE_SIZE - offset);
from = kmap(bprm->page[j]);
memcpy(to + PAGE_SIZE - offset, from, offset);
kunmap(bprm->page[j - 1]);
to = from;
}
memmove(to, to + offset, PAGE_SIZE - offset);
kunmap(bprm->page[j - 1]);
/* Limit stack size to 1GB */ /* Limit stack size to 1GB */
stack_base = current->signal->rlim[RLIMIT_STACK].rlim_max; stack_base = current->signal->rlim[RLIMIT_STACK].rlim_max;
if (stack_base > (1 << 30)) if (stack_base > (1 << 30))
stack_base = 1 << 30; stack_base = 1 << 30;
stack_base = PAGE_ALIGN(stack_top - stack_base);
/* Adjust bprm->p to point to the end of the strings. */ /* Make sure we didn't let the argument array grow too large. */
bprm->p = stack_base + PAGE_SIZE * i - offset; if (vma->vm_end - vma->vm_start > stack_base)
return -ENOMEM;
mm->arg_start = stack_base; stack_base = PAGE_ALIGN(stack_top - stack_base);
arg_size = i << PAGE_SHIFT;
/* zero pages that were copied above */ stack_shift = vma->vm_start - stack_base;
while (i < MAX_ARG_PAGES) mm->arg_start = bprm->p - stack_shift;
bprm->page[i++] = NULL; bprm->p = vma->vm_end - stack_shift;
#else #else
stack_base = arch_align_stack(stack_top - MAX_ARG_PAGES*PAGE_SIZE); stack_top = arch_align_stack(stack_top);
stack_base = PAGE_ALIGN(stack_base); stack_top = PAGE_ALIGN(stack_top);
bprm->p += stack_base; stack_shift = vma->vm_end - stack_top;
bprm->p -= stack_shift;
mm->arg_start = bprm->p; mm->arg_start = bprm->p;
arg_size = stack_top - (PAGE_MASK & (unsigned long) mm->arg_start);
#endif #endif
arg_size += EXTRA_STACK_VM_PAGES * PAGE_SIZE;
if (bprm->loader) if (bprm->loader)
bprm->loader += stack_base; bprm->loader -= stack_shift;
bprm->exec += stack_base; bprm->exec -= stack_shift;
mpnt = kmem_cache_zalloc(vm_area_cachep, GFP_KERNEL);
if (!mpnt)
return -ENOMEM;
down_write(&mm->mmap_sem); down_write(&mm->mmap_sem);
{ vm_flags = vma->vm_flags;
mpnt->vm_mm = mm;
#ifdef CONFIG_STACK_GROWSUP /*
mpnt->vm_start = stack_base; * Adjust stack execute permissions; explicitly enable for
mpnt->vm_end = stack_base + arg_size; * EXSTACK_ENABLE_X, disable for EXSTACK_DISABLE_X and leave alone
#else * (arch default) otherwise.
mpnt->vm_end = stack_top; */
mpnt->vm_start = mpnt->vm_end - arg_size; if (unlikely(executable_stack == EXSTACK_ENABLE_X))
#endif vm_flags |= VM_EXEC;
/* Adjust stack execute permissions; explicitly enable else if (executable_stack == EXSTACK_DISABLE_X)
* for EXSTACK_ENABLE_X, disable for EXSTACK_DISABLE_X vm_flags &= ~VM_EXEC;
* and leave alone (arch default) otherwise. */ vm_flags |= mm->def_flags;
if (unlikely(executable_stack == EXSTACK_ENABLE_X))
mpnt->vm_flags = VM_STACK_FLAGS | VM_EXEC; ret = mprotect_fixup(vma, &prev, vma->vm_start, vma->vm_end,
else if (executable_stack == EXSTACK_DISABLE_X) vm_flags);
mpnt->vm_flags = VM_STACK_FLAGS & ~VM_EXEC; if (ret)
else goto out_unlock;
mpnt->vm_flags = VM_STACK_FLAGS; BUG_ON(prev != vma);
mpnt->vm_flags |= mm->def_flags;
mpnt->vm_page_prot = protection_map[mpnt->vm_flags & 0x7]; /* Move stack pages down in memory. */
if ((ret = insert_vm_struct(mm, mpnt))) { if (stack_shift) {
ret = shift_arg_pages(vma, stack_shift);
if (ret) {
up_write(&mm->mmap_sem); up_write(&mm->mmap_sem);
kmem_cache_free(vm_area_cachep, mpnt);
return ret; return ret;
} }
mm->stack_vm = mm->total_vm = vma_pages(mpnt);
} }
for (i = 0 ; i < MAX_ARG_PAGES ; i++) { #ifdef CONFIG_STACK_GROWSUP
struct page *page = bprm->page[i]; stack_base = vma->vm_end + EXTRA_STACK_VM_PAGES * PAGE_SIZE;
if (page) { #else
bprm->page[i] = NULL; stack_base = vma->vm_start - EXTRA_STACK_VM_PAGES * PAGE_SIZE;
install_arg_page(mpnt, page, stack_base); #endif
} ret = expand_stack(vma, stack_base);
stack_base += PAGE_SIZE; if (ret)
} ret = -EFAULT;
out_unlock:
up_write(&mm->mmap_sem); up_write(&mm->mmap_sem);
return 0; return 0;
} }
EXPORT_SYMBOL(setup_arg_pages); EXPORT_SYMBOL(setup_arg_pages);
#define free_arg_pages(bprm) do { } while (0)
#else
static inline void free_arg_pages(struct linux_binprm *bprm)
{
int i;
for (i = 0; i < MAX_ARG_PAGES; i++) {
if (bprm->page[i])
__free_page(bprm->page[i]);
bprm->page[i] = NULL;
}
}
#endif /* CONFIG_MMU */ #endif /* CONFIG_MMU */
struct file *open_exec(const char *name) struct file *open_exec(const char *name)
...@@ -1000,43 +1194,42 @@ EXPORT_SYMBOL(compute_creds); ...@@ -1000,43 +1194,42 @@ EXPORT_SYMBOL(compute_creds);
* points to; chop off the first by relocating brpm->p to right after * points to; chop off the first by relocating brpm->p to right after
* the first '\0' encountered. * the first '\0' encountered.
*/ */
void remove_arg_zero(struct linux_binprm *bprm) int remove_arg_zero(struct linux_binprm *bprm)
{ {
if (bprm->argc) { int ret = 0;
char ch; unsigned long offset;
char *kaddr;
struct page *page;
do { if (!bprm->argc)
unsigned long offset; return 0;
unsigned long index;
char *kaddr;
struct page *page;
offset = bprm->p & ~PAGE_MASK;
index = bprm->p >> PAGE_SHIFT;
page = bprm->page[index]; do {
kaddr = kmap_atomic(page, KM_USER0); offset = bprm->p & ~PAGE_MASK;
page = get_arg_page(bprm, bprm->p, 0);
if (!page) {
ret = -EFAULT;
goto out;
}
kaddr = kmap_atomic(page, KM_USER0);
/* run through page until we reach end or find NUL */ for (; offset < PAGE_SIZE && kaddr[offset];
do { offset++, bprm->p++)
ch = *(kaddr + offset); ;
/* discard that character... */ kunmap_atomic(kaddr, KM_USER0);
bprm->p++; put_arg_page(page);
offset++;
} while (offset < PAGE_SIZE && ch != '\0');
kunmap_atomic(kaddr, KM_USER0); if (offset == PAGE_SIZE)
free_arg_page(bprm, (bprm->p >> PAGE_SHIFT) - 1);
} while (offset == PAGE_SIZE);
/* free the old page */ bprm->p++;
if (offset == PAGE_SIZE) { bprm->argc--;
__free_page(page); ret = 0;
bprm->page[index] = NULL;
}
} while (ch != '\0');
bprm->argc--; out:
} return ret;
} }
EXPORT_SYMBOL(remove_arg_zero); EXPORT_SYMBOL(remove_arg_zero);
...@@ -1062,7 +1255,7 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs) ...@@ -1062,7 +1255,7 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
fput(bprm->file); fput(bprm->file);
bprm->file = NULL; bprm->file = NULL;
loader = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); loader = bprm->vma->vm_end - sizeof(void *);
file = open_exec("/sbin/loader"); file = open_exec("/sbin/loader");
retval = PTR_ERR(file); retval = PTR_ERR(file);
...@@ -1156,7 +1349,6 @@ int do_execve(char * filename, ...@@ -1156,7 +1349,6 @@ int do_execve(char * filename,
struct file *file; struct file *file;
unsigned long env_p; unsigned long env_p;
int retval; int retval;
int i;
retval = -ENOMEM; retval = -ENOMEM;
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL); bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
...@@ -1170,25 +1362,19 @@ int do_execve(char * filename, ...@@ -1170,25 +1362,19 @@ int do_execve(char * filename,
sched_exec(); sched_exec();
bprm->p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
bprm->file = file; bprm->file = file;
bprm->filename = filename; bprm->filename = filename;
bprm->interp = filename; bprm->interp = filename;
bprm->mm = mm_alloc();
retval = -ENOMEM;
if (!bprm->mm)
goto out_file;
retval = init_new_context(current, bprm->mm); retval = bprm_mm_init(bprm);
if (retval < 0) if (retval)
goto out_mm; goto out_file;
bprm->argc = count(argv, bprm->p / sizeof(void *)); bprm->argc = count(argv, MAX_ARG_STRINGS);
if ((retval = bprm->argc) < 0) if ((retval = bprm->argc) < 0)
goto out_mm; goto out_mm;
bprm->envc = count(envp, bprm->p / sizeof(void *)); bprm->envc = count(envp, MAX_ARG_STRINGS);
if ((retval = bprm->envc) < 0) if ((retval = bprm->envc) < 0)
goto out_mm; goto out_mm;
...@@ -1217,9 +1403,8 @@ int do_execve(char * filename, ...@@ -1217,9 +1403,8 @@ int do_execve(char * filename,
retval = search_binary_handler(bprm,regs); retval = search_binary_handler(bprm,regs);
if (retval >= 0) { if (retval >= 0) {
free_arg_pages(bprm);
/* execve success */ /* execve success */
free_arg_pages(bprm);
security_bprm_free(bprm); security_bprm_free(bprm);
acct_update_integrals(current); acct_update_integrals(current);
kfree(bprm); kfree(bprm);
...@@ -1227,26 +1412,19 @@ int do_execve(char * filename, ...@@ -1227,26 +1412,19 @@ int do_execve(char * filename,
} }
out: out:
/* Something went wrong, return the inode and free the argument pages*/ free_arg_pages(bprm);
for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
struct page * page = bprm->page[i];
if (page)
__free_page(page);
}
if (bprm->security) if (bprm->security)
security_bprm_free(bprm); security_bprm_free(bprm);
out_mm: out_mm:
if (bprm->mm) if (bprm->mm)
mmdrop(bprm->mm); mmput (bprm->mm);
out_file: out_file:
if (bprm->file) { if (bprm->file) {
allow_write_access(bprm->file); allow_write_access(bprm->file);
fput(bprm->file); fput(bprm->file);
} }
out_kfree: out_kfree:
kfree(bprm); kfree(bprm);
......
...@@ -6,11 +6,13 @@ ...@@ -6,11 +6,13 @@
struct pt_regs; struct pt_regs;
/* /*
* MAX_ARG_PAGES defines the number of pages allocated for arguments * These are the maximum length and maximum number of strings passed to the
* and envelope for the new program. 32 should suffice, this gives * execve() system call. MAX_ARG_STRLEN is essentially random but serves to
* a maximum env+arg of 128kB w/4KB pages! * prevent the kernel from being unduly impacted by misaddressed pointers.
* MAX_ARG_STRINGS is chosen to fit in a signed 32-bit integer.
*/ */
#define MAX_ARG_PAGES 32 #define MAX_ARG_STRLEN (PAGE_SIZE * 32)
#define MAX_ARG_STRINGS 0x7FFFFFFF
/* sizeof(linux_binprm->buf) */ /* sizeof(linux_binprm->buf) */
#define BINPRM_BUF_SIZE 128 #define BINPRM_BUF_SIZE 128
...@@ -24,7 +26,12 @@ struct pt_regs; ...@@ -24,7 +26,12 @@ struct pt_regs;
*/ */
struct linux_binprm{ struct linux_binprm{
char buf[BINPRM_BUF_SIZE]; char buf[BINPRM_BUF_SIZE];
#ifdef CONFIG_MMU
struct vm_area_struct *vma;
#else
# define MAX_ARG_PAGES 32
struct page *page[MAX_ARG_PAGES]; struct page *page[MAX_ARG_PAGES];
#endif
struct mm_struct *mm; struct mm_struct *mm;
unsigned long p; /* current top of mem */ unsigned long p; /* current top of mem */
int sh_bang; int sh_bang;
...@@ -69,7 +76,7 @@ extern int register_binfmt(struct linux_binfmt *); ...@@ -69,7 +76,7 @@ extern int register_binfmt(struct linux_binfmt *);
extern int unregister_binfmt(struct linux_binfmt *); extern int unregister_binfmt(struct linux_binfmt *);
extern int prepare_binprm(struct linux_binprm *); extern int prepare_binprm(struct linux_binprm *);
extern void remove_arg_zero(struct linux_binprm *); extern int __must_check remove_arg_zero(struct linux_binprm *);
extern int search_binary_handler(struct linux_binprm *,struct pt_regs *); extern int search_binary_handler(struct linux_binprm *,struct pt_regs *);
extern int flush_old_exec(struct linux_binprm * bprm); extern int flush_old_exec(struct linux_binprm * bprm);
...@@ -86,6 +93,7 @@ extern int suid_dumpable; ...@@ -86,6 +93,7 @@ extern int suid_dumpable;
extern int setup_arg_pages(struct linux_binprm * bprm, extern int setup_arg_pages(struct linux_binprm * bprm,
unsigned long stack_top, unsigned long stack_top,
int executable_stack); int executable_stack);
extern int bprm_mm_init(struct linux_binprm *bprm);
extern int copy_strings_kernel(int argc,char ** argv,struct linux_binprm *bprm); extern int copy_strings_kernel(int argc,char ** argv,struct linux_binprm *bprm);
extern void compute_creds(struct linux_binprm *binprm); extern void compute_creds(struct linux_binprm *binprm);
extern int do_coredump(long signr, int exit_code, struct pt_regs * regs); extern int do_coredump(long signr, int exit_code, struct pt_regs * regs);
......
...@@ -808,7 +808,6 @@ static inline int handle_mm_fault(struct mm_struct *mm, ...@@ -808,7 +808,6 @@ static inline int handle_mm_fault(struct mm_struct *mm,
extern int make_pages_present(unsigned long addr, unsigned long end); extern int make_pages_present(unsigned long addr, unsigned long end);
extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write); extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write);
void install_arg_page(struct vm_area_struct *, struct page *, unsigned long);
int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start,
int len, int write, int force, struct page **pages, struct vm_area_struct **vmas); int len, int write, int force, struct page **pages, struct vm_area_struct **vmas);
...@@ -825,9 +824,15 @@ int FASTCALL(set_page_dirty(struct page *page)); ...@@ -825,9 +824,15 @@ int FASTCALL(set_page_dirty(struct page *page));
int set_page_dirty_lock(struct page *page); int set_page_dirty_lock(struct page *page);
int clear_page_dirty_for_io(struct page *page); int clear_page_dirty_for_io(struct page *page);
extern unsigned long move_page_tables(struct vm_area_struct *vma,
unsigned long old_addr, struct vm_area_struct *new_vma,
unsigned long new_addr, unsigned long len);
extern unsigned long do_mremap(unsigned long addr, extern unsigned long do_mremap(unsigned long addr,
unsigned long old_len, unsigned long new_len, unsigned long old_len, unsigned long new_len,
unsigned long flags, unsigned long new_addr); unsigned long flags, unsigned long new_addr);
extern int mprotect_fixup(struct vm_area_struct *vma,
struct vm_area_struct **pprev, unsigned long start,
unsigned long end, unsigned long newflags);
/* /*
* A callback you can register to apply pressure to ageable caches. * A callback you can register to apply pressure to ageable caches.
...@@ -1159,6 +1164,8 @@ extern int expand_stack(struct vm_area_struct *vma, unsigned long address); ...@@ -1159,6 +1164,8 @@ extern int expand_stack(struct vm_area_struct *vma, unsigned long address);
#ifdef CONFIG_IA64 #ifdef CONFIG_IA64
extern int expand_upwards(struct vm_area_struct *vma, unsigned long address); extern int expand_upwards(struct vm_area_struct *vma, unsigned long address);
#endif #endif
extern int expand_stack_downwards(struct vm_area_struct *vma,
unsigned long address);
/* Look up the first VMA which satisfies addr < vm_end, NULL if none. */ /* Look up the first VMA which satisfies addr < vm_end, NULL if none. */
extern struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr); extern struct vm_area_struct * find_vma(struct mm_struct * mm, unsigned long addr);
......
...@@ -843,7 +843,7 @@ static void audit_log_execve_info(struct audit_buffer *ab, ...@@ -843,7 +843,7 @@ static void audit_log_execve_info(struct audit_buffer *ab,
return; /* execve failed, no additional info */ return; /* execve failed, no additional info */
for (i = 0; i < axi->argc; i++, p += len) { for (i = 0; i < axi->argc; i++, p += len) {
len = strnlen_user(p, MAX_ARG_PAGES*PAGE_SIZE); len = strnlen_user(p, MAX_ARG_STRLEN);
/* /*
* We just created this mm, if we can't find the strings * We just created this mm, if we can't find the strings
* we just copied into it something is _very_ wrong. Similar * we just copied into it something is _very_ wrong. Similar
......
...@@ -1571,33 +1571,11 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address) ...@@ -1571,33 +1571,11 @@ int expand_upwards(struct vm_area_struct *vma, unsigned long address)
} }
#endif /* CONFIG_STACK_GROWSUP || CONFIG_IA64 */ #endif /* CONFIG_STACK_GROWSUP || CONFIG_IA64 */
#ifdef CONFIG_STACK_GROWSUP
int expand_stack(struct vm_area_struct *vma, unsigned long address)
{
return expand_upwards(vma, address);
}
struct vm_area_struct *
find_extend_vma(struct mm_struct *mm, unsigned long addr)
{
struct vm_area_struct *vma, *prev;
addr &= PAGE_MASK;
vma = find_vma_prev(mm, addr, &prev);
if (vma && (vma->vm_start <= addr))
return vma;
if (!prev || expand_stack(prev, addr))
return NULL;
if (prev->vm_flags & VM_LOCKED) {
make_pages_present(addr, prev->vm_end);
}
return prev;
}
#else
/* /*
* vma is the first one with address < vma->vm_start. Have to extend vma. * vma is the first one with address < vma->vm_start. Have to extend vma.
*/ */
int expand_stack(struct vm_area_struct *vma, unsigned long address) static inline int expand_downwards(struct vm_area_struct *vma,
unsigned long address)
{ {
int error; int error;
...@@ -1634,6 +1612,38 @@ int expand_stack(struct vm_area_struct *vma, unsigned long address) ...@@ -1634,6 +1612,38 @@ int expand_stack(struct vm_area_struct *vma, unsigned long address)
return error; return error;
} }
int expand_stack_downwards(struct vm_area_struct *vma, unsigned long address)
{
return expand_downwards(vma, address);
}
#ifdef CONFIG_STACK_GROWSUP
int expand_stack(struct vm_area_struct *vma, unsigned long address)
{
return expand_upwards(vma, address);
}
struct vm_area_struct *
find_extend_vma(struct mm_struct *mm, unsigned long addr)
{
struct vm_area_struct *vma, *prev;
addr &= PAGE_MASK;
vma = find_vma_prev(mm, addr, &prev);
if (vma && (vma->vm_start <= addr))
return vma;
if (!prev || expand_stack(prev, addr))
return NULL;
if (prev->vm_flags & VM_LOCKED)
make_pages_present(addr, prev->vm_end);
return prev;
}
#else
int expand_stack(struct vm_area_struct *vma, unsigned long address)
{
return expand_downwards(vma, address);
}
struct vm_area_struct * struct vm_area_struct *
find_extend_vma(struct mm_struct * mm, unsigned long addr) find_extend_vma(struct mm_struct * mm, unsigned long addr)
{ {
...@@ -1651,9 +1661,8 @@ find_extend_vma(struct mm_struct * mm, unsigned long addr) ...@@ -1651,9 +1661,8 @@ find_extend_vma(struct mm_struct * mm, unsigned long addr)
start = vma->vm_start; start = vma->vm_start;
if (expand_stack(vma, addr)) if (expand_stack(vma, addr))
return NULL; return NULL;
if (vma->vm_flags & VM_LOCKED) { if (vma->vm_flags & VM_LOCKED)
make_pages_present(addr, start); make_pages_present(addr, start);
}
return vma; return vma;
} }
#endif #endif
......
...@@ -128,7 +128,7 @@ static void change_protection(struct vm_area_struct *vma, ...@@ -128,7 +128,7 @@ static void change_protection(struct vm_area_struct *vma,
flush_tlb_range(vma, start, end); flush_tlb_range(vma, start, end);
} }
static int int
mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev, mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev,
unsigned long start, unsigned long end, unsigned long newflags) unsigned long start, unsigned long end, unsigned long newflags)
{ {
......
...@@ -120,7 +120,7 @@ static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd, ...@@ -120,7 +120,7 @@ static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd,
#define LATENCY_LIMIT (64 * PAGE_SIZE) #define LATENCY_LIMIT (64 * PAGE_SIZE)
static unsigned long move_page_tables(struct vm_area_struct *vma, unsigned long move_page_tables(struct vm_area_struct *vma,
unsigned long old_addr, struct vm_area_struct *new_vma, unsigned long old_addr, struct vm_area_struct *new_vma,
unsigned long new_addr, unsigned long len) unsigned long new_addr, unsigned long len)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment