dm-crypt: use __bio_add_page to add single page to clone bio
[linux-block.git] / kernel / sys.c
index 495cd87d9bf41b718e4b9acdf7b549a971c8d032..339fee3eff6a2bb2f25d1663d91e17178302c03f 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/highuid.h>
 #include <linux/fs.h>
 #include <linux/kmod.h>
+#include <linux/ksm.h>
 #include <linux/perf_event.h>
 #include <linux/resource.h>
 #include <linux/kernel.h>
@@ -664,6 +665,7 @@ long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
        struct cred *new;
        int retval;
        kuid_t kruid, keuid, ksuid;
+       bool ruid_new, euid_new, suid_new;
 
        kruid = make_kuid(ns, ruid);
        keuid = make_kuid(ns, euid);
@@ -678,25 +680,29 @@ long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
        if ((suid != (uid_t) -1) && !uid_valid(ksuid))
                return -EINVAL;
 
+       old = current_cred();
+
+       /* check for no-op */
+       if ((ruid == (uid_t) -1 || uid_eq(kruid, old->uid)) &&
+           (euid == (uid_t) -1 || (uid_eq(keuid, old->euid) &&
+                                   uid_eq(keuid, old->fsuid))) &&
+           (suid == (uid_t) -1 || uid_eq(ksuid, old->suid)))
+               return 0;
+
+       ruid_new = ruid != (uid_t) -1        && !uid_eq(kruid, old->uid) &&
+                  !uid_eq(kruid, old->euid) && !uid_eq(kruid, old->suid);
+       euid_new = euid != (uid_t) -1        && !uid_eq(keuid, old->uid) &&
+                  !uid_eq(keuid, old->euid) && !uid_eq(keuid, old->suid);
+       suid_new = suid != (uid_t) -1        && !uid_eq(ksuid, old->uid) &&
+                  !uid_eq(ksuid, old->euid) && !uid_eq(ksuid, old->suid);
+       if ((ruid_new || euid_new || suid_new) &&
+           !ns_capable_setid(old->user_ns, CAP_SETUID))
+               return -EPERM;
+
        new = prepare_creds();
        if (!new)
                return -ENOMEM;
 
-       old = current_cred();
-
-       retval = -EPERM;
-       if (!ns_capable_setid(old->user_ns, CAP_SETUID)) {
-               if (ruid != (uid_t) -1        && !uid_eq(kruid, old->uid) &&
-                   !uid_eq(kruid, old->euid) && !uid_eq(kruid, old->suid))
-                       goto error;
-               if (euid != (uid_t) -1        && !uid_eq(keuid, old->uid) &&
-                   !uid_eq(keuid, old->euid) && !uid_eq(keuid, old->suid))
-                       goto error;
-               if (suid != (uid_t) -1        && !uid_eq(ksuid, old->uid) &&
-                   !uid_eq(ksuid, old->euid) && !uid_eq(ksuid, old->suid))
-                       goto error;
-       }
-
        if (ruid != (uid_t) -1) {
                new->uid = kruid;
                if (!uid_eq(kruid, old->uid)) {
@@ -761,6 +767,7 @@ long __sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
        struct cred *new;
        int retval;
        kgid_t krgid, kegid, ksgid;
+       bool rgid_new, egid_new, sgid_new;
 
        krgid = make_kgid(ns, rgid);
        kegid = make_kgid(ns, egid);
@@ -773,23 +780,28 @@ long __sys_setresgid(gid_t rgid, gid_t egid, gid_t sgid)
        if ((sgid != (gid_t) -1) && !gid_valid(ksgid))
                return -EINVAL;
 
+       old = current_cred();
+
+       /* check for no-op */
+       if ((rgid == (gid_t) -1 || gid_eq(krgid, old->gid)) &&
+           (egid == (gid_t) -1 || (gid_eq(kegid, old->egid) &&
+                                   gid_eq(kegid, old->fsgid))) &&
+           (sgid == (gid_t) -1 || gid_eq(ksgid, old->sgid)))
+               return 0;
+
+       rgid_new = rgid != (gid_t) -1        && !gid_eq(krgid, old->gid) &&
+                  !gid_eq(krgid, old->egid) && !gid_eq(krgid, old->sgid);
+       egid_new = egid != (gid_t) -1        && !gid_eq(kegid, old->gid) &&
+                  !gid_eq(kegid, old->egid) && !gid_eq(kegid, old->sgid);
+       sgid_new = sgid != (gid_t) -1        && !gid_eq(ksgid, old->gid) &&
+                  !gid_eq(ksgid, old->egid) && !gid_eq(ksgid, old->sgid);
+       if ((rgid_new || egid_new || sgid_new) &&
+           !ns_capable_setid(old->user_ns, CAP_SETGID))
+               return -EPERM;
+
        new = prepare_creds();
        if (!new)
                return -ENOMEM;
-       old = current_cred();
-
-       retval = -EPERM;
-       if (!ns_capable_setid(old->user_ns, CAP_SETGID)) {
-               if (rgid != (gid_t) -1        && !gid_eq(krgid, old->gid) &&
-                   !gid_eq(krgid, old->egid) && !gid_eq(krgid, old->sgid))
-                       goto error;
-               if (egid != (gid_t) -1        && !gid_eq(kegid, old->gid) &&
-                   !gid_eq(kegid, old->egid) && !gid_eq(kegid, old->sgid))
-                       goto error;
-               if (sgid != (gid_t) -1        && !gid_eq(ksgid, old->gid) &&
-                   !gid_eq(ksgid, old->egid) && !gid_eq(ksgid, old->sgid))
-                       goto error;
-       }
 
        if (rgid != (gid_t) -1)
                new->gid = krgid;
@@ -2377,6 +2389,16 @@ static inline int prctl_get_mdwe(unsigned long arg2, unsigned long arg3,
                PR_MDWE_REFUSE_EXEC_GAIN : 0;
 }
 
+static int prctl_get_auxv(void __user *addr, unsigned long len)
+{
+       struct mm_struct *mm = current->mm;
+       unsigned long size = min_t(unsigned long, sizeof(mm->saved_auxv), len);
+
+       if (size && copy_to_user(addr, mm->saved_auxv, size))
+               return -EFAULT;
+       return sizeof(mm->saved_auxv);
+}
+
 SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
                unsigned long, arg4, unsigned long, arg5)
 {
@@ -2507,6 +2529,11 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
                        else
                                return -EINVAL;
                        break;
+       case PR_GET_AUXV:
+               if (arg4 || arg5)
+                       return -EINVAL;
+               error = prctl_get_auxv((void __user *)arg2, arg3);
+               break;
                default:
                        return -EINVAL;
                }
@@ -2661,6 +2688,26 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
        case PR_SET_VMA:
                error = prctl_set_vma(arg2, arg3, arg4, arg5);
                break;
+#ifdef CONFIG_KSM
+       case PR_SET_MEMORY_MERGE:
+               if (arg3 || arg4 || arg5)
+                       return -EINVAL;
+               if (mmap_write_lock_killable(me->mm))
+                       return -EINTR;
+
+               if (arg2)
+                       error = ksm_enable_merge_any(me->mm);
+               else
+                       error = ksm_disable_merge_any(me->mm);
+               mmap_write_unlock(me->mm);
+               break;
+       case PR_GET_MEMORY_MERGE:
+               if (arg2 || arg3 || arg4 || arg5)
+                       return -EINVAL;
+
+               error = !!test_bit(MMF_VM_MERGE_ANY, &me->mm->flags);
+               break;
+#endif
        default:
                error = -EINVAL;
                break;