|
@@ -1784,17 +1784,23 @@ SYSCALL_DEFINE1(umask, int, mask)
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_CHECKPOINT_RESTORE
|
|
#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,
|
|
static int prctl_set_mm(int opt, unsigned long addr,
|
|
unsigned long arg4, unsigned long arg5)
|
|
unsigned long arg4, unsigned long arg5)
|
|
{
|
|
{
|
|
unsigned long rlim = rlimit(RLIMIT_DATA);
|
|
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 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;
|
|
return -EINVAL;
|
|
|
|
|
|
if (!capable(CAP_SYS_RESOURCE))
|
|
if (!capable(CAP_SYS_RESOURCE))
|
|
@@ -1803,58 +1809,23 @@ static int prctl_set_mm(int opt, unsigned long addr,
|
|
if (addr >= TASK_SIZE)
|
|
if (addr >= TASK_SIZE)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
+ error = -EINVAL;
|
|
|
|
+
|
|
down_read(&mm->mmap_sem);
|
|
down_read(&mm->mmap_sem);
|
|
vma = find_vma(mm, addr);
|
|
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) {
|
|
switch (opt) {
|
|
case PR_SET_MM_START_CODE:
|
|
case PR_SET_MM_START_CODE:
|
|
|
|
+ mm->start_code = addr;
|
|
|
|
+ break;
|
|
case PR_SET_MM_END_CODE:
|
|
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;
|
|
break;
|
|
-
|
|
|
|
case PR_SET_MM_START_DATA:
|
|
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;
|
|
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;
|
|
break;
|
|
|
|
|
|
case PR_SET_MM_START_BRK:
|
|
case PR_SET_MM_START_BRK:
|
|
@@ -1881,16 +1852,77 @@ static int prctl_set_mm(int opt, unsigned long addr,
|
|
mm->brk = addr;
|
|
mm->brk = addr;
|
|
break;
|
|
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:
|
|
default:
|
|
- error = -EINVAL;
|
|
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
error = 0;
|
|
error = 0;
|
|
-
|
|
|
|
out:
|
|
out:
|
|
up_read(&mm->mmap_sem);
|
|
up_read(&mm->mmap_sem);
|
|
-
|
|
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
#else /* CONFIG_CHECKPOINT_RESTORE */
|
|
#else /* CONFIG_CHECKPOINT_RESTORE */
|