]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - kernel/sys.c
prlimit,security,selinux: add a security hook for prlimit
[linux.git] / kernel / sys.c
index 7d4a9a6df95688027da8236897225f95c7894889..196c7134bee6bda9ef444272beba8940ee3699d7 100644 (file)
 #include <linux/binfmts.h>
 
 #include <linux/sched.h>
+#include <linux/sched/autogroup.h>
+#include <linux/sched/loadavg.h>
+#include <linux/sched/stat.h>
+#include <linux/sched/mm.h>
+#include <linux/sched/coredump.h>
+#include <linux/sched/task.h>
+#include <linux/sched/cputime.h>
 #include <linux/rcupdate.h>
 #include <linux/uidgid.h>
 #include <linux/cred.h>
@@ -1425,25 +1432,26 @@ int do_prlimit(struct task_struct *tsk, unsigned int resource,
 }
 
 /* rcu lock must be held */
-static int check_prlimit_permission(struct task_struct *task)
+static int check_prlimit_permission(struct task_struct *task,
+                                   unsigned int flags)
 {
        const struct cred *cred = current_cred(), *tcred;
+       bool id_match;
 
        if (current == task)
                return 0;
 
        tcred = __task_cred(task);
-       if (uid_eq(cred->uid, tcred->euid) &&
-           uid_eq(cred->uid, tcred->suid) &&
-           uid_eq(cred->uid, tcred->uid)  &&
-           gid_eq(cred->gid, tcred->egid) &&
-           gid_eq(cred->gid, tcred->sgid) &&
-           gid_eq(cred->gid, tcred->gid))
-               return 0;
-       if (ns_capable(tcred->user_ns, CAP_SYS_RESOURCE))
-               return 0;
+       id_match = (uid_eq(cred->uid, tcred->euid) &&
+                   uid_eq(cred->uid, tcred->suid) &&
+                   uid_eq(cred->uid, tcred->uid)  &&
+                   gid_eq(cred->gid, tcred->egid) &&
+                   gid_eq(cred->gid, tcred->sgid) &&
+                   gid_eq(cred->gid, tcred->gid));
+       if (!id_match && !ns_capable(tcred->user_ns, CAP_SYS_RESOURCE))
+               return -EPERM;
 
-       return -EPERM;
+       return security_task_prlimit(cred, tcred, flags);
 }
 
 SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
@@ -1453,12 +1461,17 @@ SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
        struct rlimit64 old64, new64;
        struct rlimit old, new;
        struct task_struct *tsk;
+       unsigned int checkflags = 0;
        int ret;
 
+       if (old_rlim)
+               checkflags |= LSM_PRLIMIT_READ;
+
        if (new_rlim) {
                if (copy_from_user(&new64, new_rlim, sizeof(new64)))
                        return -EFAULT;
                rlim64_to_rlim(&new64, &new);
+               checkflags |= LSM_PRLIMIT_WRITE;
        }
 
        rcu_read_lock();
@@ -1467,7 +1480,7 @@ SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
                rcu_read_unlock();
                return -ESRCH;
        }
-       ret = check_prlimit_permission(tsk);
+       ret = check_prlimit_permission(tsk, checkflags);
        if (ret) {
                rcu_read_unlock();
                return ret;
@@ -2063,6 +2076,24 @@ static int prctl_get_tid_address(struct task_struct *me, int __user **tid_addr)
 }
 #endif
 
+static int propagate_has_child_subreaper(struct task_struct *p, void *data)
+{
+       /*
+        * If task has has_child_subreaper - all its decendants
+        * already have these flag too and new decendants will
+        * inherit it on fork, skip them.
+        *
+        * If we've found child_reaper - skip descendants in
+        * it's subtree as they will never get out pidns.
+        */
+       if (p->signal->has_child_subreaper ||
+           is_child_reaper(task_pid(p)))
+               return 0;
+
+       p->signal->has_child_subreaper = 1;
+       return 1;
+}
+
 SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
                unsigned long, arg4, unsigned long, arg5)
 {
@@ -2214,6 +2245,10 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
                break;
        case PR_SET_CHILD_SUBREAPER:
                me->signal->is_child_subreaper = !!arg2;
+               if (!arg2)
+                       break;
+
+               walk_process_tree(me, propagate_has_child_subreaper, NULL);
                break;
        case PR_GET_CHILD_SUBREAPER:
                error = put_user(me->signal->is_child_subreaper,