Merge tag 'keys-request-20190626' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 9 Jul 2019 02:19:37 +0000 (19:19 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 9 Jul 2019 02:19:37 +0000 (19:19 -0700)
Pull request_key improvements from David Howells:
 "These are all request_key()-related, including a fix and some improvements:

   - Fix the lack of a Link permission check on a key found by
     request_key(), thereby enabling request_key() to link keys that
     don't grant this permission to the target keyring (which must still
     grant Write permission).

     Note that the key must be in the caller's keyrings already to be
     found.

   - Invalidate used request_key authentication keys rather than
     revoking them, so that they get cleaned up immediately rather than
     hanging around till the expiry time is passed.

   - Move the RCU locks outwards from the keyring search functions so
     that a request_key_rcu() can be provided. This can be called in RCU
     mode, so it can't sleep and can't upcall - but it can be called
     from LOOKUP_RCU pathwalk mode.

   - Cache the latest positive result of request_key*() temporarily in
     task_struct so that filesystems that make a lot of request_key()
     calls during pathwalk can take advantage of it to avoid having to
     redo the searching. This requires CONFIG_KEYS_REQUEST_CACHE=y.

     It is assumed that the key just found is likely to be used multiple
     times in each step in an RCU pathwalk, and is likely to be reused
     for the next step too.

     Note that the cleanup of the cache is done on TIF_NOTIFY_RESUME,
     just before userspace resumes, and on exit"

* tag 'keys-request-20190626' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  keys: Kill off request_key_async{,_with_auxdata}
  keys: Cache result of request_key*() temporarily in task_struct
  keys: Provide request_key_rcu()
  keys: Move the RCU locks outwards from the keyring search functions
  keys: Invalidate used request_key authentication keys
  keys: Fix request_key() lack of Link perm check on found key

13 files changed:
1  2 
include/keys/request_key_auth-type.h
include/linux/key.h
include/linux/sched.h
include/linux/tracehook.h
kernel/cred.c
security/keys/Kconfig
security/keys/internal.h
security/keys/key.c
security/keys/keyring.c
security/keys/proc.c
security/keys/process_keys.c
security/keys/request_key.c
security/keys/request_key_auth.c

@@@ -1,8 -1,12 +1,8 @@@
 +/* SPDX-License-Identifier: GPL-2.0-or-later */
  /* request_key authorisation token key type
   *
   * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public Licence
 - * as published by the Free Software Foundation; either version
 - * 2 of the Licence, or (at your option) any later version.
   */
  
  #ifndef _KEYS_REQUEST_KEY_AUTH_TYPE_H
@@@ -14,6 -18,7 +14,7 @@@
   * Authorisation record for request_key().
   */
  struct request_key_auth {
+       struct rcu_head         rcu;
        struct key              *target_key;
        struct key              *dest_keyring;
        const struct cred       *cred;
diff --combined include/linux/key.h
@@@ -1,9 -1,14 +1,9 @@@
 +/* SPDX-License-Identifier: GPL-2.0-or-later */
  /* Authentication token and access key management
   *
   * Copyright (C) 2004, 2007 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
   *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
 - *
 - *
   * See Documentation/security/keys/core.rst for information on keys/keyrings.
   */
  
@@@ -269,22 -274,14 +269,14 @@@ extern struct key *request_key(struct k
                               const char *description,
                               const char *callout_info);
  
+ extern struct key *request_key_rcu(struct key_type *type,
+                                  const char *description);
  extern struct key *request_key_with_auxdata(struct key_type *type,
                                            const char *description,
                                            const void *callout_info,
                                            size_t callout_len,
                                            void *aux);
- extern struct key *request_key_async(struct key_type *type,
-                                    const char *description,
-                                    const void *callout_info,
-                                    size_t callout_len);
- extern struct key *request_key_async_with_auxdata(struct key_type *type,
-                                                 const char *description,
-                                                 const void *callout_info,
-                                                 size_t callout_len,
-                                                 void *aux);
  
  extern int wait_for_key_construction(struct key *key, bool intr);
  
diff --combined include/linux/sched.h
@@@ -35,7 -35,6 +35,7 @@@ struct audit_context
  struct backing_dev_info;
  struct bio_list;
  struct blk_plug;
 +struct capture_control;
  struct cfs_rq;
  struct fs_struct;
  struct futex_pi_state;
@@@ -48,9 -47,8 +48,9 @@@ struct pid_namespace
  struct pipe_inode_info;
  struct rcu_node;
  struct reclaim_state;
 -struct capture_control;
  struct robust_list_head;
 +struct root_domain;
 +struct rq;
  struct sched_attr;
  struct sched_param;
  struct seq_file;
@@@ -283,18 -281,6 +283,18 @@@ struct vtime 
        u64                     gtime;
  };
  
 +/*
 + * Utilization clamp constraints.
 + * @UCLAMP_MIN:       Minimum utilization
 + * @UCLAMP_MAX:       Maximum utilization
 + * @UCLAMP_CNT:       Utilization clamp constraints count
 + */
 +enum uclamp_id {
 +      UCLAMP_MIN = 0,
 +      UCLAMP_MAX,
 +      UCLAMP_CNT
 +};
 +
  struct sched_info {
  #ifdef CONFIG_SCHED_INFO
        /* Cumulative counters: */
  # define SCHED_FIXEDPOINT_SHIFT               10
  # define SCHED_FIXEDPOINT_SCALE               (1L << SCHED_FIXEDPOINT_SHIFT)
  
 +/* Increase resolution of cpu_capacity calculations */
 +# define SCHED_CAPACITY_SHIFT         SCHED_FIXEDPOINT_SHIFT
 +# define SCHED_CAPACITY_SCALE         (1L << SCHED_CAPACITY_SHIFT)
 +
  struct load_weight {
        unsigned long                   weight;
        u32                             inv_weight;
@@@ -578,47 -560,12 +578,47 @@@ struct sched_dl_entity 
        struct hrtimer inactive_timer;
  };
  
 +#ifdef CONFIG_UCLAMP_TASK
 +/* Number of utilization clamp buckets (shorter alias) */
 +#define UCLAMP_BUCKETS CONFIG_UCLAMP_BUCKETS_COUNT
 +
 +/*
 + * Utilization clamp for a scheduling entity
 + * @value:            clamp value "assigned" to a se
 + * @bucket_id:                bucket index corresponding to the "assigned" value
 + * @active:           the se is currently refcounted in a rq's bucket
 + * @user_defined:     the requested clamp value comes from user-space
 + *
 + * The bucket_id is the index of the clamp bucket matching the clamp value
 + * which is pre-computed and stored to avoid expensive integer divisions from
 + * the fast path.
 + *
 + * The active bit is set whenever a task has got an "effective" value assigned,
 + * which can be different from the clamp value "requested" from user-space.
 + * This allows to know a task is refcounted in the rq's bucket corresponding
 + * to the "effective" bucket_id.
 + *
 + * The user_defined bit is set whenever a task has got a task-specific clamp
 + * value requested from userspace, i.e. the system defaults apply to this task
 + * just as a restriction. This allows to relax default clamps when a less
 + * restrictive task-specific value has been requested, thus allowing to
 + * implement a "nice" semantic. For example, a task running with a 20%
 + * default boost can still drop its own boosting to 0%.
 + */
 +struct uclamp_se {
 +      unsigned int value              : bits_per(SCHED_CAPACITY_SCALE);
 +      unsigned int bucket_id          : bits_per(UCLAMP_BUCKETS);
 +      unsigned int active             : 1;
 +      unsigned int user_defined       : 1;
 +};
 +#endif /* CONFIG_UCLAMP_TASK */
 +
  union rcu_special {
        struct {
                u8                      blocked;
                u8                      need_qs;
                u8                      exp_hint; /* Hint for performance. */
 -              u8                      pad; /* No garbage from compiler! */
 +              u8                      deferred_qs;
        } b; /* Bits. */
        u32 s; /* Set of bits. */
  };
@@@ -693,13 -640,6 +693,13 @@@ struct task_struct 
  #endif
        struct sched_dl_entity          dl;
  
 +#ifdef CONFIG_UCLAMP_TASK
 +      /* Clamp values requested for a scheduling entity */
 +      struct uclamp_se                uclamp_req[UCLAMP_CNT];
 +      /* Effective clamp values used for a scheduling entity */
 +      struct uclamp_se                uclamp[UCLAMP_CNT];
 +#endif
 +
  #ifdef CONFIG_PREEMPT_NOTIFIERS
        /* List of struct preempt_notifier: */
        struct hlist_head               preempt_notifiers;
  
        unsigned int                    policy;
        int                             nr_cpus_allowed;
 -      cpumask_t                       cpus_allowed;
 +      const cpumask_t                 *cpus_ptr;
 +      cpumask_t                       cpus_mask;
  
  #ifdef CONFIG_PREEMPT_RCU
        int                             rcu_read_lock_nesting;
        /* Effective (overridable) subjective task credentials (COW): */
        const struct cred __rcu         *cred;
  
+ #ifdef CONFIG_KEYS
+       /* Cached requested key. */
+       struct key                      *cached_requested_key;
+ #endif
        /*
         * executable name, excluding path.
         *
@@@ -1460,7 -1404,7 +1465,7 @@@ extern struct pid *cad_pid
  #define PF_SWAPWRITE          0x00800000      /* Allowed to write to swap */
  #define PF_MEMSTALL           0x01000000      /* Stalled due to lack of memory */
  #define PF_UMH                        0x02000000      /* I'm an Usermodehelper process */
 -#define PF_NO_SETAFFINITY     0x04000000      /* Userland is not allowed to meddle with cpus_allowed */
 +#define PF_NO_SETAFFINITY     0x04000000      /* Userland is not allowed to meddle with cpus_mask */
  #define PF_MCE_EARLY          0x08000000      /* Early kill for mce process policy */
  #define PF_MEMALLOC_NOCMA     0x10000000      /* All allocation request will have _GFP_MOVABLE cleared */
  #define PF_FREEZER_SKIP               0x40000000      /* Freezer should not count it as freezable */
@@@ -1579,6 -1523,10 +1584,6 @@@ static inline int set_cpus_allowed_ptr(
  }
  #endif
  
 -#ifndef cpu_relax_yield
 -#define cpu_relax_yield() cpu_relax()
 -#endif
 -
  extern int yield_to(struct task_struct *p, bool preempt);
  extern void set_user_nice(struct task_struct *p, long nice);
  extern int task_prio(const struct task_struct *p);
@@@ -1976,16 -1924,4 +1981,16 @@@ static inline void rseq_syscall(struct 
  
  #endif
  
 +const struct sched_avg *sched_trace_cfs_rq_avg(struct cfs_rq *cfs_rq);
 +char *sched_trace_cfs_rq_path(struct cfs_rq *cfs_rq, char *str, int len);
 +int sched_trace_cfs_rq_cpu(struct cfs_rq *cfs_rq);
 +
 +const struct sched_avg *sched_trace_rq_avg_rt(struct rq *rq);
 +const struct sched_avg *sched_trace_rq_avg_dl(struct rq *rq);
 +const struct sched_avg *sched_trace_rq_avg_irq(struct rq *rq);
 +
 +int sched_trace_rq_cpu(struct rq *rq);
 +
 +const struct cpumask *sched_trace_rd_span(struct root_domain *rd);
 +
  #endif
@@@ -1,9 -1,12 +1,9 @@@
 +/* SPDX-License-Identifier: GPL-2.0-only */
  /*
   * Tracing hooks
   *
   * Copyright (C) 2008-2009 Red Hat, Inc.  All rights reserved.
   *
 - * This copyrighted material is made available to anyone wishing to use,
 - * modify, copy, or redistribute it subject to the terms and conditions
 - * of the GNU General Public License v.2.
 - *
   * This file defines hook entry points called by core code where
   * user tracing/debugging support might need to do something.  These
   * entry points are called tracehook_*().  Each hook declared below
@@@ -184,6 -187,13 +184,13 @@@ static inline void tracehook_notify_res
        if (unlikely(current->task_works))
                task_work_run();
  
+ #ifdef CONFIG_KEYS_REQUEST_CACHE
+       if (unlikely(current->cached_requested_key)) {
+               key_put(current->cached_requested_key);
+               current->cached_requested_key = NULL;
+       }
+ #endif
        mem_cgroup_handle_over_high();
        blkcg_maybe_throttle_current();
  }
diff --combined kernel/cred.c
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* Task credentials management - see Documentation/security/credentials.rst
   *
   * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public Licence
 - * as published by the Free Software Foundation; either version
 - * 2 of the Licence, or (at your option) any later version.
   */
  #include <linux/export.h>
  #include <linux/cred.h>
@@@ -170,6 -174,11 +170,11 @@@ void exit_creds(struct task_struct *tsk
        validate_creds(cred);
        alter_cred_subscribers(cred, -1);
        put_cred(cred);
+ #ifdef CONFIG_KEYS_REQUEST_CACHE
+       key_put(current->cached_requested_key);
+       current->cached_requested_key = NULL;
+ #endif
  }
  
  /**
@@@ -323,6 -332,10 +328,10 @@@ int copy_creds(struct task_struct *p, u
        struct cred *new;
        int ret;
  
+ #ifdef CONFIG_KEYS_REQUEST_CACHE
+       p->cached_requested_key = NULL;
+ #endif
        if (
  #ifdef CONFIG_KEYS
                !p->cred->thread_keyring &&
@@@ -446,15 -459,6 +455,15 @@@ int commit_creds(struct cred *new
                if (task->mm)
                        set_dumpable(task->mm, suid_dumpable);
                task->pdeath_signal = 0;
 +              /*
 +               * If a task drops privileges and becomes nondumpable,
 +               * the dumpability change must become visible before
 +               * the credential change; otherwise, a __ptrace_may_access()
 +               * racing with this change may be able to attach to a task it
 +               * shouldn't be able to attach to (as if the task had dropped
 +               * privileges without becoming nondumpable).
 +               * Pairs with a read barrier in __ptrace_may_access().
 +               */
                smp_wmb();
        }
  
diff --combined security/keys/Kconfig
@@@ -1,4 -1,3 +1,4 @@@
 +# SPDX-License-Identifier: GPL-2.0-only
  #
  # Key management configuration
  #
@@@ -25,6 -24,24 +25,24 @@@ config KEYS_COMPA
        def_bool y
        depends on COMPAT && KEYS
  
+ config KEYS_REQUEST_CACHE
+       bool "Enable temporary caching of the last request_key() result"
+       depends on KEYS
+       help
+         This option causes the result of the last successful request_key()
+         call that didn't upcall to the kernel to be cached temporarily in the
+         task_struct.  The cache is cleared by exit and just prior to the
+         resumption of userspace.
+         This allows the key used for multiple step processes where each step
+         wants to request a key that is likely the same as the one requested
+         by the last step to save on the searching.
+         An example of such a process is a pathwalk through a network
+         filesystem in which each method needs to request an authentication
+         key.  Pathwalk will call multiple methods for each dentry traversed
+         (permission, d_revalidate, lookup, getxattr, getacl, ...).
  config PERSISTENT_KEYRINGS
        bool "Enable register of persistent per-UID keyrings"
        depends on KEYS
diff --combined security/keys/internal.h
@@@ -1,8 -1,12 +1,8 @@@
 +/* SPDX-License-Identifier: GPL-2.0-or-later */
  /* Authentication token and access key management internal defs
   *
   * Copyright (C) 2003-5, 2007 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
   */
  
  #ifndef _INTERNAL_H
@@@ -135,11 -139,11 +135,11 @@@ struct keyring_search_context 
  
  extern bool key_default_cmp(const struct key *key,
                            const struct key_match_data *match_data);
- extern key_ref_t keyring_search_aux(key_ref_t keyring_ref,
+ extern key_ref_t keyring_search_rcu(key_ref_t keyring_ref,
                                    struct keyring_search_context *ctx);
  
- extern key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx);
- extern key_ref_t search_process_keyrings(struct keyring_search_context *ctx);
+ extern key_ref_t search_cred_keyrings_rcu(struct keyring_search_context *ctx);
+ extern key_ref_t search_process_keyrings_rcu(struct keyring_search_context *ctx);
  
  extern struct key *find_keyring_by_name(const char *name, bool uid_keyring);
  
diff --combined security/keys/key.c
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* Basic authentication token and access key management
   *
   * Copyright (C) 2004-2008 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
   */
  
  #include <linux/export.h>
@@@ -455,7 -459,7 +455,7 @@@ static int __key_instantiate_and_link(s
  
                        /* disable the authorisation key */
                        if (authkey)
-                               key_revoke(authkey);
+                               key_invalidate(authkey);
  
                        if (prep->expiry != TIME64_MAX) {
                                key->expiry = prep->expiry;
@@@ -612,7 -616,7 +612,7 @@@ int key_reject_and_link(struct key *key
  
                /* disable the authorisation key */
                if (authkey)
-                       key_revoke(authkey);
+                       key_invalidate(authkey);
        }
  
        mutex_unlock(&key_construction_mutex);
diff --combined security/keys/keyring.c
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* Keyring handling
   *
   * Copyright (C) 2004-2005, 2008, 2013 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
   */
  
  #include <linux/export.h>
@@@ -831,7 -835,7 +831,7 @@@ found
  }
  
  /**
-  * keyring_search_aux - Search a keyring tree for a key matching some criteria
+  * keyring_search_rcu - Search a keyring tree for a matching key under RCU
   * @keyring_ref: A pointer to the keyring with possession indicator.
   * @ctx: The keyring search context.
   *
   * addition, the LSM gets to forbid keyring searches and key matches.
   *
   * The search is performed as a breadth-then-depth search up to the prescribed
-  * limit (KEYRING_SEARCH_MAX_DEPTH).
+  * limit (KEYRING_SEARCH_MAX_DEPTH).  The caller must hold the RCU read lock to
+  * prevent keyrings from being destroyed or rearranged whilst they are being
+  * searched.
   *
   * Keys are matched to the type provided and are then filtered by the match
   * function, which is given the description to use in any way it sees fit.  The
   * In the case of a successful return, the possession attribute from
   * @keyring_ref is propagated to the returned key reference.
   */
- key_ref_t keyring_search_aux(key_ref_t keyring_ref,
+ key_ref_t keyring_search_rcu(key_ref_t keyring_ref,
                             struct keyring_search_context *ctx)
  {
        struct key *keyring;
                        return ERR_PTR(err);
        }
  
-       rcu_read_lock();
        ctx->now = ktime_get_real_seconds();
        if (search_nested_keyrings(keyring, ctx))
                __key_get(key_ref_to_ptr(ctx->result));
-       rcu_read_unlock();
        return ctx->result;
  }
  
   * @type: The type of keyring we want to find.
   * @description: The name of the keyring we want to find.
   *
-  * As keyring_search_aux() above, but using the current task's credentials and
+  * As keyring_search_rcu() above, but using the current task's credentials and
   * type's default matching function and preferred search method.
   */
  key_ref_t keyring_search(key_ref_t keyring,
                        return ERR_PTR(ret);
        }
  
-       key = keyring_search_aux(keyring, &ctx);
+       rcu_read_lock();
+       key = keyring_search_rcu(keyring, &ctx);
+       rcu_read_unlock();
  
        if (type->match_free)
                type->match_free(&ctx.match_data);
diff --combined security/keys/proc.c
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* procfs files for key database enumeration
   *
   * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
   */
  
  #include <linux/init.h>
@@@ -175,7 -179,9 +175,9 @@@ static int proc_keys_show(struct seq_fi
         * skip if the key does not indicate the possessor can view it
         */
        if (key->perm & KEY_POS_VIEW) {
-               skey_ref = search_my_process_keyrings(&ctx);
+               rcu_read_lock();
+               skey_ref = search_cred_keyrings_rcu(&ctx);
+               rcu_read_unlock();
                if (!IS_ERR(skey_ref)) {
                        key_ref_put(skey_ref);
                        key_ref = make_key_ref(key, 1);
@@@ -1,8 -1,12 +1,8 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* Manage a process's keyrings
   *
   * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
 - *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
   */
  
  #include <linux/init.h>
@@@ -314,7 -318,8 +314,8 @@@ void key_fsgid_changed(struct cred *new
  
  /*
   * Search the process keyrings attached to the supplied cred for the first
-  * matching key.
+  * matching key under RCU conditions (the caller must be holding the RCU read
+  * lock).
   *
   * The search criteria are the type and the match function.  The description is
   * given to the match function as a parameter, but doesn't otherwise influence
   * In the case of a successful return, the possession attribute is set on the
   * returned key reference.
   */
- key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
+ key_ref_t search_cred_keyrings_rcu(struct keyring_search_context *ctx)
  {
        key_ref_t key_ref, ret, err;
        const struct cred *cred = ctx->cred;
  
        /* search the thread keyring first */
        if (cred->thread_keyring) {
-               key_ref = keyring_search_aux(
+               key_ref = keyring_search_rcu(
                        make_key_ref(cred->thread_keyring, 1), ctx);
                if (!IS_ERR(key_ref))
                        goto found;
  
        /* search the process keyring second */
        if (cred->process_keyring) {
-               key_ref = keyring_search_aux(
+               key_ref = keyring_search_rcu(
                        make_key_ref(cred->process_keyring, 1), ctx);
                if (!IS_ERR(key_ref))
                        goto found;
  
        /* search the session keyring */
        if (cred->session_keyring) {
-               key_ref = keyring_search_aux(
+               key_ref = keyring_search_rcu(
                        make_key_ref(cred->session_keyring, 1), ctx);
  
                if (!IS_ERR(key_ref))
        }
        /* or search the user-session keyring */
        else if (READ_ONCE(cred->user->session_keyring)) {
-               key_ref = keyring_search_aux(
+               key_ref = keyring_search_rcu(
                        make_key_ref(READ_ONCE(cred->user->session_keyring), 1),
                        ctx);
                if (!IS_ERR(key_ref))
@@@ -444,16 -449,16 +445,16 @@@ found
   * the keys attached to the assumed authorisation key using its credentials if
   * one is available.
   *
-  * Return same as search_my_process_keyrings().
+  * The caller must be holding the RCU read lock.
+  *
+  * Return same as search_cred_keyrings_rcu().
   */
- key_ref_t search_process_keyrings(struct keyring_search_context *ctx)
+ key_ref_t search_process_keyrings_rcu(struct keyring_search_context *ctx)
  {
        struct request_key_auth *rka;
        key_ref_t key_ref, ret = ERR_PTR(-EACCES), err;
  
-       might_sleep();
-       key_ref = search_my_process_keyrings(ctx);
+       key_ref = search_cred_keyrings_rcu(ctx);
        if (!IS_ERR(key_ref))
                goto found;
        err = key_ref;
            ) {
                const struct cred *cred = ctx->cred;
  
-               /* defend against the auth key being revoked */
-               down_read(&cred->request_key_auth->sem);
-               if (key_validate(ctx->cred->request_key_auth) == 0) {
+               if (key_validate(cred->request_key_auth) == 0) {
                        rka = ctx->cred->request_key_auth->payload.data[0];
  
+                       //// was search_process_keyrings() [ie. recursive]
                        ctx->cred = rka->cred;
-                       key_ref = search_process_keyrings(ctx);
+                       key_ref = search_cred_keyrings_rcu(ctx);
                        ctx->cred = cred;
  
-                       up_read(&cred->request_key_auth->sem);
                        if (!IS_ERR(key_ref))
                                goto found;
                        ret = key_ref;
-               } else {
-                       up_read(&cred->request_key_auth->sem);
                }
        }
  
  found:
        return key_ref;
  }
  /*
   * See if the key we're looking at is the target key.
   */
@@@ -687,7 -684,9 +680,9 @@@ try_again
                ctx.index_key                   = key->index_key;
                ctx.match_data.raw_data         = key;
                kdebug("check possessed");
-               skey_ref = search_process_keyrings(&ctx);
+               rcu_read_lock();
+               skey_ref = search_process_keyrings_rcu(&ctx);
+               rcu_read_unlock();
                kdebug("possessed=%p", skey_ref);
  
                if (!IS_ERR(skey_ref)) {
@@@ -1,9 -1,13 +1,9 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* Request a key from userspace
   *
   * Copyright (C) 2004-2007 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
   *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
 - *
   * See Documentation/security/keys/request-key.rst
   */
  
  
  #define key_negative_timeout  60      /* default timeout on a negative key's existence */
  
+ static struct key *check_cached_key(struct keyring_search_context *ctx)
+ {
+ #ifdef CONFIG_KEYS_REQUEST_CACHE
+       struct key *key = current->cached_requested_key;
+       if (key &&
+           ctx->match_data.cmp(key, &ctx->match_data) &&
+           !(key->flags & ((1 << KEY_FLAG_INVALIDATED) |
+                           (1 << KEY_FLAG_REVOKED))))
+               return key_get(key);
+ #endif
+       return NULL;
+ }
+ static void cache_requested_key(struct key *key)
+ {
+ #ifdef CONFIG_KEYS_REQUEST_CACHE
+       struct task_struct *t = current;
+       key_put(t->cached_requested_key);
+       t->cached_requested_key = key_get(key);
+       set_tsk_thread_flag(t, TIF_NOTIFY_RESUME);
+ #endif
+ }
  /**
   * complete_request_key - Complete the construction of a key.
   * @authkey: The authorisation key.
@@@ -218,7 -247,7 +243,7 @@@ static int construct_key(struct key *ke
        /* check that the actor called complete_request_key() prior to
         * returning an error */
        WARN_ON(ret < 0 &&
-               !test_bit(KEY_FLAG_REVOKED, &authkey->flags));
+               !test_bit(KEY_FLAG_INVALIDATED, &authkey->flags));
  
        key_put(authkey);
        kleave(" = %d", ret);
@@@ -381,7 -410,9 +406,9 @@@ static int construct_alloc_key(struct k
         * waited for locks */
        mutex_lock(&key_construction_mutex);
  
-       key_ref = search_process_keyrings(ctx);
+       rcu_read_lock();
+       key_ref = search_process_keyrings_rcu(ctx);
+       rcu_read_unlock();
        if (!IS_ERR(key_ref))
                goto key_already_present;
  
@@@ -556,10 -587,26 +583,26 @@@ struct key *request_key_and_link(struc
                }
        }
  
+       key = check_cached_key(&ctx);
+       if (key)
+               return key;
        /* search all the process keyrings for a key */
-       key_ref = search_process_keyrings(&ctx);
+       rcu_read_lock();
+       key_ref = search_process_keyrings_rcu(&ctx);
+       rcu_read_unlock();
  
        if (!IS_ERR(key_ref)) {
+               if (dest_keyring) {
+                       ret = key_task_permission(key_ref, current_cred(),
+                                                 KEY_NEED_LINK);
+                       if (ret < 0) {
+                               key_ref_put(key_ref);
+                               key = ERR_PTR(ret);
+                               goto error_free;
+                       }
+               }
                key = key_ref_to_ptr(key_ref);
                if (dest_keyring) {
                        ret = key_link(dest_keyring, key);
                                goto error_free;
                        }
                }
+               /* Only cache the key on immediate success */
+               cache_requested_key(key);
        } else if (PTR_ERR(key_ref) != -EAGAIN) {
                key = ERR_CAST(key_ref);
        } else  {
@@@ -689,52 -739,51 +735,51 @@@ struct key *request_key_with_auxdata(st
  }
  EXPORT_SYMBOL(request_key_with_auxdata);
  
- /*
-  * request_key_async - Request a key (allow async construction)
-  * @type: Type of key.
-  * @description: The searchable description of the key.
-  * @callout_info: The data to pass to the instantiation upcall (or NULL).
-  * @callout_len: The length of callout_info.
+ /**
+  * request_key_rcu - Request key from RCU-read-locked context
+  * @type: The type of key we want.
+  * @description: The name of the key we want.
   *
-  * As for request_key_and_link() except that it does not add the returned key
-  * to a keyring if found, new keys are always allocated in the user's quota and
-  * no auxiliary data can be passed.
+  * Request a key from a context that we may not sleep in (such as RCU-mode
+  * pathwalk).  Keys under construction are ignored.
   *
-  * The caller should call wait_for_key_construction() to wait for the
-  * completion of the returned key if it is still undergoing construction.
+  * Return a pointer to the found key if successful, -ENOKEY if we couldn't find
+  * a key or some other error if the key found was unsuitable or inaccessible.
   */
- struct key *request_key_async(struct key_type *type,
-                             const char *description,
-                             const void *callout_info,
-                             size_t callout_len)
+ struct key *request_key_rcu(struct key_type *type, const char *description)
  {
-       return request_key_and_link(type, description, callout_info,
-                                   callout_len, NULL, NULL,
-                                   KEY_ALLOC_IN_QUOTA);
- }
- EXPORT_SYMBOL(request_key_async);
+       struct keyring_search_context ctx = {
+               .index_key.type         = type,
+               .index_key.description  = description,
+               .index_key.desc_len     = strlen(description),
+               .cred                   = current_cred(),
+               .match_data.cmp         = key_default_cmp,
+               .match_data.raw_data    = description,
+               .match_data.lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT,
+               .flags                  = (KEYRING_SEARCH_DO_STATE_CHECK |
+                                          KEYRING_SEARCH_SKIP_EXPIRED),
+       };
+       struct key *key;
+       key_ref_t key_ref;
  
- /*
-  * request a key with auxiliary data for the upcaller (allow async construction)
-  * @type: Type of key.
-  * @description: The searchable description of the key.
-  * @callout_info: The data to pass to the instantiation upcall (or NULL).
-  * @callout_len: The length of callout_info.
-  * @aux: Auxiliary data for the upcall.
-  *
-  * As for request_key_and_link() except that it does not add the returned key
-  * to a keyring if found and new keys are always allocated in the user's quota.
-  *
-  * The caller should call wait_for_key_construction() to wait for the
-  * completion of the returned key if it is still undergoing construction.
-  */
- struct key *request_key_async_with_auxdata(struct key_type *type,
-                                          const char *description,
-                                          const void *callout_info,
-                                          size_t callout_len,
-                                          void *aux)
- {
-       return request_key_and_link(type, description, callout_info,
-                                   callout_len, aux, NULL, KEY_ALLOC_IN_QUOTA);
+       kenter("%s,%s", type->name, description);
+       key = check_cached_key(&ctx);
+       if (key)
+               return key;
+       /* search all the process keyrings for a key */
+       key_ref = search_process_keyrings_rcu(&ctx);
+       if (IS_ERR(key_ref)) {
+               key = ERR_CAST(key_ref);
+               if (PTR_ERR(key_ref) == -EAGAIN)
+                       key = ERR_PTR(-ENOKEY);
+       } else {
+               key = key_ref_to_ptr(key_ref);
+               cache_requested_key(key);
+       }
+       kleave(" = %p", key);
+       return key;
  }
- EXPORT_SYMBOL(request_key_async_with_auxdata);
+ EXPORT_SYMBOL(request_key_rcu);
@@@ -1,9 -1,13 +1,9 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /* Request key authorisation token key definition.
   *
   * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved.
   * Written by David Howells (dhowells@redhat.com)
   *
 - * This program is free software; you can redistribute it and/or
 - * modify it under the terms of the GNU General Public License
 - * as published by the Free Software Foundation; either version
 - * 2 of the License, or (at your option) any later version.
 - *
   * See Documentation/security/keys/request-key.rst
   */
  
@@@ -54,7 -58,7 +54,7 @@@ static void request_key_auth_free_prepa
  static int request_key_auth_instantiate(struct key *key,
                                        struct key_preparsed_payload *prep)
  {
-       key->payload.data[0] = (struct request_key_auth *)prep->data;
+       rcu_assign_keypointer(key, (struct request_key_auth *)prep->data);
        return 0;
  }
  
@@@ -64,7 -68,7 +64,7 @@@
  static void request_key_auth_describe(const struct key *key,
                                      struct seq_file *m)
  {
-       struct request_key_auth *rka = get_request_key_auth(key);
+       struct request_key_auth *rka = dereference_key_rcu(key);
  
        seq_puts(m, "key:");
        seq_puts(m, key->description);
@@@ -79,7 -83,7 +79,7 @@@
  static long request_key_auth_read(const struct key *key,
                                  char __user *buffer, size_t buflen)
  {
-       struct request_key_auth *rka = get_request_key_auth(key);
+       struct request_key_auth *rka = dereference_key_locked(key);
        size_t datalen;
        long ret;
  
        return ret;
  }
  
- /*
-  * Handle revocation of an authorisation token key.
-  *
-  * Called with the key sem write-locked.
-  */
- static void request_key_auth_revoke(struct key *key)
- {
-       struct request_key_auth *rka = get_request_key_auth(key);
-       kenter("{%d}", key->serial);
-       if (rka->cred) {
-               put_cred(rka->cred);
-               rka->cred = NULL;
-       }
- }
  static void free_request_key_auth(struct request_key_auth *rka)
  {
        if (!rka)
  }
  
  /*
+  * Dispose of the request_key_auth record under RCU conditions
+  */
+ static void request_key_auth_rcu_disposal(struct rcu_head *rcu)
+ {
+       struct request_key_auth *rka =
+               container_of(rcu, struct request_key_auth, rcu);
+       free_request_key_auth(rka);
+ }
+ /*
+  * Handle revocation of an authorisation token key.
+  *
+  * Called with the key sem write-locked.
+  */
+ static void request_key_auth_revoke(struct key *key)
+ {
+       struct request_key_auth *rka = dereference_key_locked(key);
+       kenter("{%d}", key->serial);
+       rcu_assign_keypointer(key, NULL);
+       call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
+ }
+ /*
   * Destroy an instantiation authorisation token key.
   */
  static void request_key_auth_destroy(struct key *key)
  {
-       struct request_key_auth *rka = get_request_key_auth(key);
+       struct request_key_auth *rka = rcu_access_pointer(key->payload.rcu_data0);
  
        kenter("{%d}", key->serial);
-       free_request_key_auth(rka);
+       if (rka) {
+               rcu_assign_keypointer(key, NULL);
+               call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
+       }
  }
  
  /*
@@@ -245,7 -259,9 +255,9 @@@ struct key *key_get_instantiation_authk
  
        ctx.index_key.desc_len = sprintf(description, "%x", target_id);
  
-       authkey_ref = search_process_keyrings(&ctx);
+       rcu_read_lock();
+       authkey_ref = search_process_keyrings_rcu(&ctx);
+       rcu_read_unlock();
  
        if (IS_ERR(authkey_ref)) {
                authkey = ERR_CAST(authkey_ref);