diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 13:25:08 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 13:25:08 +0000 |
commit | 59223edaa18759982db0a8aced0e77457d10c68e (patch) | |
tree | 89354903b01fa0a447bffeefe00df3044495db2e /fs/exec.c | |
parent | db7d4daea91e105e3859cf461d7e53b9b77454b2 (diff) |
Merge with Linux 2.3.6. Sorry, this isn't tested on silicon, I don't
have a MIPS box at hand.
Diffstat (limited to 'fs/exec.c')
-rw-r--r-- | fs/exec.c | 136 |
1 files changed, 66 insertions, 70 deletions
@@ -222,78 +222,64 @@ static int count(char ** argv) } /* - * 'copy_string()' copies argument/envelope strings from user + * 'copy_strings()' copies argument/envelope strings from user * memory to free pages in kernel mem. These are in a format ready * to be put directly into the top of new user memory. - * - * Modified by TYT, 11/24/91 to add the from_kmem argument, which specifies - * whether the string and the string array are from user or kernel segments: - * - * from_kmem argv * argv ** - * 0 user space user space - * 1 kernel space user space - * 2 kernel space kernel space - * - * We do this by playing games with the fs segment register. Since it - * is expensive to load a segment register, we try to avoid calling - * set_fs() unless we absolutely have to. */ -unsigned long copy_strings(int argc,char ** argv,unsigned long *page, - unsigned long p, int from_kmem) +int copy_strings(int argc,char ** argv, struct linux_binprm *bprm) { - char *str; - mm_segment_t old_fs; - - if (!p) - return 0; /* bullet-proofing */ - old_fs = get_fs(); - if (from_kmem==2) - set_fs(KERNEL_DS); while (argc-- > 0) { + char *str; int len; unsigned long pos; - if (from_kmem == 1) - set_fs(KERNEL_DS); - get_user(str, argv+argc); - if (!str) - panic("VFS: argc is wrong"); - if (from_kmem == 1) - set_fs(old_fs); - len = strlen_user(str); /* includes the '\0' */ - if (p < len) { /* this shouldn't happen - 128kB */ - set_fs(old_fs); - return 0; - } - p -= len; - pos = p; + if (get_user(str, argv+argc) || !str || !(len = strlen_user(str))) + return -EFAULT; + if (bprm->p < len) + return -E2BIG; + + bprm->p -= len; + /* XXX: add architecture specific overflow check here. */ + + pos = bprm->p; while (len) { char *pag; int offset, bytes_to_copy; offset = pos % PAGE_SIZE; - if (!(pag = (char *) page[pos/PAGE_SIZE]) && - !(pag = (char *) page[pos/PAGE_SIZE] = - (unsigned long *) get_free_page(GFP_USER))) { - if (from_kmem==2) - set_fs(old_fs); - return 0; - } + if (!(pag = (char *) bprm->page[pos/PAGE_SIZE]) && + !(pag = (char *) bprm->page[pos/PAGE_SIZE] = + (unsigned long *) get_free_page(GFP_USER))) + return -ENOMEM; + bytes_to_copy = PAGE_SIZE - offset; if (bytes_to_copy > len) bytes_to_copy = len; - copy_from_user(pag + offset, str, bytes_to_copy); + if (copy_from_user(pag + offset, str, bytes_to_copy)) + return -EFAULT; + pos += bytes_to_copy; str += bytes_to_copy; len -= bytes_to_copy; } } - if (from_kmem==2) - set_fs(old_fs); - return p; + return 0; } -unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm) +/* + * Like copy_strings, but get argv and its values from kernel memory. + */ +int copy_strings_kernel(int argc,char ** argv, struct linux_binprm *bprm) +{ + int r; + mm_segment_t oldfs = get_fs(); + set_fs(KERNEL_DS); + r = copy_strings(argc, argv, bprm); + set_fs(oldfs); + return r; +} + +int setup_arg_pages(struct linux_binprm *bprm) { unsigned long stack_base; struct vm_area_struct *mpnt; @@ -301,15 +287,18 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm) stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE; - p += stack_base; + bprm->p += stack_base; if (bprm->loader) bprm->loader += stack_base; bprm->exec += stack_base; mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); - if (mpnt) { + if (!mpnt) + return -ENOMEM; + + { mpnt->vm_mm = current->mm; - mpnt->vm_start = PAGE_MASK & (unsigned long) p; + mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; mpnt->vm_end = STACK_TOP; mpnt->vm_page_prot = PAGE_COPY; mpnt->vm_flags = VM_STACK_FLAGS; @@ -319,7 +308,7 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm) mpnt->vm_pte = 0; insert_vm_struct(current->mm, mpnt); current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; - } + } for (i = 0 ; i < MAX_ARG_PAGES ; i++) { if (bprm->page[i]) { @@ -328,7 +317,8 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm) } stack_base += PAGE_SIZE; } - return p; + + return 0; } /* @@ -415,12 +405,11 @@ static int exec_mmap(void) * Failure ... restore the prior mm_struct. */ fail_restore: - /* The pgd belongs to the parent ... don't free it! */ - mm->pgd = NULL; current->mm = old_mm; /* restore the ldt for this task */ copy_segments(nr, current, NULL); - mmput(mm); + release_segments(mm); + kmem_cache_free(mm_cachep, mm); fail_nomem: return retval; @@ -805,8 +794,7 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs int i; bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); - for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */ - bprm.page[i] = 0; + memset(bprm.page, 0, MAX_ARG_PAGES*sizeof(bprm.page[0])); dentry = open_namei(filename, 0, 0); retval = PTR_ERR(dentry); @@ -830,26 +818,34 @@ int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs } retval = prepare_binprm(&bprm); - - if (retval >= 0) { - bprm.p = copy_strings(1, &bprm.filename, bprm.page, bprm.p, 2); - bprm.exec = bprm.p; - bprm.p = copy_strings(bprm.envc,envp,bprm.page,bprm.p,0); - bprm.p = copy_strings(bprm.argc,argv,bprm.page,bprm.p,0); - if (!bprm.p) - retval = -E2BIG; - } + if (retval < 0) + goto out; - if (retval >= 0) - retval = search_binary_handler(&bprm,regs); + retval = copy_strings_kernel(1, &bprm.filename, &bprm); + if (retval < 0) + goto out; + + bprm.exec = bprm.p; + retval = copy_strings(bprm.envc, envp, &bprm); + if (retval < 0) + goto out; + + retval = copy_strings(bprm.argc, argv, &bprm); + if (retval < 0) + goto out; + + retval = search_binary_handler(&bprm,regs); if (retval >= 0) /* execve success */ return retval; +out: /* Something went wrong, return the inode and free the argument pages*/ if (bprm.dentry) dput(bprm.dentry); + /* Assumes that free_page() can take a NULL argument. */ + /* I hope this is ok for all architectures */ for (i=0 ; i<MAX_ARG_PAGES ; i++) free_page(bprm.page[i]); |