Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 16 Dec 2012 23:40:50 +0000 (15:40 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 16 Dec 2012 23:40:50 +0000 (15:40 -0800)
Pull security subsystem updates from James Morris:
 "A quiet cycle for the security subsystem with just a few maintenance
  updates."

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security:
  Smack: create a sysfs mount point for smackfs
  Smack: use select not depends in Kconfig
  Yama: remove locking from delete path
  Yama: add RCU to drop read locking
  drivers/char/tpm: remove tasklet and cleanup
  KEYS: Use keyring_alloc() to create special keyrings
  KEYS: Reduce initial permissions on keys
  KEYS: Make the session and process keyrings per-thread
  seccomp: Make syscall skipping and nr changes more consistent
  key: Fix resource leak
  keys: Fix unreachable code
  KEYS: Add payload preparsing opportunity prior to key instantiate or update

12 files changed:
1  2 
arch/x86/kernel/vsyscall_64.c
drivers/char/tpm/tpm_ibmvtpm.c
fs/cifs/cifsacl.c
fs/nfs/idmap.c
include/linux/key.h
kernel/cred.c
net/dns_resolver/dns_key.c
security/keys/key.c
security/keys/keyctl.c
security/keys/keyring.c
security/keys/process_keys.c
security/keys/request_key.c

index 3a3e8c9e280dcac9ba655491b4300c1d698bfb5a,b2e58a248b3bf2dfa68be8cfcb1d57077d4f7514..9a907a67be8f48abc0399017865a4697968ae2c3
@@@ -28,7 -28,7 +28,7 @@@
  #include <linux/jiffies.h>
  #include <linux/sysctl.h>
  #include <linux/topology.h>
 -#include <linux/clocksource.h>
 +#include <linux/timekeeper_internal.h>
  #include <linux/getcpu.h>
  #include <linux/cpu.h>
  #include <linux/smp.h>
@@@ -82,41 -82,32 +82,41 @@@ void update_vsyscall_tz(void
        vsyscall_gtod_data.sys_tz = sys_tz;
  }
  
 -void update_vsyscall(struct timespec *wall_time, struct timespec *wtm,
 -                      struct clocksource *clock, u32 mult)
 +void update_vsyscall(struct timekeeper *tk)
  {
 -      struct timespec monotonic;
 +      struct vsyscall_gtod_data *vdata = &vsyscall_gtod_data;
  
 -      write_seqcount_begin(&vsyscall_gtod_data.seq);
 +      write_seqcount_begin(&vdata->seq);
  
        /* copy vsyscall data */
 -      vsyscall_gtod_data.clock.vclock_mode    = clock->archdata.vclock_mode;
 -      vsyscall_gtod_data.clock.cycle_last     = clock->cycle_last;
 -      vsyscall_gtod_data.clock.mask           = clock->mask;
 -      vsyscall_gtod_data.clock.mult           = mult;
 -      vsyscall_gtod_data.clock.shift          = clock->shift;
 -
 -      vsyscall_gtod_data.wall_time_sec        = wall_time->tv_sec;
 -      vsyscall_gtod_data.wall_time_nsec       = wall_time->tv_nsec;
 +      vdata->clock.vclock_mode        = tk->clock->archdata.vclock_mode;
 +      vdata->clock.cycle_last         = tk->clock->cycle_last;
 +      vdata->clock.mask               = tk->clock->mask;
 +      vdata->clock.mult               = tk->mult;
 +      vdata->clock.shift              = tk->shift;
 +
 +      vdata->wall_time_sec            = tk->xtime_sec;
 +      vdata->wall_time_snsec          = tk->xtime_nsec;
 +
 +      vdata->monotonic_time_sec       = tk->xtime_sec
 +                                      + tk->wall_to_monotonic.tv_sec;
 +      vdata->monotonic_time_snsec     = tk->xtime_nsec
 +                                      + (tk->wall_to_monotonic.tv_nsec
 +                                              << tk->shift);
 +      while (vdata->monotonic_time_snsec >=
 +                                      (((u64)NSEC_PER_SEC) << tk->shift)) {
 +              vdata->monotonic_time_snsec -=
 +                                      ((u64)NSEC_PER_SEC) << tk->shift;
 +              vdata->monotonic_time_sec++;
 +      }
  
 -      monotonic = timespec_add(*wall_time, *wtm);
 -      vsyscall_gtod_data.monotonic_time_sec   = monotonic.tv_sec;
 -      vsyscall_gtod_data.monotonic_time_nsec  = monotonic.tv_nsec;
 +      vdata->wall_time_coarse.tv_sec  = tk->xtime_sec;
 +      vdata->wall_time_coarse.tv_nsec = (long)(tk->xtime_nsec >> tk->shift);
  
 -      vsyscall_gtod_data.wall_time_coarse     = __current_kernel_time();
 -      vsyscall_gtod_data.monotonic_time_coarse =
 -              timespec_add(vsyscall_gtod_data.wall_time_coarse, *wtm);
 +      vdata->monotonic_time_coarse    = timespec_add(vdata->wall_time_coarse,
 +                                                      tk->wall_to_monotonic);
  
 -      write_seqcount_end(&vsyscall_gtod_data.seq);
 +      write_seqcount_end(&vdata->seq);
  }
  
  static void warn_bad_vsyscall(const char *level, struct pt_regs *regs,
@@@ -145,19 -136,6 +145,6 @@@ static int addr_to_vsyscall_nr(unsigne
        return nr;
  }
  
- #ifdef CONFIG_SECCOMP
- static int vsyscall_seccomp(struct task_struct *tsk, int syscall_nr)
- {
-       if (!seccomp_mode(&tsk->seccomp))
-               return 0;
-       task_pt_regs(tsk)->orig_ax = syscall_nr;
-       task_pt_regs(tsk)->ax = syscall_nr;
-       return __secure_computing(syscall_nr);
- }
- #else
- #define vsyscall_seccomp(_tsk, _nr) 0
- #endif
  static bool write_ok_or_segv(unsigned long ptr, size_t size)
  {
        /*
@@@ -190,10 -168,9 +177,9 @@@ bool emulate_vsyscall(struct pt_regs *r
  {
        struct task_struct *tsk;
        unsigned long caller;
-       int vsyscall_nr;
+       int vsyscall_nr, syscall_nr, tmp;
        int prev_sig_on_uaccess_error;
        long ret;
-       int skip;
  
        /*
         * No point in checking CS -- the only way to get here is a user mode
        }
  
        tsk = current;
-       /*
-        * With a real vsyscall, page faults cause SIGSEGV.  We want to
-        * preserve that behavior to make writing exploits harder.
-        */
-       prev_sig_on_uaccess_error = current_thread_info()->sig_on_uaccess_error;
-       current_thread_info()->sig_on_uaccess_error = 1;
  
        /*
+        * Check for access_ok violations and find the syscall nr.
+        *
         * NULL is a valid user pointer (in the access_ok sense) on 32-bit and
         * 64-bit, so we don't need to special-case it here.  For all the
         * vsyscalls, NULL means "don't write anything" not "write it at
         * address 0".
         */
-       ret = -EFAULT;
-       skip = 0;
        switch (vsyscall_nr) {
        case 0:
-               skip = vsyscall_seccomp(tsk, __NR_gettimeofday);
-               if (skip)
-                       break;
                if (!write_ok_or_segv(regs->di, sizeof(struct timeval)) ||
-                   !write_ok_or_segv(regs->si, sizeof(struct timezone)))
-                       break;
+                   !write_ok_or_segv(regs->si, sizeof(struct timezone))) {
+                       ret = -EFAULT;
+                       goto check_fault;
+               }
  
+               syscall_nr = __NR_gettimeofday;
+               break;
+       case 1:
+               if (!write_ok_or_segv(regs->di, sizeof(time_t))) {
+                       ret = -EFAULT;
+                       goto check_fault;
+               }
+               syscall_nr = __NR_time;
+               break;
+       case 2:
+               if (!write_ok_or_segv(regs->di, sizeof(unsigned)) ||
+                   !write_ok_or_segv(regs->si, sizeof(unsigned))) {
+                       ret = -EFAULT;
+                       goto check_fault;
+               }
+               syscall_nr = __NR_getcpu;
+               break;
+       }
+       /*
+        * Handle seccomp.  regs->ip must be the original value.
+        * See seccomp_send_sigsys and Documentation/prctl/seccomp_filter.txt.
+        *
+        * We could optimize the seccomp disabled case, but performance
+        * here doesn't matter.
+        */
+       regs->orig_ax = syscall_nr;
+       regs->ax = -ENOSYS;
+       tmp = secure_computing(syscall_nr);
+       if ((!tmp && regs->orig_ax != syscall_nr) || regs->ip != address) {
+               warn_bad_vsyscall(KERN_DEBUG, regs,
+                                 "seccomp tried to change syscall nr or ip");
+               do_exit(SIGSYS);
+       }
+       if (tmp)
+               goto do_ret;  /* skip requested */
+       /*
+        * With a real vsyscall, page faults cause SIGSEGV.  We want to
+        * preserve that behavior to make writing exploits harder.
+        */
+       prev_sig_on_uaccess_error = current_thread_info()->sig_on_uaccess_error;
+       current_thread_info()->sig_on_uaccess_error = 1;
+       ret = -EFAULT;
+       switch (vsyscall_nr) {
+       case 0:
                ret = sys_gettimeofday(
                        (struct timeval __user *)regs->di,
                        (struct timezone __user *)regs->si);
                break;
  
        case 1:
-               skip = vsyscall_seccomp(tsk, __NR_time);
-               if (skip)
-                       break;
-               if (!write_ok_or_segv(regs->di, sizeof(time_t)))
-                       break;
                ret = sys_time((time_t __user *)regs->di);
                break;
  
        case 2:
-               skip = vsyscall_seccomp(tsk, __NR_getcpu);
-               if (skip)
-                       break;
-               if (!write_ok_or_segv(regs->di, sizeof(unsigned)) ||
-                   !write_ok_or_segv(regs->si, sizeof(unsigned)))
-                       break;
                ret = sys_getcpu((unsigned __user *)regs->di,
                                 (unsigned __user *)regs->si,
                                 NULL);
  
        current_thread_info()->sig_on_uaccess_error = prev_sig_on_uaccess_error;
  
-       if (skip) {
-               if ((long)regs->ax <= 0L) /* seccomp errno emulation */
-                       goto do_ret;
-               goto done; /* seccomp trace/trap */
-       }
+ check_fault:
        if (ret == -EFAULT) {
                /* Bad news -- userspace fed a bad pointer to a vsyscall. */
                warn_bad_vsyscall(KERN_INFO, regs,
@@@ -311,7 -311,6 +320,6 @@@ do_ret
        /* Emulate a ret instruction. */
        regs->ip = caller;
        regs->sp += 8;
- done:
        return true;
  
  sigsegv:
index 7da840d487d27c338483f446a84104658ff993da,88a95ea2ba030f80dc38b99c6800c62416b67f83..9978609d93b27ef2bdb92bafb72fdf481f1b089c
  
  static const char tpm_ibmvtpm_driver_name[] = "tpm_ibmvtpm";
  
 -static struct vio_device_id tpm_ibmvtpm_device_table[] __devinitdata = {
 +static struct vio_device_id tpm_ibmvtpm_device_table[] = {
        { "IBM,vtpm", "IBM,vtpm"},
        { "", "" }
  };
  MODULE_DEVICE_TABLE(vio, tpm_ibmvtpm_device_table);
  
- DECLARE_WAIT_QUEUE_HEAD(wq);
  /**
   * ibmvtpm_send_crq - Send a CRQ request
   * @vdev:     vio device struct
@@@ -83,6 -81,7 +81,7 @@@ static int tpm_ibmvtpm_recv(struct tpm_
  {
        struct ibmvtpm_dev *ibmvtpm;
        u16 len;
+       int sig;
  
        ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data;
  
                return 0;
        }
  
-       wait_event_interruptible(wq, ibmvtpm->crq_res.len != 0);
+       sig = wait_event_interruptible(ibmvtpm->wq, ibmvtpm->res_len != 0);
+       if (sig)
+               return -EINTR;
+       len = ibmvtpm->res_len;
  
-       if (count < ibmvtpm->crq_res.len) {
+       if (count < len) {
                dev_err(ibmvtpm->dev,
                        "Invalid size in recv: count=%ld, crq_size=%d\n",
-                       count, ibmvtpm->crq_res.len);
+                       count, len);
                return -EIO;
        }
  
        spin_lock(&ibmvtpm->rtce_lock);
-       memcpy((void *)buf, (void *)ibmvtpm->rtce_buf, ibmvtpm->crq_res.len);
-       memset(ibmvtpm->rtce_buf, 0, ibmvtpm->crq_res.len);
-       ibmvtpm->crq_res.valid = 0;
-       ibmvtpm->crq_res.msg = 0;
-       len = ibmvtpm->crq_res.len;
-       ibmvtpm->crq_res.len = 0;
+       memcpy((void *)buf, (void *)ibmvtpm->rtce_buf, len);
+       memset(ibmvtpm->rtce_buf, 0, len);
+       ibmvtpm->res_len = 0;
        spin_unlock(&ibmvtpm->rtce_lock);
        return len;
  }
@@@ -267,13 -267,12 +267,12 @@@ static int ibmvtpm_crq_send_init(struc
   * Return value:
   *    0
   */
 -static int __devexit tpm_ibmvtpm_remove(struct vio_dev *vdev)
 +static int tpm_ibmvtpm_remove(struct vio_dev *vdev)
  {
        struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev);
        int rc = 0;
  
        free_irq(vdev->irq, ibmvtpm);
-       tasklet_kill(&ibmvtpm->tasklet);
  
        do {
                if (rc)
@@@ -372,7 -371,6 +371,6 @@@ static int ibmvtpm_reset_crq(struct ibm
  static int tpm_ibmvtpm_resume(struct device *dev)
  {
        struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev);
-       unsigned long flags;
        int rc = 0;
  
        do {
                return rc;
        }
  
-       spin_lock_irqsave(&ibmvtpm->lock, flags);
-       vio_disable_interrupts(ibmvtpm->vdev);
-       tasklet_schedule(&ibmvtpm->tasklet);
-       spin_unlock_irqrestore(&ibmvtpm->lock, flags);
+       rc = vio_enable_interrupts(ibmvtpm->vdev);
+       if (rc) {
+               dev_err(dev, "Error vio_enable_interrupts rc=%d\n", rc);
+               return rc;
+       }
  
        rc = ibmvtpm_crq_send_init(ibmvtpm);
        if (rc)
@@@ -467,7 -466,7 +466,7 @@@ static struct ibmvtpm_crq *ibmvtpm_crq_
        if (crq->valid & VTPM_MSG_RES) {
                if (++crq_q->index == crq_q->num_entry)
                        crq_q->index = 0;
-               rmb();
+               smp_rmb();
        } else
                crq = NULL;
        return crq;
@@@ -535,11 -534,9 +534,9 @@@ static void ibmvtpm_crq_process(struct 
                        ibmvtpm->vtpm_version = crq->data;
                        return;
                case VTPM_TPM_COMMAND_RES:
-                       ibmvtpm->crq_res.valid = crq->valid;
-                       ibmvtpm->crq_res.msg = crq->msg;
-                       ibmvtpm->crq_res.len = crq->len;
-                       ibmvtpm->crq_res.data = crq->data;
-                       wake_up_interruptible(&wq);
+                       /* len of the data in rtce buffer */
+                       ibmvtpm->res_len = crq->len;
+                       wake_up_interruptible(&ibmvtpm->wq);
                        return;
                default:
                        return;
  static irqreturn_t ibmvtpm_interrupt(int irq, void *vtpm_instance)
  {
        struct ibmvtpm_dev *ibmvtpm = (struct ibmvtpm_dev *) vtpm_instance;
-       unsigned long flags;
-       spin_lock_irqsave(&ibmvtpm->lock, flags);
-       vio_disable_interrupts(ibmvtpm->vdev);
-       tasklet_schedule(&ibmvtpm->tasklet);
-       spin_unlock_irqrestore(&ibmvtpm->lock, flags);
-       return IRQ_HANDLED;
- }
- /**
-  * ibmvtpm_tasklet - Interrupt handler tasklet
-  * @data:     ibm vtpm device struct
-  *
-  * Returns:
-  *    Nothing
-  **/
- static void ibmvtpm_tasklet(void *data)
- {
-       struct ibmvtpm_dev *ibmvtpm = data;
        struct ibmvtpm_crq *crq;
-       unsigned long flags;
  
-       spin_lock_irqsave(&ibmvtpm->lock, flags);
+       /* while loop is needed for initial setup (get version and
+        * get rtce_size). There should be only one tpm request at any
+        * given time.
+        */
        while ((crq = ibmvtpm_crq_get_next(ibmvtpm)) != NULL) {
                ibmvtpm_crq_process(crq, ibmvtpm);
                crq->valid = 0;
-               wmb();
+               smp_wmb();
        }
  
-       vio_enable_interrupts(ibmvtpm->vdev);
-       spin_unlock_irqrestore(&ibmvtpm->lock, flags);
+       return IRQ_HANDLED;
  }
  
  /**
   *    0 - Success
   *    Non-zero - Failure
   */
 -static int __devinit tpm_ibmvtpm_probe(struct vio_dev *vio_dev,
 +static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev,
                                   const struct vio_device_id *id)
  {
        struct ibmvtpm_dev *ibmvtpm;
                goto reg_crq_cleanup;
        }
  
-       tasklet_init(&ibmvtpm->tasklet, (void *)ibmvtpm_tasklet,
-                    (unsigned long)ibmvtpm);
        rc = request_irq(vio_dev->irq, ibmvtpm_interrupt, 0,
                         tpm_ibmvtpm_driver_name, ibmvtpm);
        if (rc) {
                goto init_irq_cleanup;
        }
  
+       init_waitqueue_head(&ibmvtpm->wq);
        crq_q->index = 0;
  
        ibmvtpm->dev = dev;
        ibmvtpm->vdev = vio_dev;
        chip->vendor.data = (void *)ibmvtpm;
  
-       spin_lock_init(&ibmvtpm->lock);
        spin_lock_init(&ibmvtpm->rtce_lock);
  
        rc = ibmvtpm_crq_send_init(ibmvtpm);
  
        return rc;
  init_irq_cleanup:
-       tasklet_kill(&ibmvtpm->tasklet);
        do {
                rc1 = plpar_hcall_norets(H_FREE_CRQ, vio_dev->unit_address);
        } while (rc1 == H_BUSY || H_IS_LONG_BUSY(rc1));
diff --combined fs/cifs/cifsacl.c
index 75c1ee6991433d90886f5d88dcb239f8ba2cafd6,3151a264988ec1a98cc03d45e3d30f95fccf17bd..5cbd00e740671dc13a0f4a07621513eb469762ac
@@@ -42,27 -42,135 +42,27 @@@ static const struct cifs_sid sid_authus
  /* group users */
  static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
  
 -const struct cred *root_cred;
 -
 -static void
 -shrink_idmap_tree(struct rb_root *root, int nr_to_scan, int *nr_rem,
 -                      int *nr_del)
 -{
 -      struct rb_node *node;
 -      struct rb_node *tmp;
 -      struct cifs_sid_id *psidid;
 -
 -      node = rb_first(root);
 -      while (node) {
 -              tmp = node;
 -              node = rb_next(tmp);
 -              psidid = rb_entry(tmp, struct cifs_sid_id, rbnode);
 -              if (nr_to_scan == 0 || *nr_del == nr_to_scan)
 -                      ++(*nr_rem);
 -              else {
 -                      if (time_after(jiffies, psidid->time + SID_MAP_EXPIRE)
 -                                              && psidid->refcount == 0) {
 -                              rb_erase(tmp, root);
 -                              ++(*nr_del);
 -                      } else
 -                              ++(*nr_rem);
 -              }
 -      }
 -}
 -
 -/*
 - * Run idmap cache shrinker.
 - */
 -static int
 -cifs_idmap_shrinker(struct shrinker *shrink, struct shrink_control *sc)
 -{
 -      int nr_to_scan = sc->nr_to_scan;
 -      int nr_del = 0;
 -      int nr_rem = 0;
 -      struct rb_root *root;
 -
 -      root = &uidtree;
 -      spin_lock(&siduidlock);
 -      shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
 -      spin_unlock(&siduidlock);
 -
 -      root = &gidtree;
 -      spin_lock(&sidgidlock);
 -      shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
 -      spin_unlock(&sidgidlock);
 -
 -      root = &siduidtree;
 -      spin_lock(&uidsidlock);
 -      shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
 -      spin_unlock(&uidsidlock);
 -
 -      root = &sidgidtree;
 -      spin_lock(&gidsidlock);
 -      shrink_idmap_tree(root, nr_to_scan, &nr_rem, &nr_del);
 -      spin_unlock(&gidsidlock);
 -
 -      return nr_rem;
 -}
 -
 -static void
 -sid_rb_insert(struct rb_root *root, unsigned long cid,
 -              struct cifs_sid_id **psidid, char *typestr)
 -{
 -      char *strptr;
 -      struct rb_node *node = root->rb_node;
 -      struct rb_node *parent = NULL;
 -      struct rb_node **linkto = &(root->rb_node);
 -      struct cifs_sid_id *lsidid;
 -
 -      while (node) {
 -              lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
 -              parent = node;
 -              if (cid > lsidid->id) {
 -                      linkto = &(node->rb_left);
 -                      node = node->rb_left;
 -              }
 -              if (cid < lsidid->id) {
 -                      linkto = &(node->rb_right);
 -                      node = node->rb_right;
 -              }
 -      }
 -
 -      (*psidid)->id = cid;
 -      (*psidid)->time = jiffies - (SID_MAP_RETRY + 1);
 -      (*psidid)->refcount = 0;
 -
 -      sprintf((*psidid)->sidstr, "%s", typestr);
 -      strptr = (*psidid)->sidstr + strlen((*psidid)->sidstr);
 -      sprintf(strptr, "%ld", cid);
 -
 -      clear_bit(SID_ID_PENDING, &(*psidid)->state);
 -      clear_bit(SID_ID_MAPPED, &(*psidid)->state);
 -
 -      rb_link_node(&(*psidid)->rbnode, parent, linkto);
 -      rb_insert_color(&(*psidid)->rbnode, root);
 -}
 -
 -static struct cifs_sid_id *
 -sid_rb_search(struct rb_root *root, unsigned long cid)
 -{
 -      struct rb_node *node = root->rb_node;
 -      struct cifs_sid_id *lsidid;
 -
 -      while (node) {
 -              lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
 -              if (cid > lsidid->id)
 -                      node = node->rb_left;
 -              else if (cid < lsidid->id)
 -                      node = node->rb_right;
 -              else /* node found */
 -                      return lsidid;
 -      }
 -
 -      return NULL;
 -}
 -
 -static struct shrinker cifs_shrinker = {
 -      .shrink = cifs_idmap_shrinker,
 -      .seeks = DEFAULT_SEEKS,
 -};
 +static const struct cred *root_cred;
  
  static int
  cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep)
  {
        char *payload;
  
 +      /*
 +       * If the payload is less than or equal to the size of a pointer, then
 +       * an allocation here is wasteful. Just copy the data directly to the
 +       * payload.value union member instead.
 +       *
 +       * With this however, you must check the datalen before trying to
 +       * dereference payload.data!
 +       */
 +      if (prep->datalen <= sizeof(key->payload)) {
 +              key->payload.value = 0;
 +              memcpy(&key->payload.value, prep->data, prep->datalen);
 +              key->datalen = prep->datalen;
 +              return 0;
 +      }
        payload = kmalloc(prep->datalen, GFP_KERNEL);
        if (!payload)
                return -ENOMEM;
  static inline void
  cifs_idmap_key_destroy(struct key *key)
  {
 -      kfree(key->payload.data);
 +      if (key->datalen > sizeof(key->payload))
 +              kfree(key->payload.data);
  }
  
 -struct key_type cifs_idmap_key_type = {
 +static struct key_type cifs_idmap_key_type = {
        .name        = "cifs.idmap",
        .instantiate = cifs_idmap_key_instantiate,
        .destroy     = cifs_idmap_key_destroy,
        .match       = user_match,
  };
  
 -static void
 -sid_to_str(struct cifs_sid *sidptr, char *sidstr)
 +static char *
 +sid_to_key_str(struct cifs_sid *sidptr, unsigned int type)
  {
 -      int i;
 -      unsigned long saval;
 -      char *strptr;
 +      int i, len;
 +      unsigned int saval;
 +      char *sidstr, *strptr;
 +      unsigned long long id_auth_val;
 +
 +      /* 3 bytes for prefix */
 +      sidstr = kmalloc(3 + SID_STRING_BASE_SIZE +
 +                       (SID_STRING_SUBAUTH_SIZE * sidptr->num_subauth),
 +                       GFP_KERNEL);
 +      if (!sidstr)
 +              return sidstr;
  
        strptr = sidstr;
 +      len = sprintf(strptr, "%cs:S-%hhu", type == SIDOWNER ? 'o' : 'g',
 +                      sidptr->revision);
 +      strptr += len;
 +
 +      /* The authority field is a single 48-bit number */
 +      id_auth_val = (unsigned long long)sidptr->authority[5];
 +      id_auth_val |= (unsigned long long)sidptr->authority[4] << 8;
 +      id_auth_val |= (unsigned long long)sidptr->authority[3] << 16;
 +      id_auth_val |= (unsigned long long)sidptr->authority[2] << 24;
 +      id_auth_val |= (unsigned long long)sidptr->authority[1] << 32;
 +      id_auth_val |= (unsigned long long)sidptr->authority[0] << 48;
  
 -      sprintf(strptr, "%s", "S");
 -      strptr = sidstr + strlen(sidstr);
 -
 -      sprintf(strptr, "-%d", sidptr->revision);
 -      strptr = sidstr + strlen(sidstr);
 +      /*
 +       * MS-DTYP states that if the authority is >= 2^32, then it should be
 +       * expressed as a hex value.
 +       */
 +      if (id_auth_val <= UINT_MAX)
 +              len = sprintf(strptr, "-%llu", id_auth_val);
 +      else
 +              len = sprintf(strptr, "-0x%llx", id_auth_val);
  
 -      for (i = 0; i < 6; ++i) {
 -              if (sidptr->authority[i]) {
 -                      sprintf(strptr, "-%d", sidptr->authority[i]);
 -                      strptr = sidstr + strlen(sidstr);
 -              }
 -      }
 +      strptr += len;
  
        for (i = 0; i < sidptr->num_subauth; ++i) {
                saval = le32_to_cpu(sidptr->sub_auth[i]);
 -              sprintf(strptr, "-%ld", saval);
 -              strptr = sidstr + strlen(sidstr);
 +              len = sprintf(strptr, "-%u", saval);
 +              strptr += len;
        }
 +
 +      return sidstr;
  }
  
 -static void
 -id_rb_insert(struct rb_root *root, struct cifs_sid *sidptr,
 -              struct cifs_sid_id **psidid, char *typestr)
 +/*
 + * if the two SIDs (roughly equivalent to a UUID for a user or group) are
 + * the same returns zero, if they do not match returns non-zero.
 + */
 +static int
 +compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
  {
 -      int rc;
 -      char *strptr;
 -      struct rb_node *node = root->rb_node;
 -      struct rb_node *parent = NULL;
 -      struct rb_node **linkto = &(root->rb_node);
 -      struct cifs_sid_id *lsidid;
 -
 -      while (node) {
 -              lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
 -              parent = node;
 -              rc = compare_sids(sidptr, &((lsidid)->sid));
 -              if (rc > 0) {
 -                      linkto = &(node->rb_left);
 -                      node = node->rb_left;
 -              } else if (rc < 0) {
 -                      linkto = &(node->rb_right);
 -                      node = node->rb_right;
 -              }
 -      }
 -
 -      memcpy(&(*psidid)->sid, sidptr, sizeof(struct cifs_sid));
 -      (*psidid)->time = jiffies - (SID_MAP_RETRY + 1);
 -      (*psidid)->refcount = 0;
 +      int i;
 +      int num_subauth, num_sat, num_saw;
  
 -      sprintf((*psidid)->sidstr, "%s", typestr);
 -      strptr = (*psidid)->sidstr + strlen((*psidid)->sidstr);
 -      sid_to_str(&(*psidid)->sid, strptr);
 +      if ((!ctsid) || (!cwsid))
 +              return 1;
  
 -      clear_bit(SID_ID_PENDING, &(*psidid)->state);
 -      clear_bit(SID_ID_MAPPED, &(*psidid)->state);
 +      /* compare the revision */
 +      if (ctsid->revision != cwsid->revision) {
 +              if (ctsid->revision > cwsid->revision)
 +                      return 1;
 +              else
 +                      return -1;
 +      }
  
 -      rb_link_node(&(*psidid)->rbnode, parent, linkto);
 -      rb_insert_color(&(*psidid)->rbnode, root);
 -}
 +      /* compare all of the six auth values */
 +      for (i = 0; i < NUM_AUTHS; ++i) {
 +              if (ctsid->authority[i] != cwsid->authority[i]) {
 +                      if (ctsid->authority[i] > cwsid->authority[i])
 +                              return 1;
 +                      else
 +                              return -1;
 +              }
 +      }
  
 -static struct cifs_sid_id *
 -id_rb_search(struct rb_root *root, struct cifs_sid *sidptr)
 -{
 -      int rc;
 -      struct rb_node *node = root->rb_node;
 -      struct cifs_sid_id *lsidid;
 -
 -      while (node) {
 -              lsidid = rb_entry(node, struct cifs_sid_id, rbnode);
 -              rc = compare_sids(sidptr, &((lsidid)->sid));
 -              if (rc > 0) {
 -                      node = node->rb_left;
 -              } else if (rc < 0) {
 -                      node = node->rb_right;
 -              } else /* node found */
 -                      return lsidid;
 +      /* compare all of the subauth values if any */
 +      num_sat = ctsid->num_subauth;
 +      num_saw = cwsid->num_subauth;
 +      num_subauth = num_sat < num_saw ? num_sat : num_saw;
 +      if (num_subauth) {
 +              for (i = 0; i < num_subauth; ++i) {
 +                      if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) {
 +                              if (le32_to_cpu(ctsid->sub_auth[i]) >
 +                                      le32_to_cpu(cwsid->sub_auth[i]))
 +                                      return 1;
 +                              else
 +                                      return -1;
 +                      }
 +              }
        }
  
 -      return NULL;
 +      return 0; /* sids compare/match */
  }
  
 -static int
 -sidid_pending_wait(void *unused)
 +static void
 +cifs_copy_sid(struct cifs_sid *dst, const struct cifs_sid *src)
  {
 -      schedule();
 -      return signal_pending(current) ? -ERESTARTSYS : 0;
 +      int i;
 +
 +      dst->revision = src->revision;
 +      dst->num_subauth = min_t(u8, src->num_subauth, SID_MAX_SUB_AUTHORITIES);
 +      for (i = 0; i < NUM_AUTHS; ++i)
 +              dst->authority[i] = src->authority[i];
 +      for (i = 0; i < dst->num_subauth; ++i)
 +              dst->sub_auth[i] = src->sub_auth[i];
  }
  
  static int
 -id_to_sid(unsigned long cid, uint sidtype, struct cifs_sid *ssid)
 +id_to_sid(unsigned int cid, uint sidtype, struct cifs_sid *ssid)
  {
 -      int rc = 0;
 +      int rc;
        struct key *sidkey;
 +      struct cifs_sid *ksid;
 +      unsigned int ksid_size;
 +      char desc[3 + 10 + 1]; /* 3 byte prefix + 10 bytes for value + NULL */
        const struct cred *saved_cred;
 -      struct cifs_sid *lsid;
 -      struct cifs_sid_id *psidid, *npsidid;
 -      struct rb_root *cidtree;
 -      spinlock_t *cidlock;
 -
 -      if (sidtype == SIDOWNER) {
 -              cidlock = &siduidlock;
 -              cidtree = &uidtree;
 -      } else if (sidtype == SIDGROUP) {
 -              cidlock = &sidgidlock;
 -              cidtree = &gidtree;
 -      } else
 -              return -EINVAL;
  
 -      spin_lock(cidlock);
 -      psidid = sid_rb_search(cidtree, cid);
 -
 -      if (!psidid) { /* node does not exist, allocate one & attempt adding */
 -              spin_unlock(cidlock);
 -              npsidid = kzalloc(sizeof(struct cifs_sid_id), GFP_KERNEL);
 -              if (!npsidid)
 -                      return -ENOMEM;
 -
 -              npsidid->sidstr = kmalloc(SIDLEN, GFP_KERNEL);
 -              if (!npsidid->sidstr) {
 -                      kfree(npsidid);
 -                      return -ENOMEM;
 -              }
 +      rc = snprintf(desc, sizeof(desc), "%ci:%u",
 +                      sidtype == SIDOWNER ? 'o' : 'g', cid);
 +      if (rc >= sizeof(desc))
 +              return -EINVAL;
  
 -              spin_lock(cidlock);
 -              psidid = sid_rb_search(cidtree, cid);
 -              if (psidid) { /* node happened to get inserted meanwhile */
 -                      ++psidid->refcount;
 -                      spin_unlock(cidlock);
 -                      kfree(npsidid->sidstr);
 -                      kfree(npsidid);
 -              } else {
 -                      psidid = npsidid;
 -                      sid_rb_insert(cidtree, cid, &psidid,
 -                                      sidtype == SIDOWNER ? "oi:" : "gi:");
 -                      ++psidid->refcount;
 -                      spin_unlock(cidlock);
 -              }
 -      } else {
 -              ++psidid->refcount;
 -              spin_unlock(cidlock);
 +      rc = 0;
 +      saved_cred = override_creds(root_cred);
 +      sidkey = request_key(&cifs_idmap_key_type, desc, "");
 +      if (IS_ERR(sidkey)) {
 +              rc = -EINVAL;
 +              cFYI(1, "%s: Can't map %cid %u to a SID", __func__,
 +                      sidtype == SIDOWNER ? 'u' : 'g', cid);
 +              goto out_revert_creds;
 +      } else if (sidkey->datalen < CIFS_SID_BASE_SIZE) {
 +              rc = -EIO;
 +              cFYI(1, "%s: Downcall contained malformed key "
 +                      "(datalen=%hu)", __func__, sidkey->datalen);
 +              goto invalidate_key;
        }
  
        /*
 -       * If we are here, it is safe to access psidid and its fields
 -       * since a reference was taken earlier while holding the spinlock.
 -       * A reference on the node is put without holding the spinlock
 -       * and it is OK to do so in this case, shrinker will not erase
 -       * this node until all references are put and we do not access
 -       * any fields of the node after a reference is put .
 +       * A sid is usually too large to be embedded in payload.value, but if
 +       * there are no subauthorities and the host has 8-byte pointers, then
 +       * it could be.
         */
 -      if (test_bit(SID_ID_MAPPED, &psidid->state)) {
 -              memcpy(ssid, &psidid->sid, sizeof(struct cifs_sid));
 -              psidid->time = jiffies; /* update ts for accessing */
 -              goto id_sid_out;
 -      }
 -
 -      if (time_after(psidid->time + SID_MAP_RETRY, jiffies)) {
 -              rc = -EINVAL;
 -              goto id_sid_out;
 +      ksid = sidkey->datalen <= sizeof(sidkey->payload) ?
 +              (struct cifs_sid *)&sidkey->payload.value :
 +              (struct cifs_sid *)sidkey->payload.data;
 +
 +      ksid_size = CIFS_SID_BASE_SIZE + (ksid->num_subauth * sizeof(__le32));
 +      if (ksid_size > sidkey->datalen) {
 +              rc = -EIO;
 +              cFYI(1, "%s: Downcall contained malformed key (datalen=%hu, "
 +                      "ksid_size=%u)", __func__, sidkey->datalen, ksid_size);
 +              goto invalidate_key;
        }
  
 -      if (!test_and_set_bit(SID_ID_PENDING, &psidid->state)) {
 -              saved_cred = override_creds(root_cred);
 -              sidkey = request_key(&cifs_idmap_key_type, psidid->sidstr, "");
 -              if (IS_ERR(sidkey)) {
 -                      rc = -EINVAL;
 -                      cFYI(1, "%s: Can't map and id to a SID", __func__);
 -              } else {
 -                      lsid = (struct cifs_sid *)sidkey->payload.data;
 -                      memcpy(&psidid->sid, lsid,
 -                              sidkey->datalen < sizeof(struct cifs_sid) ?
 -                              sidkey->datalen : sizeof(struct cifs_sid));
 -                      memcpy(ssid, &psidid->sid,
 -                              sidkey->datalen < sizeof(struct cifs_sid) ?
 -                              sidkey->datalen : sizeof(struct cifs_sid));
 -                      set_bit(SID_ID_MAPPED, &psidid->state);
 -                      key_put(sidkey);
 -                      kfree(psidid->sidstr);
 -              }
 -              psidid->time = jiffies; /* update ts for accessing */
 -              revert_creds(saved_cred);
 -              clear_bit(SID_ID_PENDING, &psidid->state);
 -              wake_up_bit(&psidid->state, SID_ID_PENDING);
 -      } else {
 -              rc = wait_on_bit(&psidid->state, SID_ID_PENDING,
 -                              sidid_pending_wait, TASK_INTERRUPTIBLE);
 -              if (rc) {
 -                      cFYI(1, "%s: sidid_pending_wait interrupted %d",
 -                                      __func__, rc);
 -                      --psidid->refcount;
 -                      return rc;
 -              }
 -              if (test_bit(SID_ID_MAPPED, &psidid->state))
 -                      memcpy(ssid, &psidid->sid, sizeof(struct cifs_sid));
 -              else
 -                      rc = -EINVAL;
 -      }
 -id_sid_out:
 -      --psidid->refcount;
 +      cifs_copy_sid(ssid, ksid);
 +out_key_put:
 +      key_put(sidkey);
 +out_revert_creds:
 +      revert_creds(saved_cred);
        return rc;
 +
 +invalidate_key:
 +      key_invalidate(sidkey);
 +      goto out_key_put;
  }
  
  static int
@@@ -263,67 -410,111 +263,67 @@@ sid_to_id(struct cifs_sb_info *cifs_sb
                struct cifs_fattr *fattr, uint sidtype)
  {
        int rc;
 -      unsigned long cid;
 -      struct key *idkey;
 +      struct key *sidkey;
 +      char *sidstr;
        const struct cred *saved_cred;
 -      struct cifs_sid_id *psidid, *npsidid;
 -      struct rb_root *cidtree;
 -      spinlock_t *cidlock;
 -
 -      if (sidtype == SIDOWNER) {
 -              cid = cifs_sb->mnt_uid; /* default uid, in case upcall fails */
 -              cidlock = &siduidlock;
 -              cidtree = &uidtree;
 -      } else if (sidtype == SIDGROUP) {
 -              cid = cifs_sb->mnt_gid; /* default gid, in case upcall fails */
 -              cidlock = &sidgidlock;
 -              cidtree = &gidtree;
 -      } else
 -              return -ENOENT;
 -
 -      spin_lock(cidlock);
 -      psidid = id_rb_search(cidtree, psid);
 -
 -      if (!psidid) { /* node does not exist, allocate one & attempt adding */
 -              spin_unlock(cidlock);
 -              npsidid = kzalloc(sizeof(struct cifs_sid_id), GFP_KERNEL);
 -              if (!npsidid)
 -                      return -ENOMEM;
 -
 -              npsidid->sidstr = kmalloc(SIDLEN, GFP_KERNEL);
 -              if (!npsidid->sidstr) {
 -                      kfree(npsidid);
 -                      return -ENOMEM;
 -              }
 -
 -              spin_lock(cidlock);
 -              psidid = id_rb_search(cidtree, psid);
 -              if (psidid) { /* node happened to get inserted meanwhile */
 -                      ++psidid->refcount;
 -                      spin_unlock(cidlock);
 -                      kfree(npsidid->sidstr);
 -                      kfree(npsidid);
 -              } else {
 -                      psidid = npsidid;
 -                      id_rb_insert(cidtree, psid, &psidid,
 -                                      sidtype == SIDOWNER ? "os:" : "gs:");
 -                      ++psidid->refcount;
 -                      spin_unlock(cidlock);
 -              }
 -      } else {
 -              ++psidid->refcount;
 -              spin_unlock(cidlock);
 -      }
 +      uid_t fuid = cifs_sb->mnt_uid;
 +      gid_t fgid = cifs_sb->mnt_gid;
  
        /*
 -       * If we are here, it is safe to access psidid and its fields
 -       * since a reference was taken earlier while holding the spinlock.
 -       * A reference on the node is put without holding the spinlock
 -       * and it is OK to do so in this case, shrinker will not erase
 -       * this node until all references are put and we do not access
 -       * any fields of the node after a reference is put .
 +       * If we have too many subauthorities, then something is really wrong.
 +       * Just return an error.
         */
 -      if (test_bit(SID_ID_MAPPED, &psidid->state)) {
 -              cid = psidid->id;
 -              psidid->time = jiffies; /* update ts for accessing */
 -              goto sid_to_id_out;
 +      if (unlikely(psid->num_subauth > SID_MAX_SUB_AUTHORITIES)) {
 +              cFYI(1, "%s: %u subauthorities is too many!", __func__,
 +                      psid->num_subauth);
 +              return -EIO;
        }
  
 -      if (time_after(psidid->time + SID_MAP_RETRY, jiffies))
 -              goto sid_to_id_out;
 -
 -      if (!test_and_set_bit(SID_ID_PENDING, &psidid->state)) {
 -              saved_cred = override_creds(root_cred);
 -              idkey = request_key(&cifs_idmap_key_type, psidid->sidstr, "");
 -              if (IS_ERR(idkey))
 -                      cFYI(1, "%s: Can't map SID to an id", __func__);
 -              else {
 -                      cid = *(unsigned long *)idkey->payload.value;
 -                      psidid->id = cid;
 -                      set_bit(SID_ID_MAPPED, &psidid->state);
 -                      key_put(idkey);
 -                      kfree(psidid->sidstr);
 -              }
 -              revert_creds(saved_cred);
 -              psidid->time = jiffies; /* update ts for accessing */
 -              clear_bit(SID_ID_PENDING, &psidid->state);
 -              wake_up_bit(&psidid->state, SID_ID_PENDING);
 -      } else {
 -              rc = wait_on_bit(&psidid->state, SID_ID_PENDING,
 -                              sidid_pending_wait, TASK_INTERRUPTIBLE);
 -              if (rc) {
 -                      cFYI(1, "%s: sidid_pending_wait interrupted %d",
 -                                      __func__, rc);
 -                      --psidid->refcount; /* decremented without spinlock */
 -                      return rc;
 -              }
 -              if (test_bit(SID_ID_MAPPED, &psidid->state))
 -                      cid = psidid->id;
 +      sidstr = sid_to_key_str(psid, sidtype);
 +      if (!sidstr)
 +              return -ENOMEM;
 +
 +      saved_cred = override_creds(root_cred);
 +      sidkey = request_key(&cifs_idmap_key_type, sidstr, "");
 +      if (IS_ERR(sidkey)) {
 +              rc = -EINVAL;
 +              cFYI(1, "%s: Can't map SID %s to a %cid", __func__, sidstr,
 +                      sidtype == SIDOWNER ? 'u' : 'g');
 +              goto out_revert_creds;
 +      }
 +
 +      /*
 +       * FIXME: Here we assume that uid_t and gid_t are same size. It's
 +       * probably a safe assumption but might be better to check based on
 +       * sidtype.
 +       */
 +      if (sidkey->datalen != sizeof(uid_t)) {
 +              rc = -EIO;
 +              cFYI(1, "%s: Downcall contained malformed key "
 +                      "(datalen=%hu)", __func__, sidkey->datalen);
 +              key_invalidate(sidkey);
 +              goto out_key_put;
        }
  
 -sid_to_id_out:
 -      --psidid->refcount; /* decremented without spinlock */
        if (sidtype == SIDOWNER)
 -              fattr->cf_uid = cid;
 +              memcpy(&fuid, &sidkey->payload.value, sizeof(uid_t));
        else
 -              fattr->cf_gid = cid;
 +              memcpy(&fgid, &sidkey->payload.value, sizeof(gid_t));
 +
 +out_key_put:
 +      key_put(sidkey);
 +out_revert_creds:
 +      revert_creds(saved_cred);
 +      kfree(sidstr);
  
 +      /*
 +       * Note that we return 0 here unconditionally. If the mapping
 +       * fails then we just fall back to using the mnt_uid/mnt_gid.
 +       */
 +      if (sidtype == SIDOWNER)
 +              fattr->cf_uid = fuid;
 +      else
 +              fattr->cf_gid = fgid;
        return 0;
  }
  
@@@ -346,19 -537,15 +346,15 @@@ init_cifs_idmap(void
        if (!cred)
                return -ENOMEM;
  
-       keyring = key_alloc(&key_type_keyring, ".cifs_idmap", 0, 0, cred,
-                           (KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                           KEY_USR_VIEW | KEY_USR_READ,
-                           KEY_ALLOC_NOT_IN_QUOTA);
+       keyring = keyring_alloc(".cifs_idmap", 0, 0, cred,
+                               (KEY_POS_ALL & ~KEY_POS_SETATTR) |
+                               KEY_USR_VIEW | KEY_USR_READ,
+                               KEY_ALLOC_NOT_IN_QUOTA, NULL);
        if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
                goto failed_put_cred;
        }
  
-       ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
-       if (ret < 0)
-               goto failed_put_key;
        ret = register_key_type(&cifs_idmap_key_type);
        if (ret < 0)
                goto failed_put_key;
        cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
        root_cred = cred;
  
 -      spin_lock_init(&siduidlock);
 -      uidtree = RB_ROOT;
 -      spin_lock_init(&sidgidlock);
 -      gidtree = RB_ROOT;
 -
 -      spin_lock_init(&uidsidlock);
 -      siduidtree = RB_ROOT;
 -      spin_lock_init(&gidsidlock);
 -      sidgidtree = RB_ROOT;
 -      register_shrinker(&cifs_shrinker);
 -
        cFYI(1, "cifs idmap keyring: %d", key_serial(keyring));
        return 0;
  
@@@ -386,13 -584,95 +382,13 @@@ exit_cifs_idmap(void
        key_revoke(root_cred->thread_keyring);
        unregister_key_type(&cifs_idmap_key_type);
        put_cred(root_cred);
 -      unregister_shrinker(&cifs_shrinker);
        cFYI(1, "Unregistered %s key type", cifs_idmap_key_type.name);
  }
  
 -void
 -cifs_destroy_idmaptrees(void)
 -{
 -      struct rb_root *root;
 -      struct rb_node *node;
 -
 -      root = &uidtree;
 -      spin_lock(&siduidlock);
 -      while ((node = rb_first(root)))
 -              rb_erase(node, root);
 -      spin_unlock(&siduidlock);
 -
 -      root = &gidtree;
 -      spin_lock(&sidgidlock);
 -      while ((node = rb_first(root)))
 -              rb_erase(node, root);
 -      spin_unlock(&sidgidlock);
 -
 -      root = &siduidtree;
 -      spin_lock(&uidsidlock);
 -      while ((node = rb_first(root)))
 -              rb_erase(node, root);
 -      spin_unlock(&uidsidlock);
 -
 -      root = &sidgidtree;
 -      spin_lock(&gidsidlock);
 -      while ((node = rb_first(root)))
 -              rb_erase(node, root);
 -      spin_unlock(&gidsidlock);
 -}
 -
 -/* if the two SIDs (roughly equivalent to a UUID for a user or group) are
 -   the same returns 1, if they do not match returns 0 */
 -int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
 -{
 -      int i;
 -      int num_subauth, num_sat, num_saw;
 -
 -      if ((!ctsid) || (!cwsid))
 -              return 1;
 -
 -      /* compare the revision */
 -      if (ctsid->revision != cwsid->revision) {
 -              if (ctsid->revision > cwsid->revision)
 -                      return 1;
 -              else
 -                      return -1;
 -      }
 -
 -      /* compare all of the six auth values */
 -      for (i = 0; i < 6; ++i) {
 -              if (ctsid->authority[i] != cwsid->authority[i]) {
 -                      if (ctsid->authority[i] > cwsid->authority[i])
 -                              return 1;
 -                      else
 -                              return -1;
 -              }
 -      }
 -
 -      /* compare all of the subauth values if any */
 -      num_sat = ctsid->num_subauth;
 -      num_saw = cwsid->num_subauth;
 -      num_subauth = num_sat < num_saw ? num_sat : num_saw;
 -      if (num_subauth) {
 -              for (i = 0; i < num_subauth; ++i) {
 -                      if (ctsid->sub_auth[i] != cwsid->sub_auth[i]) {
 -                              if (le32_to_cpu(ctsid->sub_auth[i]) >
 -                                      le32_to_cpu(cwsid->sub_auth[i]))
 -                                      return 1;
 -                              else
 -                                      return -1;
 -                      }
 -              }
 -      }
 -
 -      return 0; /* sids compare/match */
 -}
 -
 -
  /* copy ntsd, owner sid, and group sid from a security descriptor to another */
  static void copy_sec_desc(const struct cifs_ntsd *pntsd,
                                struct cifs_ntsd *pnntsd, __u32 sidsoffset)
  {
 -      int i;
 -
        struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
        struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
  
        owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
                                le32_to_cpu(pntsd->osidoffset));
        nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset);
 -
 -      nowner_sid_ptr->revision = owner_sid_ptr->revision;
 -      nowner_sid_ptr->num_subauth = owner_sid_ptr->num_subauth;
 -      for (i = 0; i < 6; i++)
 -              nowner_sid_ptr->authority[i] = owner_sid_ptr->authority[i];
 -      for (i = 0; i < 5; i++)
 -              nowner_sid_ptr->sub_auth[i] = owner_sid_ptr->sub_auth[i];
 +      cifs_copy_sid(nowner_sid_ptr, owner_sid_ptr);
  
        /* copy group sid */
        group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
                                le32_to_cpu(pntsd->gsidoffset));
        ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset +
                                        sizeof(struct cifs_sid));
 -
 -      ngroup_sid_ptr->revision = group_sid_ptr->revision;
 -      ngroup_sid_ptr->num_subauth = group_sid_ptr->num_subauth;
 -      for (i = 0; i < 6; i++)
 -              ngroup_sid_ptr->authority[i] = group_sid_ptr->authority[i];
 -      for (i = 0; i < 5; i++)
 -              ngroup_sid_ptr->sub_auth[i] = group_sid_ptr->sub_auth[i];
 +      cifs_copy_sid(ngroup_sid_ptr, group_sid_ptr);
  
        return;
  }
@@@ -522,7 -814,7 +518,7 @@@ static __u16 fill_ace_for_sid(struct ci
  
        pntace->sid.revision = psid->revision;
        pntace->sid.num_subauth = psid->num_subauth;
 -      for (i = 0; i < 6; i++)
 +      for (i = 0; i < NUM_AUTHS; i++)
                pntace->sid.authority[i] = psid->authority[i];
        for (i = 0; i < psid->num_subauth; i++)
                pntace->sid.sub_auth[i] = psid->sub_auth[i];
@@@ -698,8 -990,8 +694,8 @@@ static int parse_sid(struct cifs_sid *p
                return -EINVAL;
        }
  
 -      if (psid->num_subauth) {
  #ifdef CONFIG_CIFS_DEBUG2
 +      if (psid->num_subauth) {
                int i;
                cFYI(1, "SID revision %d num_auth %d",
                        psid->revision, psid->num_subauth);
                        num auths and therefore go off the end */
                cFYI(1, "RID 0x%x",
                        le32_to_cpu(psid->sub_auth[psid->num_subauth-1]));
 -#endif
        }
 +#endif
  
        return 0;
  }
@@@ -824,7 -1116,8 +820,7 @@@ static int build_sec_desc(struct cifs_n
                                kfree(nowner_sid_ptr);
                                return rc;
                        }
 -                      memcpy(owner_sid_ptr, nowner_sid_ptr,
 -                                      sizeof(struct cifs_sid));
 +                      cifs_copy_sid(owner_sid_ptr, nowner_sid_ptr);
                        kfree(nowner_sid_ptr);
                        *aclflag = CIFS_ACL_OWNER;
                }
                                kfree(ngroup_sid_ptr);
                                return rc;
                        }
 -                      memcpy(group_sid_ptr, ngroup_sid_ptr,
 -                                      sizeof(struct cifs_sid));
 +                      cifs_copy_sid(group_sid_ptr, ngroup_sid_ptr);
                        kfree(ngroup_sid_ptr);
                        *aclflag = CIFS_ACL_GROUP;
                }
@@@ -924,7 -1218,7 +920,7 @@@ struct cifs_ntsd *get_cifs_acl(struct c
        if (!open_file)
                return get_cifs_acl_by_path(cifs_sb, path, pacllen);
  
 -      pntsd = get_cifs_acl_by_fid(cifs_sb, open_file->netfid, pacllen);
 +      pntsd = get_cifs_acl_by_fid(cifs_sb, open_file->fid.netfid, pacllen);
        cifsFileInfo_put(open_file);
        return pntsd;
  }
@@@ -1018,39 -1312,42 +1014,39 @@@ id_mode_to_cifs_acl(struct inode *inode
  
        /* Get the security descriptor */
        pntsd = get_cifs_acl(CIFS_SB(inode->i_sb), inode, path, &secdesclen);
 -
 -      /* Add three ACEs for owner, group, everyone getting rid of
 -         other ACEs as chmod disables ACEs and set the security descriptor */
 -
        if (IS_ERR(pntsd)) {
                rc = PTR_ERR(pntsd);
                cERROR(1, "%s: error %d getting sec desc", __func__, rc);
 -      } else {
 -              /* allocate memory for the smb header,
 -                 set security descriptor request security descriptor
 -                 parameters, and secuirty descriptor itself */
 -
 -              secdesclen = secdesclen < DEFSECDESCLEN ?
 -                                      DEFSECDESCLEN : secdesclen;
 -              pnntsd = kmalloc(secdesclen, GFP_KERNEL);
 -              if (!pnntsd) {
 -                      cERROR(1, "Unable to allocate security descriptor");
 -                      kfree(pntsd);
 -                      return -ENOMEM;
 -              }
 +              goto out;
 +      }
  
 -              rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid,
 -                                      &aclflag);
 +      /*
 +       * Add three ACEs for owner, group, everyone getting rid of other ACEs
 +       * as chmod disables ACEs and set the security descriptor. Allocate
 +       * memory for the smb header, set security descriptor request security
 +       * descriptor parameters, and secuirty descriptor itself
 +       */
 +      secdesclen = max_t(u32, secdesclen, DEFAULT_SEC_DESC_LEN);
 +      pnntsd = kmalloc(secdesclen, GFP_KERNEL);
 +      if (!pnntsd) {
 +              cERROR(1, "Unable to allocate security descriptor");
 +              kfree(pntsd);
 +              return -ENOMEM;
 +      }
  
 -              cFYI(DBG2, "build_sec_desc rc: %d", rc);
 +      rc = build_sec_desc(pntsd, pnntsd, secdesclen, nmode, uid, gid,
 +                              &aclflag);
  
 -              if (!rc) {
 -                      /* Set the security descriptor */
 -                      rc = set_cifs_acl(pnntsd, secdesclen, inode,
 -                                              path, aclflag);
 -                      cFYI(DBG2, "set_cifs_acl rc: %d", rc);
 -              }
 +      cFYI(DBG2, "build_sec_desc rc: %d", rc);
  
 -              kfree(pnntsd);
 -              kfree(pntsd);
 +      if (!rc) {
 +              /* Set the security descriptor */
 +              rc = set_cifs_acl(pnntsd, secdesclen, inode, path, aclflag);
 +              cFYI(DBG2, "set_cifs_acl rc: %d", rc);
        }
  
 +      kfree(pnntsd);
 +      kfree(pntsd);
 +out:
        return rc;
  }
diff --combined fs/nfs/idmap.c
index 9cc4a3fbf4b0d4ab3106b7584ba756776005e6f8,957134b4c0fd47c59977607aa27b05d096bd0e0c..bc3968fa81e53c1164b9ebb93c7f122303731143
  static const struct cred *id_resolver_cache;
  static struct key_type key_type_id_resolver_legacy;
  
 -struct idmap {
 -      struct rpc_pipe         *idmap_pipe;
 -      struct key_construction *idmap_key_cons;
 -      struct mutex            idmap_mutex;
 -};
 -
  struct idmap_legacy_upcalldata {
        struct rpc_pipe_msg pipe_msg;
        struct idmap_msg idmap_msg;
 +      struct key_construction *key_cons;
        struct idmap *idmap;
  };
  
 +struct idmap {
 +      struct rpc_pipe         *idmap_pipe;
 +      struct idmap_legacy_upcalldata *idmap_upcall_data;
 +      struct mutex            idmap_mutex;
 +};
 +
  /**
   * nfs_fattr_init_names - initialise the nfs_fattr owner_name/group_name fields
   * @fattr: fully initialised struct nfs_fattr
@@@ -159,7 -158,7 +159,7 @@@ static int nfs_map_string_to_numeric(co
                return 0;
        memcpy(buf, name, namelen);
        buf[namelen] = '\0';
 -      if (strict_strtoul(buf, 0, &val) != 0)
 +      if (kstrtoul(buf, 0, &val) != 0)
                return 0;
        *res = val;
        return 1;
@@@ -193,19 -192,15 +193,15 @@@ static int nfs_idmap_init_keyring(void
        if (!cred)
                return -ENOMEM;
  
-       keyring = key_alloc(&key_type_keyring, ".id_resolver", 0, 0, cred,
-                            (KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                            KEY_USR_VIEW | KEY_USR_READ,
-                            KEY_ALLOC_NOT_IN_QUOTA);
+       keyring = keyring_alloc(".id_resolver", 0, 0, cred,
+                               (KEY_POS_ALL & ~KEY_POS_SETATTR) |
+                               KEY_USR_VIEW | KEY_USR_READ,
+                               KEY_ALLOC_NOT_IN_QUOTA, NULL);
        if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
                goto failed_put_cred;
        }
  
-       ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
-       if (ret < 0)
-               goto failed_put_key;
        ret = register_key_type(&key_type_id_resolver);
        if (ret < 0)
                goto failed_put_key;
@@@ -331,6 -326,7 +327,6 @@@ static ssize_t nfs_idmap_get_key(const 
                ret = nfs_idmap_request_key(&key_type_id_resolver_legacy,
                                            name, namelen, type, data,
                                            data_size, idmap);
 -              idmap->idmap_key_cons = NULL;
                mutex_unlock(&idmap->idmap_mutex);
        }
        return ret;
@@@ -364,7 -360,7 +360,7 @@@ static int nfs_idmap_lookup_id(const ch
        if (data_size <= 0) {
                ret = -EINVAL;
        } else {
 -              ret = strict_strtol(id_str, 10, &id_long);
 +              ret = kstrtol(id_str, 10, &id_long);
                *id = (__u32)id_long;
        }
        return ret;
@@@ -465,6 -461,8 +461,6 @@@ nfs_idmap_new(struct nfs_client *clp
        struct rpc_pipe *pipe;
        int error;
  
 -      BUG_ON(clp->cl_idmap != NULL);
 -
        idmap = kzalloc(sizeof(*idmap), GFP_KERNEL);
        if (idmap == NULL)
                return -ENOMEM;
@@@ -508,6 -506,7 +504,6 @@@ static int __rpc_pipefs_event(struct nf
  
        switch (event) {
        case RPC_PIPEFS_MOUNT:
 -              BUG_ON(clp->cl_rpcclient->cl_dentry == NULL);
                err = __nfs_idmap_register(clp->cl_rpcclient->cl_dentry,
                                                clp->cl_idmap,
                                                clp->cl_idmap->idmap_pipe);
@@@ -629,6 -628,9 +625,6 @@@ static int nfs_idmap_prepare_message(ch
        substring_t substr;
        int token, ret;
  
 -      memset(im,  0, sizeof(*im));
 -      memset(msg, 0, sizeof(*msg));
 -
        im->im_type = IDMAP_TYPE_GROUP;
        token = match_token(desc, nfs_idmap_tokens, &substr);
  
@@@ -659,35 -661,6 +655,35 @@@ out
        return ret;
  }
  
 +static bool
 +nfs_idmap_prepare_pipe_upcall(struct idmap *idmap,
 +              struct idmap_legacy_upcalldata *data)
 +{
 +      if (idmap->idmap_upcall_data != NULL) {
 +              WARN_ON_ONCE(1);
 +              return false;
 +      }
 +      idmap->idmap_upcall_data = data;
 +      return true;
 +}
 +
 +static void
 +nfs_idmap_complete_pipe_upcall_locked(struct idmap *idmap, int ret)
 +{
 +      struct key_construction *cons = idmap->idmap_upcall_data->key_cons;
 +
 +      kfree(idmap->idmap_upcall_data);
 +      idmap->idmap_upcall_data = NULL;
 +      complete_request_key(cons, ret);
 +}
 +
 +static void
 +nfs_idmap_abort_pipe_upcall(struct idmap *idmap, int ret)
 +{
 +      if (idmap->idmap_upcall_data != NULL)
 +              nfs_idmap_complete_pipe_upcall_locked(idmap, ret);
 +}
 +
  static int nfs_idmap_legacy_upcall(struct key_construction *cons,
                                   const char *op,
                                   void *aux)
        int ret = -ENOMEM;
  
        /* msg and im are freed in idmap_pipe_destroy_msg */
 -      data = kmalloc(sizeof(*data), GFP_KERNEL);
 +      data = kzalloc(sizeof(*data), GFP_KERNEL);
        if (!data)
                goto out1;
  
        msg = &data->pipe_msg;
        im = &data->idmap_msg;
        data->idmap = idmap;
 +      data->key_cons = cons;
  
        ret = nfs_idmap_prepare_message(key->description, idmap, im, msg);
        if (ret < 0)
                goto out2;
  
 -      BUG_ON(idmap->idmap_key_cons != NULL);
 -      idmap->idmap_key_cons = cons;
 +      ret = -EAGAIN;
 +      if (!nfs_idmap_prepare_pipe_upcall(idmap, data))
 +              goto out2;
  
        ret = rpc_queue_upcall(idmap->idmap_pipe, msg);
        if (ret < 0)
 -              goto out3;
 +              nfs_idmap_abort_pipe_upcall(idmap, ret);
  
        return ret;
 -
 -out3:
 -      idmap->idmap_key_cons = NULL;
  out2:
        kfree(data);
  out1:
@@@ -736,32 -710,21 +732,32 @@@ static int nfs_idmap_instantiate(struc
                                        authkey);
  }
  
 -static int nfs_idmap_read_message(struct idmap_msg *im, struct key *key, struct key *authkey)
 +static int nfs_idmap_read_and_verify_message(struct idmap_msg *im,
 +              struct idmap_msg *upcall,
 +              struct key *key, struct key *authkey)
  {
        char id_str[NFS_UINT_MAXLEN];
 -      int ret = -EINVAL;
 +      int ret = -ENOKEY;
  
 +      /* ret = -ENOKEY */
 +      if (upcall->im_type != im->im_type || upcall->im_conv != im->im_conv)
 +              goto out;
        switch (im->im_conv) {
        case IDMAP_CONV_NAMETOID:
 +              if (strcmp(upcall->im_name, im->im_name) != 0)
 +                      break;
                sprintf(id_str, "%d", im->im_id);
                ret = nfs_idmap_instantiate(key, authkey, id_str);
                break;
        case IDMAP_CONV_IDTONAME:
 +              if (upcall->im_id != im->im_id)
 +                      break;
                ret = nfs_idmap_instantiate(key, authkey, im->im_name);
                break;
 +      default:
 +              ret = -EINVAL;
        }
 -
 +out:
        return ret;
  }
  
@@@ -773,16 -736,14 +769,16 @@@ idmap_pipe_downcall(struct file *filp, 
        struct key_construction *cons;
        struct idmap_msg im;
        size_t namelen_in;
 -      int ret;
 +      int ret = -ENOKEY;
  
        /* If instantiation is successful, anyone waiting for key construction
         * will have been woken up and someone else may now have used
         * idmap_key_cons - so after this point we may no longer touch it.
         */
 -      cons = ACCESS_ONCE(idmap->idmap_key_cons);
 -      idmap->idmap_key_cons = NULL;
 +      if (idmap->idmap_upcall_data == NULL)
 +              goto out_noupcall;
 +
 +      cons = idmap->idmap_upcall_data->key_cons;
  
        if (mlen != sizeof(im)) {
                ret = -ENOSPC;
        if (namelen_in == 0 || namelen_in == IDMAP_NAMESZ) {
                ret = -EINVAL;
                goto out;
 -      }
 +}
  
 -      ret = nfs_idmap_read_message(&im, cons->key, cons->authkey);
 +      ret = nfs_idmap_read_and_verify_message(&im,
 +                      &idmap->idmap_upcall_data->idmap_msg,
 +                      cons->key, cons->authkey);
        if (ret >= 0) {
                key_set_timeout(cons->key, nfs_idmap_cache_timeout);
                ret = mlen;
        }
  
  out:
 -      complete_request_key(cons, ret);
 +      nfs_idmap_complete_pipe_upcall_locked(idmap, ret);
 +out_noupcall:
        return ret;
  }
  
@@@ -826,9 -784,14 +822,9 @@@ idmap_pipe_destroy_msg(struct rpc_pipe_
                        struct idmap_legacy_upcalldata,
                        pipe_msg);
        struct idmap *idmap = data->idmap;
 -      struct key_construction *cons;
 -      if (msg->errno) {
 -              cons = ACCESS_ONCE(idmap->idmap_key_cons);
 -              idmap->idmap_key_cons = NULL;
 -              complete_request_key(cons, msg->errno);
 -      }
 -      /* Free memory allocated in nfs_idmap_legacy_upcall() */
 -      kfree(data);
 +
 +      if (msg->errno)
 +              nfs_idmap_abort_pipe_upcall(idmap, msg->errno);
  }
  
  static void
@@@ -836,8 -799,7 +832,8 @@@ idmap_release_pipe(struct inode *inode
  {
        struct rpc_inode *rpci = RPC_I(inode);
        struct idmap *idmap = (struct idmap *)rpci->private;
 -      idmap->idmap_key_cons = NULL;
 +
 +      nfs_idmap_abort_pipe_upcall(idmap, -EPIPE);
  }
  
  int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid)
diff --combined include/linux/key.h
index 2393b1c040b695d69d3a2f700300d3b4cec296c5,890699815212009a4ba7bf08cdb86f7128477408..4dfde1161c5e7878565d05ad7f5293e9e4d19cef
@@@ -24,7 -24,6 +24,7 @@@
  #include <linux/atomic.h>
  
  #ifdef __KERNEL__
 +#include <linux/uidgid.h>
  
  /* key handle serial number */
  typedef int32_t key_serial_t;
@@@ -138,8 -137,8 +138,8 @@@ struct key 
                time_t          revoked_at;     /* time at which key was revoked */
        };
        time_t                  last_used_at;   /* last time used for LRU keyring discard */
 -      uid_t                   uid;
 -      gid_t                   gid;
 +      kuid_t                  uid;
 +      kgid_t                  gid;
        key_perm_t              perm;           /* access permissions */
        unsigned short          quotalen;       /* length added to quota */
        unsigned short          datalen;        /* payload data length
  
  extern struct key *key_alloc(struct key_type *type,
                             const char *desc,
 -                           uid_t uid, gid_t gid,
 +                           kuid_t uid, kgid_t gid,
                             const struct cred *cred,
                             key_perm_t perm,
                             unsigned long flags);
@@@ -263,8 -262,9 +263,9 @@@ extern int key_link(struct key *keyring
  extern int key_unlink(struct key *keyring,
                      struct key *key);
  
 -extern struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
 +extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
                                 const struct cred *cred,
+                                key_perm_t perm,
                                 unsigned long flags,
                                 struct key *dest);
  
diff --combined kernel/cred.c
index 48cea3da6d052c77bdfe3b5b8e766f0a3033c28c,3f7ad1ec2ae43b20c8e952feab4003d9b7b50535..8888afb846e95f36f5fa4ab27b82685785083bd8
  
  static struct kmem_cache *cred_jar;
  
- /*
-  * The common credentials for the initial task's thread group
-  */
- #ifdef CONFIG_KEYS
- static struct thread_group_cred init_tgcred = {
-       .usage  = ATOMIC_INIT(2),
-       .tgid   = 0,
-       .lock   = __SPIN_LOCK_UNLOCKED(init_cred.tgcred.lock),
- };
- #endif
  /*
   * The initial credentials for the initial task
   */
@@@ -65,9 -54,6 +54,6 @@@ struct cred init_cred = 
        .user                   = INIT_USER,
        .user_ns                = &init_user_ns,
        .group_info             = &init_groups,
- #ifdef CONFIG_KEYS
-       .tgcred                 = &init_tgcred,
- #endif
  };
  
  static inline void set_cred_subscribers(struct cred *cred, int n)
@@@ -95,36 -81,6 +81,6 @@@ static inline void alter_cred_subscribe
  #endif
  }
  
- /*
-  * Dispose of the shared task group credentials
-  */
- #ifdef CONFIG_KEYS
- static void release_tgcred_rcu(struct rcu_head *rcu)
- {
-       struct thread_group_cred *tgcred =
-               container_of(rcu, struct thread_group_cred, rcu);
-       BUG_ON(atomic_read(&tgcred->usage) != 0);
-       key_put(tgcred->session_keyring);
-       key_put(tgcred->process_keyring);
-       kfree(tgcred);
- }
- #endif
- /*
-  * Release a set of thread group credentials.
-  */
- static void release_tgcred(struct cred *cred)
- {
- #ifdef CONFIG_KEYS
-       struct thread_group_cred *tgcred = cred->tgcred;
-       if (atomic_dec_and_test(&tgcred->usage))
-               call_rcu(&tgcred->rcu, release_tgcred_rcu);
- #endif
- }
  /*
   * The RCU callback to actually dispose of a set of credentials
   */
@@@ -150,9 -106,10 +106,10 @@@ static void put_cred_rcu(struct rcu_hea
  #endif
  
        security_cred_free(cred);
+       key_put(cred->session_keyring);
+       key_put(cred->process_keyring);
        key_put(cred->thread_keyring);
        key_put(cred->request_key_auth);
-       release_tgcred(cred);
        if (cred->group_info)
                put_group_info(cred->group_info);
        free_uid(cred->user);
@@@ -246,15 -203,6 +203,6 @@@ struct cred *cred_alloc_blank(void
        if (!new)
                return NULL;
  
- #ifdef CONFIG_KEYS
-       new->tgcred = kzalloc(sizeof(*new->tgcred), GFP_KERNEL);
-       if (!new->tgcred) {
-               kmem_cache_free(cred_jar, new);
-               return NULL;
-       }
-       atomic_set(&new->tgcred->usage, 1);
- #endif
        atomic_set(&new->usage, 1);
  #ifdef CONFIG_DEBUG_CREDENTIALS
        new->magic = CRED_MAGIC;
@@@ -308,9 -256,10 +256,10 @@@ struct cred *prepare_creds(void
        get_user_ns(new->user_ns);
  
  #ifdef CONFIG_KEYS
+       key_get(new->session_keyring);
+       key_get(new->process_keyring);
        key_get(new->thread_keyring);
        key_get(new->request_key_auth);
-       atomic_inc(&new->tgcred->usage);
  #endif
  
  #ifdef CONFIG_SECURITY
@@@ -334,39 -283,20 +283,20 @@@ EXPORT_SYMBOL(prepare_creds)
   */
  struct cred *prepare_exec_creds(void)
  {
-       struct thread_group_cred *tgcred = NULL;
        struct cred *new;
  
- #ifdef CONFIG_KEYS
-       tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
-       if (!tgcred)
-               return NULL;
- #endif
        new = prepare_creds();
-       if (!new) {
-               kfree(tgcred);
+       if (!new)
                return new;
-       }
  
  #ifdef CONFIG_KEYS
        /* newly exec'd tasks don't get a thread keyring */
        key_put(new->thread_keyring);
        new->thread_keyring = NULL;
  
-       /* create a new per-thread-group creds for all this set of threads to
-        * share */
-       memcpy(tgcred, new->tgcred, sizeof(struct thread_group_cred));
-       atomic_set(&tgcred->usage, 1);
-       spin_lock_init(&tgcred->lock);
        /* inherit the session keyring; new process keyring */
-       key_get(tgcred->session_keyring);
-       tgcred->process_keyring = NULL;
-       release_tgcred(new);
-       new->tgcred = tgcred;
+       key_put(new->process_keyring);
+       new->process_keyring = NULL;
  #endif
  
        return new;
   */
  int copy_creds(struct task_struct *p, unsigned long clone_flags)
  {
- #ifdef CONFIG_KEYS
-       struct thread_group_cred *tgcred;
- #endif
        struct cred *new;
        int ret;
  
                        install_thread_keyring_to_cred(new);
        }
  
-       /* we share the process and session keyrings between all the threads in
-        * a process - this is slightly icky as we violate COW credentials a
-        * bit */
+       /* The process keyring is only shared between the threads in a process;
+        * anything outside of those threads doesn't inherit.
+        */
        if (!(clone_flags & CLONE_THREAD)) {
-               tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
-               if (!tgcred) {
-                       ret = -ENOMEM;
-                       goto error_put;
-               }
-               atomic_set(&tgcred->usage, 1);
-               spin_lock_init(&tgcred->lock);
-               tgcred->process_keyring = NULL;
-               tgcred->session_keyring = key_get(new->tgcred->session_keyring);
-               release_tgcred(new);
-               new->tgcred = tgcred;
+               key_put(new->process_keyring);
+               new->process_keyring = NULL;
        }
  #endif
  
@@@ -643,9 -560,6 +560,6 @@@ void __init cred_init(void
   */
  struct cred *prepare_kernel_cred(struct task_struct *daemon)
  {
- #ifdef CONFIG_KEYS
-       struct thread_group_cred *tgcred;
- #endif
        const struct cred *old;
        struct cred *new;
  
        if (!new)
                return NULL;
  
- #ifdef CONFIG_KEYS
-       tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL);
-       if (!tgcred) {
-               kmem_cache_free(cred_jar, new);
-               return NULL;
-       }
- #endif
        kdebug("prepare_kernel_cred() alloc %p", new);
  
        if (daemon)
        get_group_info(new->group_info);
  
  #ifdef CONFIG_KEYS
-       atomic_set(&tgcred->usage, 1);
-       spin_lock_init(&tgcred->lock);
-       tgcred->process_keyring = NULL;
-       tgcred->session_keyring = NULL;
-       new->tgcred = tgcred;
-       new->request_key_auth = NULL;
+       new->session_keyring = NULL;
+       new->process_keyring = NULL;
        new->thread_keyring = NULL;
+       new->request_key_auth = NULL;
        new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
  #endif
  
@@@ -799,15 -702,9 +702,15 @@@ static void dump_invalid_creds(const st
               atomic_read(&cred->usage),
               read_cred_subscribers(cred));
        printk(KERN_ERR "CRED: ->*uid = { %d,%d,%d,%d }\n",
 -             cred->uid, cred->euid, cred->suid, cred->fsuid);
 +              from_kuid_munged(&init_user_ns, cred->uid),
 +              from_kuid_munged(&init_user_ns, cred->euid),
 +              from_kuid_munged(&init_user_ns, cred->suid),
 +              from_kuid_munged(&init_user_ns, cred->fsuid));
        printk(KERN_ERR "CRED: ->*gid = { %d,%d,%d,%d }\n",
 -             cred->gid, cred->egid, cred->sgid, cred->fsgid);
 +              from_kgid_munged(&init_user_ns, cred->gid),
 +              from_kgid_munged(&init_user_ns, cred->egid),
 +              from_kgid_munged(&init_user_ns, cred->sgid),
 +              from_kgid_munged(&init_user_ns, cred->fsgid));
  #ifdef CONFIG_SECURITY
        printk(KERN_ERR "CRED: ->security is %p\n", cred->security);
        if ((unsigned long) cred->security >= PAGE_SIZE &&
index 8aa4b1115384e3018b461ca153bd932a511c3fef,b53bb4a41daa6cf0b05dedfb5df9c1979af46bf8..0a69d075779556fa1f8093b2ba6816e7f7d5cb77
@@@ -259,20 -259,15 +259,16 @@@ static int __init init_dns_resolver(voi
        if (!cred)
                return -ENOMEM;
  
-       keyring = key_alloc(&key_type_keyring, ".dns_resolver",
-                           GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
-                           (KEY_POS_ALL & ~KEY_POS_SETATTR) |
-                           KEY_USR_VIEW | KEY_USR_READ,
-                           KEY_ALLOC_NOT_IN_QUOTA);
 -      keyring = keyring_alloc(".dns_resolver", 0, 0, cred,
++      keyring = keyring_alloc(".dns_resolver",
++                              GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred,
+                               (KEY_POS_ALL & ~KEY_POS_SETATTR) |
+                               KEY_USR_VIEW | KEY_USR_READ,
+                               KEY_ALLOC_NOT_IN_QUOTA, NULL);
        if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
                goto failed_put_cred;
        }
  
-       ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
-       if (ret < 0)
-               goto failed_put_key;
        ret = register_key_type(&key_type_dns_resolver);
        if (ret < 0)
                goto failed_put_key;
@@@ -304,3 -299,4 +300,4 @@@ static void __exit exit_dns_resolver(vo
  module_init(init_dns_resolver)
  module_exit(exit_dns_resolver)
  MODULE_LICENSE("GPL");
diff --combined security/keys/key.c
index a15c9da8f971832edb41a455b72f738fac4e9cb8,da63a659db75866694e6b5909668ea6e06a6c360..8fb7c7bd465769cb5dca49e6d6f1ad011c75de63
@@@ -18,6 -18,7 +18,6 @@@
  #include <linux/workqueue.h>
  #include <linux/random.h>
  #include <linux/err.h>
 -#include <linux/user_namespace.h>
  #include "internal.h"
  
  struct kmem_cache *key_jar;
@@@ -51,7 -52,7 +51,7 @@@ void __key_check(const struct key *key
   * Get the key quota record for a user, allocating a new record if one doesn't
   * already exist.
   */
 -struct key_user *key_user_lookup(uid_t uid, struct user_namespace *user_ns)
 +struct key_user *key_user_lookup(kuid_t uid)
  {
        struct key_user *candidate = NULL, *user;
        struct rb_node *parent = NULL;
@@@ -66,9 -67,13 +66,9 @@@ try_again
                parent = *p;
                user = rb_entry(parent, struct key_user, node);
  
 -              if (uid < user->uid)
 +              if (uid_lt(uid, user->uid))
                        p = &(*p)->rb_left;
 -              else if (uid > user->uid)
 -                      p = &(*p)->rb_right;
 -              else if (user_ns < user->user_ns)
 -                      p = &(*p)->rb_left;
 -              else if (user_ns > user->user_ns)
 +              else if (uid_gt(uid, user->uid))
                        p = &(*p)->rb_right;
                else
                        goto found;
        atomic_set(&candidate->nkeys, 0);
        atomic_set(&candidate->nikeys, 0);
        candidate->uid = uid;
 -      candidate->user_ns = get_user_ns(user_ns);
        candidate->qnkeys = 0;
        candidate->qnbytes = 0;
        spin_lock_init(&candidate->lock);
@@@ -125,6 -131,7 +125,6 @@@ void key_user_put(struct key_user *user
        if (atomic_dec_and_lock(&user->usage, &key_user_lock)) {
                rb_erase(&user->node, &key_user_tree);
                spin_unlock(&key_user_lock);
 -              put_user_ns(user->user_ns);
  
                kfree(user);
        }
@@@ -222,7 -229,7 +222,7 @@@ serial_exists
   * key_alloc() calls don't race with module unloading.
   */
  struct key *key_alloc(struct key_type *type, const char *desc,
 -                    uid_t uid, gid_t gid, const struct cred *cred,
 +                    kuid_t uid, kgid_t gid, const struct cred *cred,
                      key_perm_t perm, unsigned long flags)
  {
        struct key_user *user = NULL;
        quotalen = desclen + type->def_datalen;
  
        /* get hold of the key tracking for this user */
 -      user = key_user_lookup(uid, cred->user_ns);
 +      user = key_user_lookup(uid);
        if (!user)
                goto no_memory_1;
  
        /* check that the user's quota permits allocation of another key and
         * its description */
        if (!(flags & KEY_ALLOC_NOT_IN_QUOTA)) {
 -              unsigned maxkeys = (uid == 0) ?
 +              unsigned maxkeys = uid_eq(uid, GLOBAL_ROOT_UID) ?
                        key_quota_root_maxkeys : key_quota_maxkeys;
 -              unsigned maxbytes = (uid == 0) ?
 +              unsigned maxbytes = uid_eq(uid, GLOBAL_ROOT_UID) ?
                        key_quota_root_maxbytes : key_quota_maxbytes;
  
                spin_lock(&user->lock);
@@@ -373,7 -380,7 +373,7 @@@ int key_payload_reserve(struct key *key
  
        /* contemplate the quota adjustment */
        if (delta != 0 && test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
 -              unsigned maxbytes = (key->user->uid == 0) ?
 +              unsigned maxbytes = uid_eq(key->user->uid, GLOBAL_ROOT_UID) ?
                        key_quota_root_maxbytes : key_quota_maxbytes;
  
                spin_lock(&key->user->lock);
@@@ -605,7 -612,7 +605,7 @@@ void key_put(struct key *key
                key_check(key);
  
                if (atomic_dec_and_test(&key->usage))
 -                      queue_work(system_nrt_wq, &key_gc_work);
 +                      schedule_work(&key_gc_work);
        }
  }
  EXPORT_SYMBOL(key_put);
@@@ -854,13 -861,13 +854,13 @@@ key_ref_t key_create_or_update(key_ref_
        /* if the client doesn't provide, decide on the permissions we want */
        if (perm == KEY_PERM_UNDEF) {
                perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
-               perm |= KEY_USR_VIEW | KEY_USR_SEARCH | KEY_USR_LINK | KEY_USR_SETATTR;
+               perm |= KEY_USR_VIEW;
  
                if (ktype->read)
-                       perm |= KEY_POS_READ | KEY_USR_READ;
+                       perm |= KEY_POS_READ;
  
                if (ktype == &key_type_keyring || ktype->update)
-                       perm |= KEY_USR_WRITE;
+                       perm |= KEY_POS_WRITE;
        }
  
        /* allocate a new key */
diff --combined security/keys/keyctl.c
index 5d34b4e827d6349a46f6b892573e129e500e49db,6d9d0c747525fe3dee3458d2f20c235d5126d623..4b5c948eb41426c76ef1810239cb0a98f9c4f918
@@@ -579,8 -579,8 +579,8 @@@ okay
        ret = snprintf(tmpbuf, PAGE_SIZE - 1,
                       "%s;%d;%d;%08x;%s",
                       key->type->name,
 -                     key->uid,
 -                     key->gid,
 +                     from_kuid_munged(current_user_ns(), key->uid),
 +                     from_kgid_munged(current_user_ns(), key->gid),
                       key->perm,
                       key->description ?: "");
  
@@@ -776,25 -776,15 +776,25 @@@ error
   *
   * If successful, 0 will be returned.
   */
 -long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
 +long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group)
  {
        struct key_user *newowner, *zapowner = NULL;
        struct key *key;
        key_ref_t key_ref;
        long ret;
 +      kuid_t uid;
 +      kgid_t gid;
 +
 +      uid = make_kuid(current_user_ns(), user);
 +      gid = make_kgid(current_user_ns(), group);
 +      ret = -EINVAL;
 +      if ((user != (uid_t) -1) && !uid_valid(uid))
 +              goto error;
 +      if ((group != (gid_t) -1) && !gid_valid(gid))
 +              goto error;
  
        ret = 0;
 -      if (uid == (uid_t) -1 && gid == (gid_t) -1)
 +      if (user == (uid_t) -1 && group == (gid_t) -1)
                goto error;
  
        key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
  
        if (!capable(CAP_SYS_ADMIN)) {
                /* only the sysadmin can chown a key to some other UID */
 -              if (uid != (uid_t) -1 && key->uid != uid)
 +              if (user != (uid_t) -1 && !uid_eq(key->uid, uid))
                        goto error_put;
  
                /* only the sysadmin can set the key's GID to a group other
                 * than one of those that the current process subscribes to */
 -              if (gid != (gid_t) -1 && gid != key->gid && !in_group_p(gid))
 +              if (group != (gid_t) -1 && !gid_eq(gid, key->gid) && !in_group_p(gid))
                        goto error_put;
        }
  
        /* change the UID */
 -      if (uid != (uid_t) -1 && uid != key->uid) {
 +      if (user != (uid_t) -1 && !uid_eq(uid, key->uid)) {
                ret = -ENOMEM;
 -              newowner = key_user_lookup(uid, current_user_ns());
 +              newowner = key_user_lookup(uid);
                if (!newowner)
                        goto error_put;
  
                /* transfer the quota burden to the new user */
                if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
 -                      unsigned maxkeys = (uid == 0) ?
 +                      unsigned maxkeys = uid_eq(uid, GLOBAL_ROOT_UID) ?
                                key_quota_root_maxkeys : key_quota_maxkeys;
 -                      unsigned maxbytes = (uid == 0) ?
 +                      unsigned maxbytes = uid_eq(uid, GLOBAL_ROOT_UID) ?
                                key_quota_root_maxbytes : key_quota_maxbytes;
  
                        spin_lock(&newowner->lock);
        }
  
        /* change the GID */
 -      if (gid != (gid_t) -1)
 +      if (group != (gid_t) -1)
                key->gid = gid;
  
        ret = 0;
@@@ -917,7 -907,7 +917,7 @@@ long keyctl_setperm_key(key_serial_t id
        down_write(&key->sem);
  
        /* if we're not the sysadmin, we can only change a key that we own */
 -      if (capable(CAP_SYS_ADMIN) || key->uid == current_fsuid()) {
 +      if (capable(CAP_SYS_ADMIN) || uid_eq(key->uid, current_fsuid())) {
                key->perm = perm;
                ret = 0;
        }
@@@ -1132,12 -1122,12 +1132,12 @@@ long keyctl_instantiate_key_iov(key_ser
        ret = rw_copy_check_uvector(WRITE, _payload_iov, ioc,
                                    ARRAY_SIZE(iovstack), iovstack, &iov);
        if (ret < 0)
-               return ret;
+               goto err;
        if (ret == 0)
                goto no_payload_free;
  
        ret = keyctl_instantiate_key_common(id, iov, ioc, ret, ringid);
+ err:
        if (iov != iovstack)
                kfree(iov);
        return ret;
@@@ -1495,7 -1485,8 +1495,8 @@@ long keyctl_session_to_parent(void
                goto error_keyring;
        newwork = &cred->rcu;
  
-       cred->tgcred->session_keyring = key_ref_to_ptr(keyring_r);
+       cred->session_keyring = key_ref_to_ptr(keyring_r);
+       keyring_r = NULL;
        init_task_work(newwork, key_change_session_keyring);
  
        me = current;
        oldwork = NULL;
        parent = me->real_parent;
  
 -      task_lock(parent);
        /* the parent mustn't be init and mustn't be a kernel thread */
        if (parent->pid <= 1 || !parent->mm)
                goto unlock;
        mycred = current_cred();
        pcred = __task_cred(parent);
        if (mycred == pcred ||
-           mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) {
+           mycred->session_keyring == pcred->session_keyring) {
                ret = 0;
                goto unlock;
        }
  
        /* the parent must have the same effective ownership and mustn't be
         * SUID/SGID */
 -      if (pcred->uid  != mycred->euid ||
 -          pcred->euid != mycred->euid ||
 -          pcred->suid != mycred->euid ||
 -          pcred->gid  != mycred->egid ||
 -          pcred->egid != mycred->egid ||
 -          pcred->sgid != mycred->egid)
 +      if (!uid_eq(pcred->uid,  mycred->euid) ||
 +          !uid_eq(pcred->euid, mycred->euid) ||
 +          !uid_eq(pcred->suid, mycred->euid) ||
 +          !gid_eq(pcred->gid,  mycred->egid) ||
 +          !gid_eq(pcred->egid, mycred->egid) ||
 +          !gid_eq(pcred->sgid, mycred->egid))
                goto unlock;
  
        /* the keyrings must have the same UID */
-       if ((pcred->tgcred->session_keyring &&
-            !uid_eq(pcred->tgcred->session_keyring->uid, mycred->euid)) ||
-           !uid_eq(mycred->tgcred->session_keyring->uid, mycred->euid))
+       if ((pcred->session_keyring &&
 -           pcred->session_keyring->uid != mycred->euid) ||
 -          mycred->session_keyring->uid != mycred->euid)
++           !uid_eq(pcred->session_keyring->uid, mycred->euid)) ||
++          !uid_eq(mycred->session_keyring->uid, mycred->euid))
                goto unlock;
  
        /* cancel an already pending keyring replacement */
        if (!ret)
                newwork = NULL;
  unlock:
 -      task_unlock(parent);
        write_unlock_irq(&tasklist_lock);
        rcu_read_unlock();
        if (oldwork)
diff --combined security/keys/keyring.c
index 6e42df15a24c3b3509455d8afedaed3dccf66662,9270ba054a1ea57096e7bce87e766704377bf825..6ece7f2e5707f45c2736ca4a05504c2dd391ea00
@@@ -256,18 -256,15 +256,15 @@@ error
  /*
   * Allocate a keyring and link into the destination keyring.
   */
 -struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid,
 +struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid,
-                         const struct cred *cred, unsigned long flags,
-                         struct key *dest)
+                         const struct cred *cred, key_perm_t perm,
+                         unsigned long flags, struct key *dest)
  {
        struct key *keyring;
        int ret;
  
        keyring = key_alloc(&key_type_keyring, description,
-                           uid, gid, cred,
-                           (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL,
-                           flags);
+                           uid, gid, cred, perm, flags);
        if (!IS_ERR(keyring)) {
                ret = key_instantiate_and_link(keyring, NULL, 0, dest, NULL);
                if (ret < 0) {
  
        return keyring;
  }
+ EXPORT_SYMBOL(keyring_alloc);
  
  /**
   * keyring_search_aux - Search a keyring tree for a key matching some criteria
@@@ -612,7 -610,7 +610,7 @@@ struct key *find_keyring_by_name(const 
                                    &keyring_name_hash[bucket],
                                    type_data.link
                                    ) {
 -                      if (keyring->user->user_ns != current_user_ns())
 +                      if (!kuid_has_mapping(current_user_ns(), keyring->user->uid))
                                continue;
  
                        if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
index 86468f385fc8ef1ac0422afb117114f8d81b6ba4,b58d93892740476ed115568167d3dac8a5b2db34..58dfe089094793030f56fdb895f6621e403e4157
@@@ -34,7 -34,8 +34,7 @@@ struct key_user root_key_user = 
        .lock           = __SPIN_LOCK_UNLOCKED(root_key_user.lock),
        .nkeys          = ATOMIC_INIT(2),
        .nikeys         = ATOMIC_INIT(2),
 -      .uid            = 0,
 -      .user_ns        = &init_user_ns,
 +      .uid            = GLOBAL_ROOT_UID,
  };
  
  /*
@@@ -45,15 -46,15 +45,17 @@@ int install_user_keyrings(void
        struct user_struct *user;
        const struct cred *cred;
        struct key *uid_keyring, *session_keyring;
+       key_perm_t user_keyring_perm;
        char buf[20];
        int ret;
 +      uid_t uid;
  
+       user_keyring_perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL;
        cred = current_cred();
        user = cred->user;
 +      uid = from_kuid(cred->user_ns, user->uid);
  
 -      kenter("%p{%u}", user, user->uid);
 +      kenter("%p{%u}", user, uid);
  
        if (user->uid_keyring) {
                kleave(" = 0 [exist]");
                 * - there may be one in existence already as it may have been
                 *   pinned by a session, but the user_struct pointing to it
                 *   may have been destroyed by setuid */
 -              sprintf(buf, "_uid.%u", user->uid);
 +              sprintf(buf, "_uid.%u", uid);
  
                uid_keyring = find_keyring_by_name(buf, true);
                if (IS_ERR(uid_keyring)) {
 -                      uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1,
 +                      uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID,
-                                                   cred, KEY_ALLOC_IN_QUOTA,
-                                                   NULL);
+                                                   cred, user_keyring_perm,
+                                                   KEY_ALLOC_IN_QUOTA, NULL);
                        if (IS_ERR(uid_keyring)) {
                                ret = PTR_ERR(uid_keyring);
                                goto error;
  
                /* get a default session keyring (which might also exist
                 * already) */
 -              sprintf(buf, "_uid_ses.%u", user->uid);
 +              sprintf(buf, "_uid_ses.%u", uid);
  
                session_keyring = find_keyring_by_name(buf, true);
                if (IS_ERR(session_keyring)) {
                        session_keyring =
 -                              keyring_alloc(buf, user->uid, (gid_t) -1,
 +                              keyring_alloc(buf, user->uid, INVALID_GID,
-                                             cred, KEY_ALLOC_IN_QUOTA, NULL);
+                                             cred, user_keyring_perm,
+                                             KEY_ALLOC_IN_QUOTA, NULL);
                        if (IS_ERR(session_keyring)) {
                                ret = PTR_ERR(session_keyring);
                                goto error_release;
@@@ -130,6 -132,7 +133,7 @@@ int install_thread_keyring_to_cred(stru
        struct key *keyring;
  
        keyring = keyring_alloc("_tid", new->uid, new->gid, new,
+                               KEY_POS_ALL | KEY_USR_VIEW,
                                KEY_ALLOC_QUOTA_OVERRUN, NULL);
        if (IS_ERR(keyring))
                return PTR_ERR(keyring);
@@@ -170,27 -173,18 +174,18 @@@ static int install_thread_keyring(void
  int install_process_keyring_to_cred(struct cred *new)
  {
        struct key *keyring;
-       int ret;
  
-       if (new->tgcred->process_keyring)
+       if (new->process_keyring)
                return -EEXIST;
  
-       keyring = keyring_alloc("_pid", new->uid, new->gid,
-                               new, KEY_ALLOC_QUOTA_OVERRUN, NULL);
+       keyring = keyring_alloc("_pid", new->uid, new->gid, new,
+                               KEY_POS_ALL | KEY_USR_VIEW,
+                               KEY_ALLOC_QUOTA_OVERRUN, NULL);
        if (IS_ERR(keyring))
                return PTR_ERR(keyring);
  
-       spin_lock_irq(&new->tgcred->lock);
-       if (!new->tgcred->process_keyring) {
-               new->tgcred->process_keyring = keyring;
-               keyring = NULL;
-               ret = 0;
-       } else {
-               ret = -EEXIST;
-       }
-       spin_unlock_irq(&new->tgcred->lock);
-       key_put(keyring);
-       return ret;
+       new->process_keyring = keyring;
+       return 0;
  }
  
  /*
@@@ -231,11 -225,12 +226,12 @@@ int install_session_keyring_to_cred(str
        /* create an empty session keyring */
        if (!keyring) {
                flags = KEY_ALLOC_QUOTA_OVERRUN;
-               if (cred->tgcred->session_keyring)
+               if (cred->session_keyring)
                        flags = KEY_ALLOC_IN_QUOTA;
  
-               keyring = keyring_alloc("_ses", cred->uid, cred->gid,
-                                       cred, flags, NULL);
+               keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred,
+                                       KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
+                                       flags, NULL);
                if (IS_ERR(keyring))
                        return PTR_ERR(keyring);
        } else {
        }
  
        /* install the keyring */
-       spin_lock_irq(&cred->tgcred->lock);
-       old = cred->tgcred->session_keyring;
-       rcu_assign_pointer(cred->tgcred->session_keyring, keyring);
-       spin_unlock_irq(&cred->tgcred->lock);
-       /* we're using RCU on the pointer, but there's no point synchronising
-        * on it if it didn't previously point to anything */
-       if (old) {
-               synchronize_rcu();
+       old = cred->session_keyring;
+       rcu_assign_pointer(cred->session_keyring, keyring);
+       if (old)
                key_put(old);
-       }
  
        return 0;
  }
@@@ -368,9 -357,9 +358,9 @@@ key_ref_t search_my_process_keyrings(st
        }
  
        /* search the process keyring second */
-       if (cred->tgcred->process_keyring) {
+       if (cred->process_keyring) {
                key_ref = keyring_search_aux(
-                       make_key_ref(cred->tgcred->process_keyring, 1),
+                       make_key_ref(cred->process_keyring, 1),
                        cred, type, description, match, no_state_check);
                if (!IS_ERR(key_ref))
                        goto found;
        }
  
        /* search the session keyring */
-       if (cred->tgcred->session_keyring) {
+       if (cred->session_keyring) {
                rcu_read_lock();
                key_ref = keyring_search_aux(
-                       make_key_ref(rcu_dereference(
-                                            cred->tgcred->session_keyring),
-                                    1),
+                       make_key_ref(rcu_dereference(cred->session_keyring), 1),
                        cred, type, description, match, no_state_check);
                rcu_read_unlock();
  
@@@ -564,7 -551,7 +552,7 @@@ try_again
                break;
  
        case KEY_SPEC_PROCESS_KEYRING:
-               if (!cred->tgcred->process_keyring) {
+               if (!cred->process_keyring) {
                        if (!(lflags & KEY_LOOKUP_CREATE))
                                goto error;
  
                        goto reget_creds;
                }
  
-               key = cred->tgcred->process_keyring;
+               key = cred->process_keyring;
                atomic_inc(&key->usage);
                key_ref = make_key_ref(key, 1);
                break;
  
        case KEY_SPEC_SESSION_KEYRING:
-               if (!cred->tgcred->session_keyring) {
+               if (!cred->session_keyring) {
                        /* always install a session keyring upon access if one
                         * doesn't exist yet */
                        ret = install_user_keyrings();
                        if (ret < 0)
                                goto error;
                        goto reget_creds;
-               } else if (cred->tgcred->session_keyring ==
+               } else if (cred->session_keyring ==
                           cred->user->session_keyring &&
                           lflags & KEY_LOOKUP_CREATE) {
                        ret = join_session_keyring(NULL);
                }
  
                rcu_read_lock();
-               key = rcu_dereference(cred->tgcred->session_keyring);
+               key = rcu_dereference(cred->session_keyring);
                atomic_inc(&key->usage);
                rcu_read_unlock();
                key_ref = make_key_ref(key, 1);
@@@ -767,12 -754,6 +755,6 @@@ long join_session_keyring(const char *n
        struct key *keyring;
        long ret, serial;
  
-       /* only permit this if there's a single thread in the thread group -
-        * this avoids us having to adjust the creds on all threads and risking
-        * ENOMEM */
-       if (!current_is_single_threaded())
-               return -EMLINK;
        new = prepare_creds();
        if (!new)
                return -ENOMEM;
                if (ret < 0)
                        goto error;
  
-               serial = new->tgcred->session_keyring->serial;
+               serial = new->session_keyring->serial;
                ret = commit_creds(new);
                if (ret == 0)
                        ret = serial;
        keyring = find_keyring_by_name(name, false);
        if (PTR_ERR(keyring) == -ENOKEY) {
                /* not found - try and create a new one */
-               keyring = keyring_alloc(name, old->uid, old->gid, old,
-                                       KEY_ALLOC_IN_QUOTA, NULL);
+               keyring = keyring_alloc(
+                       name, old->uid, old->gid, old,
+                       KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK,
+                       KEY_ALLOC_IN_QUOTA, NULL);
                if (IS_ERR(keyring)) {
                        ret = PTR_ERR(keyring);
                        goto error2;
        } else if (IS_ERR(keyring)) {
                ret = PTR_ERR(keyring);
                goto error2;
+       } else if (keyring == new->session_keyring) {
+               ret = 0;
+               goto error2;
        }
  
        /* we've got a keyring - now to install it */
@@@ -863,8 -849,7 +850,7 @@@ void key_change_session_keyring(struct 
  
        new->jit_keyring        = old->jit_keyring;
        new->thread_keyring     = key_get(old->thread_keyring);
-       new->tgcred->tgid       = old->tgcred->tgid;
-       new->tgcred->process_keyring = key_get(old->tgcred->process_keyring);
+       new->process_keyring    = key_get(old->process_keyring);
  
        security_transfer_creds(new, old);
  
index 66e21184b559e2f0097fe73c970a7da17e2b951d,0ae3a2202771a0b58730b2aee01e4b1f573c2f75..4bd6bdb74193cba33b235b4b8b3934ddc576526c
@@@ -126,6 -126,7 +126,7 @@@ static int call_sbin_request_key(struc
  
        cred = get_current_cred();
        keyring = keyring_alloc(desc, cred->fsuid, cred->fsgid, cred,
+                               KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ,
                                KEY_ALLOC_QUOTA_OVERRUN, NULL);
        put_cred(cred);
        if (IS_ERR(keyring)) {
                goto error_link;
  
        /* record the UID and GID */
 -      sprintf(uid_str, "%d", cred->fsuid);
 -      sprintf(gid_str, "%d", cred->fsgid);
 +      sprintf(uid_str, "%d", from_kuid(&init_user_ns, cred->fsuid));
 +      sprintf(gid_str, "%d", from_kgid(&init_user_ns, cred->fsgid));
  
        /* we say which key is under construction */
        sprintf(key_str, "%d", key->serial);
                cred->thread_keyring ? cred->thread_keyring->serial : 0);
  
        prkey = 0;
-       if (cred->tgcred->process_keyring)
-               prkey = cred->tgcred->process_keyring->serial;
+       if (cred->process_keyring)
+               prkey = cred->process_keyring->serial;
        sprintf(keyring_str[1], "%d", prkey);
  
        rcu_read_lock();
-       session = rcu_dereference(cred->tgcred->session_keyring);
+       session = rcu_dereference(cred->session_keyring);
        if (!session)
                session = cred->user->session_keyring;
        sskey = session->serial;
@@@ -297,14 -298,14 +298,14 @@@ static void construct_get_dest_keyring(
                                break;
  
                case KEY_REQKEY_DEFL_PROCESS_KEYRING:
-                       dest_keyring = key_get(cred->tgcred->process_keyring);
+                       dest_keyring = key_get(cred->process_keyring);
                        if (dest_keyring)
                                break;
  
                case KEY_REQKEY_DEFL_SESSION_KEYRING:
                        rcu_read_lock();
                        dest_keyring = key_get(
-                               rcu_dereference(cred->tgcred->session_keyring));
+                               rcu_dereference(cred->session_keyring));
                        rcu_read_unlock();
  
                        if (dest_keyring)
@@@ -347,6 -348,7 +348,7 @@@ static int construct_alloc_key(struct k
        const struct cred *cred = current_cred();
        unsigned long prealloc;
        struct key *key;
+       key_perm_t perm;
        key_ref_t key_ref;
        int ret;
  
        *_key = NULL;
        mutex_lock(&user->cons_lock);
  
+       perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
+       perm |= KEY_USR_VIEW;
+       if (type->read)
+               perm |= KEY_POS_READ;
+       if (type == &key_type_keyring || type->update)
+               perm |= KEY_POS_WRITE;
        key = key_alloc(type, description, cred->fsuid, cred->fsgid, cred,
-                       KEY_POS_ALL, flags);
+                       perm, flags);
        if (IS_ERR(key))
                goto alloc_failed;
  
@@@ -442,7 -451,7 +451,7 @@@ static struct key *construct_key_and_li
  
        kenter("");
  
 -      user = key_user_lookup(current_fsuid(), current_user_ns());
 +      user = key_user_lookup(current_fsuid());
        if (!user)
                return ERR_PTR(-ENOMEM);