ARM: tegra3: initialize pll_a during boot
[linux-2.6.git] / kernel / sys.c
index 26a6b73..1dbbe69 100644 (file)
@@ -8,7 +8,6 @@
 #include <linux/mm.h>
 #include <linux/utsname.h>
 #include <linux/mman.h>
-#include <linux/notifier.h>
 #include <linux/reboot.h>
 #include <linux/prctl.h>
 #include <linux/highuid.h>
 #include <linux/task_io_accounting_ops.h>
 #include <linux/seccomp.h>
 #include <linux/cpu.h>
+#include <linux/personality.h>
 #include <linux/ptrace.h>
 #include <linux/fs_struct.h>
+#include <linux/gfp.h>
+#include <linux/syscore_ops.h>
+#include <linux/version.h>
+#include <linux/ctype.h>
 
 #include <linux/compat.h>
 #include <linux/syscalls.h>
 #include <linux/kprobes.h>
 #include <linux/user_namespace.h>
 
+#include <linux/kmsg_dump.h>
+/* Move somewhere else to avoid recompiling? */
+#include <generated/utsrelease.h>
+
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/unistd.h>
@@ -115,16 +123,33 @@ EXPORT_SYMBOL(cad_pid);
 void (*pm_power_off_prepare)(void);
 
 /*
+ * Returns true if current's euid is same as p's uid or euid,
+ * or has CAP_SYS_NICE to p's user_ns.
+ *
+ * Called with rcu_read_lock, creds are safe
+ */
+static bool set_one_prio_perm(struct task_struct *p)
+{
+       const struct cred *cred = current_cred(), *pcred = __task_cred(p);
+
+       if (pcred->user->user_ns == cred->user->user_ns &&
+           (pcred->uid  == cred->euid ||
+            pcred->euid == cred->euid))
+               return true;
+       if (ns_capable(pcred->user->user_ns, CAP_SYS_NICE))
+               return true;
+       return false;
+}
+
+/*
  * set the priority of a task
  * - the caller must hold the RCU read lock
  */
 static int set_one_prio(struct task_struct *p, int niceval, int error)
 {
-       const struct cred *cred = current_cred(), *pcred = __task_cred(p);
        int no_nice;
 
-       if (pcred->uid  != cred->euid &&
-           pcred->euid != cred->euid && !capable(CAP_SYS_NICE)) {
+       if (!set_one_prio_perm(p)) {
                error = -EPERM;
                goto out;
        }
@@ -222,6 +247,7 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who)
        if (which > PRIO_USER || which < PRIO_PROCESS)
                return -EINVAL;
 
+       rcu_read_lock();
        read_lock(&tasklist_lock);
        switch (which) {
                case PRIO_PROCESS:
@@ -267,6 +293,7 @@ SYSCALL_DEFINE2(getpriority, int, which, int, who)
        }
 out_unlock:
        read_unlock(&tasklist_lock);
+       rcu_read_unlock();
 
        return retval;
 }
@@ -281,6 +308,7 @@ out_unlock:
  */
 void emergency_restart(void)
 {
+       kmsg_dump(KMSG_DUMP_EMERG);
        machine_emergency_restart();
 }
 EXPORT_SYMBOL_GPL(emergency_restart);
@@ -289,9 +317,41 @@ void kernel_restart_prepare(char *cmd)
 {
        blocking_notifier_call_chain(&reboot_notifier_list, SYS_RESTART, cmd);
        system_state = SYSTEM_RESTART;
+       usermodehelper_disable();
        device_shutdown();
-       sysdev_shutdown();
+       syscore_shutdown();
+}
+
+/**
+ *     register_reboot_notifier - Register function to be called at reboot time
+ *     @nb: Info about notifier function to be called
+ *
+ *     Registers a function with the list of functions
+ *     to be called at reboot time.
+ *
+ *     Currently always returns zero, as blocking_notifier_chain_register()
+ *     always returns zero.
+ */
+int register_reboot_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_register(&reboot_notifier_list, nb);
+}
+EXPORT_SYMBOL(register_reboot_notifier);
+
+/**
+ *     unregister_reboot_notifier - Unregister previously registered reboot notifier
+ *     @nb: Hook to be unregistered
+ *
+ *     Unregisters a previously registered reboot
+ *     notifier function.
+ *
+ *     Returns zero on success, or %-ENOENT on failure.
+ */
+int unregister_reboot_notifier(struct notifier_block *nb)
+{
+       return blocking_notifier_chain_unregister(&reboot_notifier_list, nb);
 }
+EXPORT_SYMBOL(unregister_reboot_notifier);
 
 /**
  *     kernel_restart - reboot the system
@@ -308,6 +368,7 @@ void kernel_restart(char *cmd)
                printk(KERN_EMERG "Restarting system.\n");
        else
                printk(KERN_EMERG "Restarting system with command '%s'.\n", cmd);
+       kmsg_dump(KMSG_DUMP_RESTART);
        machine_restart(cmd);
 }
 EXPORT_SYMBOL_GPL(kernel_restart);
@@ -317,6 +378,7 @@ static void kernel_shutdown_prepare(enum system_states state)
        blocking_notifier_call_chain(&reboot_notifier_list,
                (state == SYSTEM_HALT)?SYS_HALT:SYS_POWER_OFF, NULL);
        system_state = state;
+       usermodehelper_disable();
        device_shutdown();
 }
 /**
@@ -327,8 +389,9 @@ static void kernel_shutdown_prepare(enum system_states state)
 void kernel_halt(void)
 {
        kernel_shutdown_prepare(SYSTEM_HALT);
-       sysdev_shutdown();
+       syscore_shutdown();
        printk(KERN_EMERG "System halted.\n");
+       kmsg_dump(KMSG_DUMP_HALT);
        machine_halt();
 }
 
@@ -345,8 +408,9 @@ void kernel_power_off(void)
        if (pm_power_off_prepare)
                pm_power_off_prepare();
        disable_nonboot_cpus();
-       sysdev_shutdown();
+       syscore_shutdown();
        printk(KERN_EMERG "Power down.\n");
+       kmsg_dump(KMSG_DUMP_POWEROFF);
        machine_power_off();
 }
 EXPORT_SYMBOL_GPL(kernel_power_off);
@@ -488,15 +552,11 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)
                return -ENOMEM;
        old = current_cred();
 
-       retval = security_task_setgid(rgid, egid, (gid_t)-1, LSM_SETID_RE);
-       if (retval)
-               goto error;
-
        retval = -EPERM;
        if (rgid != (gid_t) -1) {
                if (old->gid == rgid ||
                    old->egid == rgid ||
-                   capable(CAP_SETGID))
+                   nsown_capable(CAP_SETGID))
                        new->gid = rgid;
                else
                        goto error;
@@ -505,7 +565,7 @@ SYSCALL_DEFINE2(setregid, gid_t, rgid, gid_t, egid)
                if (old->gid == egid ||
                    old->egid == egid ||
                    old->sgid == egid ||
-                   capable(CAP_SETGID))
+                   nsown_capable(CAP_SETGID))
                        new->egid = egid;
                else
                        goto error;
@@ -539,12 +599,8 @@ SYSCALL_DEFINE1(setgid, gid_t, gid)
                return -ENOMEM;
        old = current_cred();
 
-       retval = security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_ID);
-       if (retval)
-               goto error;
-
        retval = -EPERM;
-       if (capable(CAP_SETGID))
+       if (nsown_capable(CAP_SETGID))
                new->gid = new->egid = new->sgid = new->fsgid = gid;
        else if (gid == old->gid || gid == old->sgid)
                new->egid = new->fsgid = gid;
@@ -569,17 +625,18 @@ static int set_user(struct cred *new)
        if (!new_user)
                return -EAGAIN;
 
-       if (!task_can_switch_user(new_user, current)) {
-               free_uid(new_user);
-               return -EINVAL;
-       }
-
-       if (atomic_read(&new_user->processes) >=
-                               current->signal->rlim[RLIMIT_NPROC].rlim_cur &&
-                       new_user != INIT_USER) {
-               free_uid(new_user);
-               return -EAGAIN;
-       }
+       /*
+        * We don't fail in case of NPROC limit excess here because too many
+        * poorly written programs don't check set*uid() return code, assuming
+        * it never fails if called by root.  We may still enforce NPROC limit
+        * for programs doing set*uid()+execve() by harmlessly deferring the
+        * failure to the execve() stage.
+        */
+       if (atomic_read(&new_user->processes) >= rlimit(RLIMIT_NPROC) &&
+                       new_user != INIT_USER)
+               current->flags |= PF_NPROC_EXCEEDED;
+       else
+               current->flags &= ~PF_NPROC_EXCEEDED;
 
        free_uid(new->user);
        new->user = new_user;
@@ -612,16 +669,12 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)
                return -ENOMEM;
        old = current_cred();
 
-       retval = security_task_setuid(ruid, euid, (uid_t)-1, LSM_SETID_RE);
-       if (retval)
-               goto error;
-
        retval = -EPERM;
        if (ruid != (uid_t) -1) {
                new->uid = ruid;
                if (old->uid != ruid &&
                    old->euid != ruid &&
-                   !capable(CAP_SETUID))
+                   !nsown_capable(CAP_SETUID))
                        goto error;
        }
 
@@ -630,7 +683,7 @@ SYSCALL_DEFINE2(setreuid, uid_t, ruid, uid_t, euid)
                if (old->uid != euid &&
                    old->euid != euid &&
                    old->suid != euid &&
-                   !capable(CAP_SETUID))
+                   !nsown_capable(CAP_SETUID))
                        goto error;
        }
 
@@ -677,12 +730,8 @@ SYSCALL_DEFINE1(setuid, uid_t, uid)
                return -ENOMEM;
        old = current_cred();
 
-       retval = security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_ID);
-       if (retval)
-               goto error;
-
        retval = -EPERM;
-       if (capable(CAP_SETUID)) {
+       if (nsown_capable(CAP_SETUID)) {
                new->suid = new->uid = uid;
                if (uid != old->uid) {
                        retval = set_user(new);
@@ -721,13 +770,10 @@ SYSCALL_DEFINE3(setresuid, uid_t, ruid, uid_t, euid, uid_t, suid)
        if (!new)
                return -ENOMEM;
 
-       retval = security_task_setuid(ruid, euid, suid, LSM_SETID_RES);
-       if (retval)
-               goto error;
        old = current_cred();
 
        retval = -EPERM;
-       if (!capable(CAP_SETUID)) {
+       if (!nsown_capable(CAP_SETUID)) {
                if (ruid != (uid_t) -1 && ruid != old->uid &&
                    ruid != old->euid  && ruid != old->suid)
                        goto error;
@@ -790,12 +836,8 @@ SYSCALL_DEFINE3(setresgid, gid_t, rgid, gid_t, egid, gid_t, sgid)
                return -ENOMEM;
        old = current_cred();
 
-       retval = security_task_setgid(rgid, egid, sgid, LSM_SETID_RES);
-       if (retval)
-               goto error;
-
        retval = -EPERM;
-       if (!capable(CAP_SETGID)) {
+       if (!nsown_capable(CAP_SETGID)) {
                if (rgid != (gid_t) -1 && rgid != old->gid &&
                    rgid != old->egid  && rgid != old->sgid)
                        goto error;
@@ -853,12 +895,9 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)
        old = current_cred();
        old_fsuid = old->fsuid;
 
-       if (security_task_setuid(uid, (uid_t)-1, (uid_t)-1, LSM_SETID_FS) < 0)
-               goto error;
-
        if (uid == old->uid  || uid == old->euid  ||
            uid == old->suid || uid == old->fsuid ||
-           capable(CAP_SETUID)) {
+           nsown_capable(CAP_SETUID)) {
                if (uid != old_fsuid) {
                        new->fsuid = uid;
                        if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0)
@@ -866,7 +905,6 @@ SYSCALL_DEFINE1(setfsuid, uid_t, uid)
                }
        }
 
-error:
        abort_creds(new);
        return old_fsuid;
 
@@ -890,19 +928,15 @@ SYSCALL_DEFINE1(setfsgid, gid_t, gid)
        old = current_cred();
        old_fsgid = old->fsgid;
 
-       if (security_task_setgid(gid, (gid_t)-1, (gid_t)-1, LSM_SETID_FS))
-               goto error;
-
        if (gid == old->gid  || gid == old->egid  ||
            gid == old->sgid || gid == old->fsgid ||
-           capable(CAP_SETGID)) {
+           nsown_capable(CAP_SETGID)) {
                if (gid != old_fsgid) {
                        new->fsgid = gid;
                        goto change_okay;
                }
        }
 
-error:
        abort_creds(new);
        return old_fsgid;
 
@@ -964,6 +998,7 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
                pgid = pid;
        if (pgid < 0)
                return -EINVAL;
+       rcu_read_lock();
 
        /* From this point forward we keep holding onto the tasklist lock
         * so that our parent does not change from under us. -DaveM
@@ -1017,6 +1052,7 @@ SYSCALL_DEFINE2(setpgid, pid_t, pid, pid_t, pgid)
 out:
        /* All paths lead to here, thus we are safe. -DaveM */
        write_unlock_irq(&tasklist_lock);
+       rcu_read_unlock();
        return err;
 }
 
@@ -1111,13 +1147,52 @@ SYSCALL_DEFINE0(setsid)
        err = session;
 out:
        write_unlock_irq(&tasklist_lock);
-       if (err > 0)
+       if (err > 0) {
                proc_sid_connector(group_leader);
+               sched_autogroup_create_attach(group_leader);
+       }
        return err;
 }
 
 DECLARE_RWSEM(uts_sem);
 
+#ifdef COMPAT_UTS_MACHINE
+#define override_architecture(name) \
+       (personality(current->personality) == PER_LINUX32 && \
+        copy_to_user(name->machine, COMPAT_UTS_MACHINE, \
+                     sizeof(COMPAT_UTS_MACHINE)))
+#else
+#define override_architecture(name)    0
+#endif
+
+/*
+ * Work around broken programs that cannot handle "Linux 3.0".
+ * Instead we map 3.x to 2.6.40+x, so e.g. 3.0 would be 2.6.40
+ */
+static int override_release(char __user *release, int len)
+{
+       int ret = 0;
+       char buf[65];
+
+       if (current->personality & UNAME26) {
+               char *rest = UTS_RELEASE;
+               int ndots = 0;
+               unsigned v;
+
+               while (*rest) {
+                       if (*rest == '.' && ++ndots >= 3)
+                               break;
+                       if (!isdigit(*rest) && *rest != '.')
+                               break;
+                       rest++;
+               }
+               v = ((LINUX_VERSION_CODE >> 8) & 0xff) + 40;
+               snprintf(buf, len, "2.6.%u%s", v, rest);
+               ret = copy_to_user(release, buf, len);
+       }
+       return ret;
+}
+
 SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
 {
        int errno = 0;
@@ -1126,16 +1201,80 @@ SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
        if (copy_to_user(name, utsname(), sizeof *name))
                errno = -EFAULT;
        up_read(&uts_sem);
+
+       if (!errno && override_release(name->release, sizeof(name->release)))
+               errno = -EFAULT;
+       if (!errno && override_architecture(name))
+               errno = -EFAULT;
        return errno;
 }
 
+#ifdef __ARCH_WANT_SYS_OLD_UNAME
+/*
+ * Old cruft
+ */
+SYSCALL_DEFINE1(uname, struct old_utsname __user *, name)
+{
+       int error = 0;
+
+       if (!name)
+               return -EFAULT;
+
+       down_read(&uts_sem);
+       if (copy_to_user(name, utsname(), sizeof(*name)))
+               error = -EFAULT;
+       up_read(&uts_sem);
+
+       if (!error && override_release(name->release, sizeof(name->release)))
+               error = -EFAULT;
+       if (!error && override_architecture(name))
+               error = -EFAULT;
+       return error;
+}
+
+SYSCALL_DEFINE1(olduname, struct oldold_utsname __user *, name)
+{
+       int error;
+
+       if (!name)
+               return -EFAULT;
+       if (!access_ok(VERIFY_WRITE, name, sizeof(struct oldold_utsname)))
+               return -EFAULT;
+
+       down_read(&uts_sem);
+       error = __copy_to_user(&name->sysname, &utsname()->sysname,
+                              __OLD_UTS_LEN);
+       error |= __put_user(0, name->sysname + __OLD_UTS_LEN);
+       error |= __copy_to_user(&name->nodename, &utsname()->nodename,
+                               __OLD_UTS_LEN);
+       error |= __put_user(0, name->nodename + __OLD_UTS_LEN);
+       error |= __copy_to_user(&name->release, &utsname()->release,
+                               __OLD_UTS_LEN);
+       error |= __put_user(0, name->release + __OLD_UTS_LEN);
+       error |= __copy_to_user(&name->version, &utsname()->version,
+                               __OLD_UTS_LEN);
+       error |= __put_user(0, name->version + __OLD_UTS_LEN);
+       error |= __copy_to_user(&name->machine, &utsname()->machine,
+                               __OLD_UTS_LEN);
+       error |= __put_user(0, name->machine + __OLD_UTS_LEN);
+       up_read(&uts_sem);
+
+       if (!error && override_architecture(name))
+               error = -EFAULT;
+       if (!error && override_release(name->release, sizeof(name->release)))
+               error = -EFAULT;
+       return error ? -EFAULT : 0;
+}
+#endif
+
 SYSCALL_DEFINE2(sethostname, char __user *, name, int, len)
 {
        int errno;
        char tmp[__NEW_UTS_LEN];
 
-       if (!capable(CAP_SYS_ADMIN))
+       if (!ns_capable(current->nsproxy->uts_ns->user_ns, CAP_SYS_ADMIN))
                return -EPERM;
+
        if (len < 0 || len > __NEW_UTS_LEN)
                return -EINVAL;
        down_write(&uts_sem);
@@ -1183,7 +1322,7 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len)
        int errno;
        char tmp[__NEW_UTS_LEN];
 
-       if (!capable(CAP_SYS_ADMIN))
+       if (!ns_capable(current->nsproxy->uts_ns->user_ns, CAP_SYS_ADMIN))
                return -EPERM;
        if (len < 0 || len > __NEW_UTS_LEN)
                return -EINVAL;
@@ -1203,15 +1342,14 @@ SYSCALL_DEFINE2(setdomainname, char __user *, name, int, len)
 
 SYSCALL_DEFINE2(getrlimit, unsigned int, resource, struct rlimit __user *, rlim)
 {
-       if (resource >= RLIM_NLIMITS)
-               return -EINVAL;
-       else {
-               struct rlimit value;
-               task_lock(current->group_leader);
-               value = current->signal->rlim[resource];
-               task_unlock(current->group_leader);
-               return copy_to_user(rlim, &value, sizeof(*rlim)) ? -EFAULT : 0;
-       }
+       struct rlimit value;
+       int ret;
+
+       ret = do_prlimit(current, resource, NULL, &value);
+       if (!ret)
+               ret = copy_to_user(rlim, &value, sizeof(*rlim)) ? -EFAULT : 0;
+
+       return ret;
 }
 
 #ifdef __ARCH_WANT_SYS_OLD_GETRLIMIT
@@ -1239,44 +1377,91 @@ SYSCALL_DEFINE2(old_getrlimit, unsigned int, resource,
 
 #endif
 
-SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim)
+static inline bool rlim64_is_infinity(__u64 rlim64)
 {
-       struct rlimit new_rlim, *old_rlim;
-       int retval;
+#if BITS_PER_LONG < 64
+       return rlim64 >= ULONG_MAX;
+#else
+       return rlim64 == RLIM64_INFINITY;
+#endif
+}
+
+static void rlim_to_rlim64(const struct rlimit *rlim, struct rlimit64 *rlim64)
+{
+       if (rlim->rlim_cur == RLIM_INFINITY)
+               rlim64->rlim_cur = RLIM64_INFINITY;
+       else
+               rlim64->rlim_cur = rlim->rlim_cur;
+       if (rlim->rlim_max == RLIM_INFINITY)
+               rlim64->rlim_max = RLIM64_INFINITY;
+       else
+               rlim64->rlim_max = rlim->rlim_max;
+}
+
+static void rlim64_to_rlim(const struct rlimit64 *rlim64, struct rlimit *rlim)
+{
+       if (rlim64_is_infinity(rlim64->rlim_cur))
+               rlim->rlim_cur = RLIM_INFINITY;
+       else
+               rlim->rlim_cur = (unsigned long)rlim64->rlim_cur;
+       if (rlim64_is_infinity(rlim64->rlim_max))
+               rlim->rlim_max = RLIM_INFINITY;
+       else
+               rlim->rlim_max = (unsigned long)rlim64->rlim_max;
+}
+
+/* make sure you are allowed to change @tsk limits before calling this */
+int do_prlimit(struct task_struct *tsk, unsigned int resource,
+               struct rlimit *new_rlim, struct rlimit *old_rlim)
+{
+       struct rlimit *rlim;
+       int retval = 0;
 
        if (resource >= RLIM_NLIMITS)
                return -EINVAL;
-       if (copy_from_user(&new_rlim, rlim, sizeof(*rlim)))
-               return -EFAULT;
-       if (new_rlim.rlim_cur > new_rlim.rlim_max)
-               return -EINVAL;
-       old_rlim = current->signal->rlim + resource;
-       if ((new_rlim.rlim_max > old_rlim->rlim_max) &&
-           !capable(CAP_SYS_RESOURCE))
-               return -EPERM;
-       if (resource == RLIMIT_NOFILE && new_rlim.rlim_max > sysctl_nr_open)
-               return -EPERM;
-
-       retval = security_task_setrlimit(resource, &new_rlim);
-       if (retval)
-               return retval;
-
-       if (resource == RLIMIT_CPU && new_rlim.rlim_cur == 0) {
-               /*
-                * The caller is asking for an immediate RLIMIT_CPU
-                * expiry.  But we use the zero value to mean "it was
-                * never set".  So let's cheat and make it one second
-                * instead
-                */
-               new_rlim.rlim_cur = 1;
+       if (new_rlim) {
+               if (new_rlim->rlim_cur > new_rlim->rlim_max)
+                       return -EINVAL;
+               if (resource == RLIMIT_NOFILE &&
+                               new_rlim->rlim_max > sysctl_nr_open)
+                       return -EPERM;
        }
 
-       task_lock(current->group_leader);
-       *old_rlim = new_rlim;
-       task_unlock(current->group_leader);
-
-       if (resource != RLIMIT_CPU)
+       /* protect tsk->signal and tsk->sighand from disappearing */
+       read_lock(&tasklist_lock);
+       if (!tsk->sighand) {
+               retval = -ESRCH;
                goto out;
+       }
+
+       rlim = tsk->signal->rlim + resource;
+       task_lock(tsk->group_leader);
+       if (new_rlim) {
+               /* Keep the capable check against init_user_ns until
+                  cgroups can contain all limits */
+               if (new_rlim->rlim_max > rlim->rlim_max &&
+                               !capable(CAP_SYS_RESOURCE))
+                       retval = -EPERM;
+               if (!retval)
+                       retval = security_task_setrlimit(tsk->group_leader,
+                                       resource, new_rlim);
+               if (resource == RLIMIT_CPU && new_rlim->rlim_cur == 0) {
+                       /*
+                        * The caller is asking for an immediate RLIMIT_CPU
+                        * expiry.  But we use the zero value to mean "it was
+                        * never set".  So let's cheat and make it one second
+                        * instead
+                        */
+                       new_rlim->rlim_cur = 1;
+               }
+       }
+       if (!retval) {
+               if (old_rlim)
+                       *old_rlim = *rlim;
+               if (new_rlim)
+                       *rlim = *new_rlim;
+       }
+       task_unlock(tsk->group_leader);
 
        /*
         * RLIMIT_CPU handling.   Note that the kernel fails to return an error
@@ -1284,12 +1469,86 @@ SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim)
         * very long-standing error, and fixing it now risks breakage of
         * applications, so we live with it
         */
-       if (new_rlim.rlim_cur == RLIM_INFINITY)
-               goto out;
-
-       update_rlimit_cpu(new_rlim.rlim_cur);
+        if (!retval && new_rlim && resource == RLIMIT_CPU &&
+                        new_rlim->rlim_cur != RLIM_INFINITY)
+               update_rlimit_cpu(tsk, new_rlim->rlim_cur);
 out:
-       return 0;
+       read_unlock(&tasklist_lock);
+       return retval;
+}
+
+/* rcu lock must be held */
+static int check_prlimit_permission(struct task_struct *task)
+{
+       const struct cred *cred = current_cred(), *tcred;
+
+       if (current == task)
+               return 0;
+
+       tcred = __task_cred(task);
+       if (cred->user->user_ns == tcred->user->user_ns &&
+           (cred->uid == tcred->euid &&
+            cred->uid == tcred->suid &&
+            cred->uid == tcred->uid  &&
+            cred->gid == tcred->egid &&
+            cred->gid == tcred->sgid &&
+            cred->gid == tcred->gid))
+               return 0;
+       if (ns_capable(tcred->user->user_ns, CAP_SYS_RESOURCE))
+               return 0;
+
+       return -EPERM;
+}
+
+SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
+               const struct rlimit64 __user *, new_rlim,
+               struct rlimit64 __user *, old_rlim)
+{
+       struct rlimit64 old64, new64;
+       struct rlimit old, new;
+       struct task_struct *tsk;
+       int ret;
+
+       if (new_rlim) {
+               if (copy_from_user(&new64, new_rlim, sizeof(new64)))
+                       return -EFAULT;
+               rlim64_to_rlim(&new64, &new);
+       }
+
+       rcu_read_lock();
+       tsk = pid ? find_task_by_vpid(pid) : current;
+       if (!tsk) {
+               rcu_read_unlock();
+               return -ESRCH;
+       }
+       ret = check_prlimit_permission(tsk);
+       if (ret) {
+               rcu_read_unlock();
+               return ret;
+       }
+       get_task_struct(tsk);
+       rcu_read_unlock();
+
+       ret = do_prlimit(tsk, resource, new_rlim ? &new : NULL,
+                       old_rlim ? &old : NULL);
+
+       if (!ret && old_rlim) {
+               rlim_to_rlim64(&old, &old64);
+               if (copy_to_user(old_rlim, &old64, sizeof(old64)))
+                       ret = -EFAULT;
+       }
+
+       put_task_struct(tsk);
+       return ret;
+}
+
+SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim)
+{
+       struct rlimit new_rlim;
+
+       if (copy_from_user(&new_rlim, rlim, sizeof(*rlim)))
+               return -EFAULT;
+       return do_prlimit(current, resource, &new_rlim, NULL);
 }
 
 /*
@@ -1599,9 +1858,9 @@ SYSCALL_DEFINE3(getcpu, unsigned __user *, cpup, unsigned __user *, nodep,
 
 char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";
 
-static void argv_cleanup(char **argv, char **envp)
+static void argv_cleanup(struct subprocess_info *info)
 {
-       argv_free(argv);
+       argv_free(info->argv);
 }
 
 /**
@@ -1635,7 +1894,7 @@ int orderly_poweroff(bool force)
                goto out;
        }
 
-       call_usermodehelper_setcleanup(info, argv_cleanup);
+       call_usermodehelper_setfns(info, NULL, argv_cleanup, NULL);
 
        ret = call_usermodehelper_exec(info, UMH_NO_WAIT);