|
@@ -1692,6 +1692,124 @@ SYSCALL_DEFINE1(umask, int, mask)
|
|
|
return mask;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_CHECKPOINT_RESTORE
|
|
|
+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;
|
|
|
+
|
|
|
+ if (arg4 | arg5)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (!capable(CAP_SYS_ADMIN))
|
|
|
+ return -EPERM;
|
|
|
+
|
|
|
+ if (addr >= TASK_SIZE)
|
|
|
+ return -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:
|
|
|
+ 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;
|
|
|
+ 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;
|
|
|
+ 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;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case PR_SET_MM_START_BRK:
|
|
|
+ if (addr <= mm->end_data)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (rlim < RLIM_INFINITY &&
|
|
|
+ (mm->brk - addr) +
|
|
|
+ (mm->end_data - mm->start_data) > rlim)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ mm->start_brk = addr;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case PR_SET_MM_BRK:
|
|
|
+ if (addr <= mm->end_data)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (rlim < RLIM_INFINITY &&
|
|
|
+ (addr - mm->start_brk) +
|
|
|
+ (mm->end_data - mm->start_data) > rlim)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ mm->brk = addr;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ error = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ error = 0;
|
|
|
+
|
|
|
+out:
|
|
|
+ up_read(&mm->mmap_sem);
|
|
|
+
|
|
|
+ return error;
|
|
|
+}
|
|
|
+#else /* CONFIG_CHECKPOINT_RESTORE */
|
|
|
+static int prctl_set_mm(int opt, unsigned long addr,
|
|
|
+ unsigned long arg4, unsigned long arg5)
|
|
|
+{
|
|
|
+ return -EINVAL;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
|
|
unsigned long, arg4, unsigned long, arg5)
|
|
|
{
|
|
@@ -1841,6 +1959,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
|
|
else
|
|
|
error = PR_MCE_KILL_DEFAULT;
|
|
|
break;
|
|
|
+ case PR_SET_MM:
|
|
|
+ error = prctl_set_mm(arg2, arg3, arg4, arg5);
|
|
|
+ break;
|
|
|
default:
|
|
|
error = -EINVAL;
|
|
|
break;
|