Merge branch 'exec_rm_compat' of git://git.kernel.org/pub/scm/linux/kernel/git/oleg...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 23 May 2011 15:28:34 +0000 (08:28 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 23 May 2011 15:28:34 +0000 (08:28 -0700)
* 'exec_rm_compat' of git://git.kernel.org/pub/scm/linux/kernel/git/oleg/misc:
  exec: document acct_arg_size()
  exec: unify do_execve/compat_do_execve code
  exec: introduce struct user_arg_ptr
  exec: introduce get_user_arg_ptr() helper

1  2 
fs/exec.c

diff --combined fs/exec.c
index 8328beb9016f909dc9cef5b169fb8fc33a7fcb8f,5cb53f0232b1c1346a6d315c6b0019052fa97ef5..c016896dcbb2cb2e93016634d6c9b45709d5c833
+++ b/fs/exec.c
@@@ -55,6 -55,7 +55,7 @@@
  #include <linux/fs_struct.h>
  #include <linux/pipe_fs_i.h>
  #include <linux/oom.h>
+ #include <linux/compat.h>
  
  #include <asm/uaccess.h>
  #include <asm/mmu_context.h>
@@@ -166,8 -167,13 +167,13 @@@ out
  }
  
  #ifdef CONFIG_MMU
- void acct_arg_size(struct linux_binprm *bprm, unsigned long pages)
+ /*
+  * The nascent bprm->mm is not visible until exec_mmap() but it can
+  * use a lot of memory, account these pages in current->mm temporary
+  * for oom_badness()->get_mm_rss(). Once exec succeeds or fails, we
+  * change the counter back via acct_arg_size(0).
+  */
+ static void acct_arg_size(struct linux_binprm *bprm, unsigned long pages)
  {
        struct mm_struct *mm = current->mm;
        long diff = (long)(pages - bprm->vma_pages);
  #endif
  }
  
- struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
+ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
                int write)
  {
        struct page *page;
@@@ -305,11 -311,11 +311,11 @@@ static bool valid_arg_len(struct linux_
  
  #else
  
- void acct_arg_size(struct linux_binprm *bprm, unsigned long pages)
static inline void acct_arg_size(struct linux_binprm *bprm, unsigned long pages)
  {
  }
  
- struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
+ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
                int write)
  {
        struct page *page;
@@@ -398,22 -404,56 +404,56 @@@ err
        return err;
  }
  
+ struct user_arg_ptr {
+ #ifdef CONFIG_COMPAT
+       bool is_compat;
+ #endif
+       union {
+               const char __user *const __user *native;
+ #ifdef CONFIG_COMPAT
+               compat_uptr_t __user *compat;
+ #endif
+       } ptr;
+ };
+ static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
+ {
+       const char __user *native;
+ #ifdef CONFIG_COMPAT
+       if (unlikely(argv.is_compat)) {
+               compat_uptr_t compat;
+               if (get_user(compat, argv.ptr.compat + nr))
+                       return ERR_PTR(-EFAULT);
+               return compat_ptr(compat);
+       }
+ #endif
+       if (get_user(native, argv.ptr.native + nr))
+               return ERR_PTR(-EFAULT);
+       return native;
+ }
  /*
   * count() counts the number of strings in array ARGV.
   */
- static int count(const char __user * const __user * argv, int max)
+ static int count(struct user_arg_ptr argv, int max)
  {
        int i = 0;
  
-       if (argv != NULL) {
+       if (argv.ptr.native != NULL) {
                for (;;) {
-                       const char __user * p;
+                       const char __user *p = get_user_arg_ptr(argv, i);
  
-                       if (get_user(p, argv))
-                               return -EFAULT;
                        if (!p)
                                break;
-                       argv++;
+                       if (IS_ERR(p))
+                               return -EFAULT;
                        if (i++ >= max)
                                return -E2BIG;
  
   * processes's memory to the new process's stack.  The call to get_user_pages()
   * ensures the destination page is created and not swapped out.
   */
- static int copy_strings(int argc, const char __user *const __user *argv,
+ static int copy_strings(int argc, struct user_arg_ptr argv,
                        struct linux_binprm *bprm)
  {
        struct page *kmapped_page = NULL;
                int len;
                unsigned long pos;
  
-               if (get_user(str, argv+argc) ||
-                               !(len = strnlen_user(str, MAX_ARG_STRLEN))) {
-                       ret = -EFAULT;
+               ret = -EFAULT;
+               str = get_user_arg_ptr(argv, argc);
+               if (IS_ERR(str))
                        goto out;
-               }
  
-               if (!valid_arg_len(bprm, len)) {
-                       ret = -E2BIG;
+               len = strnlen_user(str, MAX_ARG_STRLEN);
+               if (!len)
+                       goto out;
+               ret = -E2BIG;
+               if (!valid_arg_len(bprm, len))
                        goto out;
-               }
  
                /* We're going to work our way backwords. */
                pos = bprm->p;
@@@ -519,14 -561,19 +561,19 @@@ out
  /*
   * Like copy_strings, but get argv and its values from kernel memory.
   */
- int copy_strings_kernel(int argc, const char *const *argv,
+ int copy_strings_kernel(int argc, const char *const *__argv,
                        struct linux_binprm *bprm)
  {
        int r;
        mm_segment_t oldfs = get_fs();
+       struct user_arg_ptr argv = {
+               .ptr.native = (const char __user *const  __user *)__argv,
+       };
        set_fs(KERNEL_DS);
-       r = copy_strings(argc, (const char __user *const  __user *)argv, bprm);
+       r = copy_strings(argc, argv, bprm);
        set_fs(oldfs);
        return r;
  }
  EXPORT_SYMBOL(copy_strings_kernel);
@@@ -1379,10 -1426,10 +1426,10 @@@ EXPORT_SYMBOL(search_binary_handler)
  /*
   * sys_execve() executes a new program.
   */
int do_execve(const char * filename,
-       const char __user *const __user *argv,
-       const char __user *const __user *envp,
-       struct pt_regs * regs)
static int do_execve_common(const char *filename,
+                               struct user_arg_ptr argv,
+                               struct user_arg_ptr envp,
+                               struct pt_regs *regs)
  {
        struct linux_binprm *bprm;
        struct file *file;
@@@ -1489,6 -1536,34 +1536,34 @@@ out_ret
        return retval;
  }
  
+ int do_execve(const char *filename,
+       const char __user *const __user *__argv,
+       const char __user *const __user *__envp,
+       struct pt_regs *regs)
+ {
+       struct user_arg_ptr argv = { .ptr.native = __argv };
+       struct user_arg_ptr envp = { .ptr.native = __envp };
+       return do_execve_common(filename, argv, envp, regs);
+ }
+ #ifdef CONFIG_COMPAT
+ int compat_do_execve(char *filename,
+       compat_uptr_t __user *__argv,
+       compat_uptr_t __user *__envp,
+       struct pt_regs *regs)
+ {
+       struct user_arg_ptr argv = {
+               .is_compat = true,
+               .ptr.compat = __argv,
+       };
+       struct user_arg_ptr envp = {
+               .is_compat = true,
+               .ptr.compat = __envp,
+       };
+       return do_execve_common(filename, argv, envp, regs);
+ }
+ #endif
  void set_binfmt(struct linux_binfmt *new)
  {
        struct mm_struct *mm = current->mm;
@@@ -1659,7 -1734,6 +1734,7 @@@ static int zap_process(struct task_stru
  
        t = start;
        do {
 +              task_clear_group_stop_pending(t);
                if (t != current && t->mm) {
                        sigaddset(&t->pending.signal, SIGKILL);
                        signal_wake_up(t, 1);