I attached a patch proposal, it adds mlockall_pid() and munlockall_pid() syscalls (I've included only the mm/mlock.c file).
I generalized the present code to work on a pointer `p` that in case of mlockall() and munlockall() corresponds to `current`.
Instead, with mlockall_pid() and munlockall_pid(), after permission checks, it gets the task_struct from find_task_by_vpid.
I tested the syscalls and they seem ok, but I'm not sure how to test them thoroughly.
Cheers,
Federico
diff --git a/mm/mlock.c b/mm/mlock.c index cdbed8a..f1c4bdc 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -752,17 +752,17 @@ SYSCALL_DEFINE2(munlock, unsigned long, start, size_t, len) * is called once including the MCL_FUTURE flag and then a second time without * it, VM_LOCKED and VM_LOCKONFAULT will be cleared from mm->def_flags. */ -static int apply_mlockall_flags(int flags) +static int apply_mlockall_flags(struct task_struct *p, int flags) { struct vm_area_struct * vma, * prev = NULL; vm_flags_t to_add = 0; - current->mm->def_flags &= VM_LOCKED_CLEAR_MASK; + p->mm->def_flags &= VM_LOCKED_CLEAR_MASK; if (flags & MCL_FUTURE) { - current->mm->def_flags |= VM_LOCKED; + p->mm->def_flags |= VM_LOCKED; if (flags & MCL_ONFAULT) - current->mm->def_flags |= VM_LOCKONFAULT; + p->mm->def_flags |= VM_LOCKONFAULT; if (!(flags & MCL_CURRENT)) goto out; @@ -774,7 +774,7 @@ static int apply_mlockall_flags(int flags) to_add |= VM_LOCKONFAULT; } - for (vma = current->mm->mmap; vma ; vma = prev->vm_next) { + for (vma = p->mm->mmap; vma ; vma = prev->vm_next) { vm_flags_t newflags; newflags = vma->vm_flags & VM_LOCKED_CLEAR_MASK; @@ -788,7 +788,7 @@ static int apply_mlockall_flags(int flags) return 0; } -SYSCALL_DEFINE1(mlockall, int, flags) +static int _mlockall(struct task_struct *p, int flags) { unsigned long lock_limit; int ret; @@ -805,31 +805,132 @@ SYSCALL_DEFINE1(mlockall, int, flags) lock_limit = rlimit(RLIMIT_MEMLOCK); lock_limit >>= PAGE_SHIFT; - if (down_write_killable(¤t->mm->mmap_sem)) + if (down_write_killable(&p->mm->mmap_sem)) return -EINTR; ret = -ENOMEM; - if (!(flags & MCL_CURRENT) || (current->mm->total_vm <= lock_limit) || + if (!(flags & MCL_CURRENT) || (p->mm->total_vm <= lock_limit) || capable(CAP_IPC_LOCK)) - ret = apply_mlockall_flags(flags); - up_write(¤t->mm->mmap_sem); + ret = apply_mlockall_flags(p, flags); + up_write(&p->mm->mmap_sem); if (!ret && (flags & MCL_CURRENT)) mm_populate(0, TASK_SIZE); return ret; } -SYSCALL_DEFINE0(munlockall) +static int _munlockall(struct task_struct *p) { int ret; - if (down_write_killable(¤t->mm->mmap_sem)) + if (down_write_killable(&p->mm->mmap_sem)) return -EINTR; - ret = apply_mlockall_flags(0); - up_write(¤t->mm->mmap_sem); + ret = apply_mlockall_flags(p, 0); + up_write(&p->mm->mmap_sem); + + return ret; +} + +static bool check_same_owner(struct task_struct *p) +{ + const struct cred *cred = current_cred(), *pcred; + bool match; + + rcu_read_lock(); + pcred = __task_cred(p); + match = (uid_eq(cred->euid, pcred->euid) || + uid_eq(cred->euid, pcred->uid)); + rcu_read_unlock(); + return match; +} + +/* + * Check the permission to exec the mlockall_pid and munlockall_pid and write + * the struct corresponding to the pid provided. + */ +static int check_and_get_process(pid_t pid, struct task_struct **p) +{ + *p = NULL; + + if (pid < 0) + return -EINVAL; + + if (pid == 0) { + *p = current; + return 0; + } + + rcu_read_lock(); + *p = find_task_by_vpid(pid); + + if (*p == NULL) { + rcu_read_unlock(); + return -ESRCH; + } + + if ((*p)->flags & PF_KTHREAD) { + rcu_read_unlock(); + return -EINVAL; + } + + /* Prevent p going away */ + get_task_struct(*p); + rcu_read_unlock(); + + if (!check_same_owner(*p) && !capable(CAP_IPC_LOCK)) { + put_task_struct(*p); + return -EPERM; + } + + return 0; +} + +SYSCALL_DEFINE1(mlockall, int, flags) +{ + return _mlockall(current, flags); +} + +SYSCALL_DEFINE0(munlockall) +{ + return _munlockall(current); +} + +SYSCALL_DEFINE2(mlockall_pid, pid_t, pid, int, flags) +{ + int ret; + struct task_struct *p; + + ret = check_and_get_process(pid, &p); + + if (ret) + return ret; + + ret = _mlockall(p, flags); + + if (p != current) + put_task_struct(p); + return ret; } +SYSCALL_DEFINE1(munlockall_pid, pid_t, pid) +{ + int ret; + struct task_struct *p; + + ret = check_and_get_process(pid, &p); + if (ret) + return ret; + + ret = _munlockall(p); + + if (p != current) + put_task_struct(p); + + return ret; +} + + /* * Objects with different lifetime than processes (SHM_LOCK and SHM_HUGETLB * shm segments) get accounted against the user_struct instead.