}
 
 #ifdef CONFIG_CHECKPOINT_RESTORE
+static bool vma_flags_mismatch(struct vm_area_struct *vma,
+                              unsigned long required,
+                              unsigned long banned)
+{
+       return (vma->vm_flags & required) != required ||
+               (vma->vm_flags & banned);
+}
+
 static int prctl_set_mm(int opt, unsigned long addr,
                        unsigned long arg4, unsigned long arg5)
 {
        unsigned long rlim = rlimit(RLIMIT_DATA);
-       unsigned long vm_req_flags;
-       unsigned long vm_bad_flags;
-       struct vm_area_struct *vma;
-       int error = 0;
        struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma;
+       int error;
 
-       if (arg4 | arg5)
+       if (arg5 || (arg4 && opt != PR_SET_MM_AUXV))
                return -EINVAL;
 
        if (!capable(CAP_SYS_RESOURCE))
        if (addr >= TASK_SIZE)
                return -EINVAL;
 
+       error = -EINVAL;
+
        down_read(&mm->mmap_sem);
        vma = find_vma(mm, addr);
 
-       if (opt != PR_SET_MM_START_BRK && opt != PR_SET_MM_BRK) {
-               /* It must be existing VMA */
-               if (!vma || vma->vm_start > addr)
-                       goto out;
-       }
-
-       error = -EINVAL;
        switch (opt) {
        case PR_SET_MM_START_CODE:
+               mm->start_code = addr;
+               break;
        case PR_SET_MM_END_CODE:
-               vm_req_flags = VM_READ | VM_EXEC;
-               vm_bad_flags = VM_WRITE | VM_MAYSHARE;
-
-               if ((vma->vm_flags & vm_req_flags) != vm_req_flags ||
-                   (vma->vm_flags & vm_bad_flags))
-                       goto out;
-
-               if (opt == PR_SET_MM_START_CODE)
-                       mm->start_code = addr;
-               else
-                       mm->end_code = addr;
+               mm->end_code = addr;
                break;
-
        case PR_SET_MM_START_DATA:
-       case PR_SET_MM_END_DATA:
-               vm_req_flags = VM_READ | VM_WRITE;
-               vm_bad_flags = VM_EXEC | VM_MAYSHARE;
-
-               if ((vma->vm_flags & vm_req_flags) != vm_req_flags ||
-                   (vma->vm_flags & vm_bad_flags))
-                       goto out;
-
-               if (opt == PR_SET_MM_START_DATA)
-                       mm->start_data = addr;
-               else
-                       mm->end_data = addr;
+               mm->start_data = addr;
                break;
-
-       case PR_SET_MM_START_STACK:
-
-#ifdef CONFIG_STACK_GROWSUP
-               vm_req_flags = VM_READ | VM_WRITE | VM_GROWSUP;
-#else
-               vm_req_flags = VM_READ | VM_WRITE | VM_GROWSDOWN;
-#endif
-               if ((vma->vm_flags & vm_req_flags) != vm_req_flags)
-                       goto out;
-
-               mm->start_stack = addr;
+       case PR_SET_MM_END_DATA:
+               mm->end_data = addr;
                break;
 
        case PR_SET_MM_START_BRK:
                mm->brk = addr;
                break;
 
+       /*
+        * If command line arguments and environment
+        * are placed somewhere else on stack, we can
+        * set them up here, ARG_START/END to setup
+        * command line argumets and ENV_START/END
+        * for environment.
+        */
+       case PR_SET_MM_START_STACK:
+       case PR_SET_MM_ARG_START:
+       case PR_SET_MM_ARG_END:
+       case PR_SET_MM_ENV_START:
+       case PR_SET_MM_ENV_END:
+               if (!vma) {
+                       error = -EFAULT;
+                       goto out;
+               }
+#ifdef CONFIG_STACK_GROWSUP
+               if (vma_flags_mismatch(vma, VM_READ | VM_WRITE | VM_GROWSUP, 0))
+#else
+               if (vma_flags_mismatch(vma, VM_READ | VM_WRITE | VM_GROWSDOWN, 0))
+#endif
+                       goto out;
+               if (opt == PR_SET_MM_START_STACK)
+                       mm->start_stack = addr;
+               else if (opt == PR_SET_MM_ARG_START)
+                       mm->arg_start = addr;
+               else if (opt == PR_SET_MM_ARG_END)
+                       mm->arg_end = addr;
+               else if (opt == PR_SET_MM_ENV_START)
+                       mm->env_start = addr;
+               else if (opt == PR_SET_MM_ENV_END)
+                       mm->env_end = addr;
+               break;
+
+       /*
+        * This doesn't move auxiliary vector itself
+        * since it's pinned to mm_struct, but allow
+        * to fill vector with new values. It's up
+        * to a caller to provide sane values here
+        * otherwise user space tools which use this
+        * vector might be unhappy.
+        */
+       case PR_SET_MM_AUXV: {
+               unsigned long user_auxv[AT_VECTOR_SIZE];
+
+               if (arg4 > sizeof(user_auxv))
+                       goto out;
+               up_read(&mm->mmap_sem);
+
+               if (copy_from_user(user_auxv, (const void __user *)addr, arg4))
+                       return -EFAULT;
+
+               /* Make sure the last entry is always AT_NULL */
+               user_auxv[AT_VECTOR_SIZE - 2] = 0;
+               user_auxv[AT_VECTOR_SIZE - 1] = 0;
+
+               BUILD_BUG_ON(sizeof(user_auxv) != sizeof(mm->saved_auxv));
+
+               task_lock(current);
+               memcpy(mm->saved_auxv, user_auxv, arg4);
+               task_unlock(current);
+
+               return 0;
+       }
        default:
-               error = -EINVAL;
                goto out;
        }
 
        error = 0;
-
 out:
        up_read(&mm->mmap_sem);
-
        return error;
 }
 #else /* CONFIG_CHECKPOINT_RESTORE */