diff mbox series

[v9,06/10] bpf: Add bpf_lookup_*_key() and bpf_key_put() kfuncs

Message ID 20220809134603.1769279-7-roberto.sassu@huawei.com (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series bpf: Add kfuncs for PKCS#7 signature verification | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR fail PR summary
bpf/vmtest-bpf-next-VM_Test-1 success Logs for Kernel LATEST on ubuntu-latest with gcc
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Kernel LATEST on ubuntu-latest with llvm-16
bpf/vmtest-bpf-next-VM_Test-3 fail Logs for Kernel LATEST on z15 with gcc
netdev/tree_selection success Guessed tree name to be net-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 1407 this patch: 1392
netdev/cc_maintainers success CCed 14 of 14 maintainers
netdev/build_clang fail Errors and warnings before: 163 this patch: 164
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn fail Errors and warnings before: 1399 this patch: 1384
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 173 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Roberto Sassu Aug. 9, 2022, 1:45 p.m. UTC
Add the bpf_lookup_user_key(), bpf_lookup_system_key() and bpf_key_put()
kfuncs, to respectively search a key with a given serial and flags, obtain
a key from a pre-determined ID defined in include/linux/verification.h, and
cleanup.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 include/linux/bpf.h      |   6 ++
 kernel/trace/bpf_trace.c | 151 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 157 insertions(+)

Comments

Daniel Borkmann Aug. 9, 2022, 10:53 p.m. UTC | #1
On 8/9/22 3:45 PM, Roberto Sassu wrote:
> Add the bpf_lookup_user_key(), bpf_lookup_system_key() and bpf_key_put()
> kfuncs, to respectively search a key with a given serial and flags, obtain
> a key from a pre-determined ID defined in include/linux/verification.h, and
> cleanup.
> 
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> ---
>   include/linux/bpf.h      |   6 ++
>   kernel/trace/bpf_trace.c | 151 +++++++++++++++++++++++++++++++++++++++
>   2 files changed, 157 insertions(+)
> 
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index 0d56c23cc504..564b9e0b8c16 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -2572,4 +2572,10 @@ static inline void bpf_cgroup_atype_get(u32 attach_btf_id, int cgroup_atype) {}
>   static inline void bpf_cgroup_atype_put(int cgroup_atype) {}
>   #endif /* CONFIG_BPF_LSM */
>   
> +#ifdef CONFIG_KEYS
> +struct bpf_key {
> +	struct key *key;
> +	bool valid_ptr;
> +};
> +#endif /* CONFIG_KEYS */
>   #endif /* _LINUX_BPF_H */
> diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
> index 68e5cdd24cef..33ca4cfe6e26 100644
> --- a/kernel/trace/bpf_trace.c
> +++ b/kernel/trace/bpf_trace.c
> @@ -20,6 +20,7 @@
>   #include <linux/fprobe.h>
>   #include <linux/bsearch.h>
>   #include <linux/sort.h>
> +#include <linux/key.h>
>   
>   #include <net/bpf_sk_storage.h>
>   
> @@ -1181,6 +1182,156 @@ static const struct bpf_func_proto bpf_get_func_arg_cnt_proto = {
>   	.arg1_type	= ARG_PTR_TO_CTX,
>   };
>   
> +#ifdef CONFIG_KEYS
> +__diag_push();
> +__diag_ignore_all("-Wmissing-prototypes",
> +		  "kfuncs which will be used in BPF programs");
> +
> +/**
> + * bpf_lookup_user_key - lookup a key by its serial
> + * @serial: key serial
> + * @flags: lookup-specific flags
> + *
> + * Search a key with a given *serial* and the provided *flags*. The
> + * returned key, if found, has the reference count incremented by
> + * one, and is stored in a bpf_key structure, returned to the caller.
> + * The bpf_key structure must be passed to bpf_key_put() when done
> + * with it, so that the key reference count is decremented and the
> + * bpf_key structure is freed.
> + *
> + * Permission checks are deferred to the time the key is used by
> + * one of the available key-specific kfuncs.
> + *
> + * Set *flags* with 1, to attempt creating a requested special
> + * keyring (e.g. session keyring), if it doesn't yet exist. Set
> + * *flags* with 2 to lookup a key without waiting for the key
> + * construction, and to retrieve uninstantiated keys (keys without
> + * data attached to them).
> + *
> + * Return: a bpf_key pointer with a valid key pointer if the key is found, a
> + *         NULL pointer otherwise.
> + */
> +noinline __weak struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags)

Why the need for noinline and the __weak here and below? (If indeed needed this
should probably be explained in the commit desc.)

> +{
> +	key_ref_t key_ref;
> +	struct bpf_key *bkey;
> +
> +	/* Keep in sync with include/linux/key.h. */
> +	if (flags > (KEY_LOOKUP_PARTIAL << 1) - 1)

Can't we just simplify and test flags & ~(KEY_LOOKUP_CREATE|KEY_LOOKUP_PARTIAL)?

> +		return NULL;
> +
> +	/* Permission check is deferred until actual kfunc using the key. */
> +	key_ref = lookup_user_key(serial, flags, KEY_DEFER_PERM_CHECK);
> +	if (IS_ERR(key_ref))
> +		return NULL;
> +
> +	bkey = kmalloc(sizeof(*bkey), GFP_KERNEL);
> +	if (!bkey) {
> +		key_put(key_ref_to_ptr(key_ref));
> +		return bkey;

nit: just return NULL probably cleaner

> +	}
> +
> +	bkey->key = key_ref_to_ptr(key_ref);
> +	bkey->valid_ptr = true;

nit: I'd probably rename s/valid_ptr/has_ref/.

> +	return bkey;
> +}
> +
> +/**
> + * bpf_lookup_system_key - lookup a key by a system-defined ID
> + * @id: key ID
> + *
> + * Obtain a bpf_key structure with a key pointer set to the passed key ID.
> + * The key pointer is marked as invalid, to prevent bpf_key_put() from
> + * attempting to decrement the key reference count on that pointer. The key
> + * pointer set in such way is currently understood only by
> + * verify_pkcs7_signature().
> + *
> + * Set *id* to one of the values defined in include/linux/verification.h:
> + * 0 for the primary keyring (immutable keyring of system keys); 1 for both
> + * the primary and secondary keyring (where keys can be added only if they
> + * are vouched for by existing keys in those keyrings); 2 for the platform
> + * keyring (primarily used by the integrity subsystem to verify a kexec'ed
> + * kerned image and, possibly, the initramfs signature).
> + *
> + * Return: a bpf_key pointer with an invalid key pointer set from the
> + *         pre-determined ID on success, a NULL pointer otherwise
> + */
> +noinline __weak struct bpf_key *bpf_lookup_system_key(u64 id)
> +{
> +	struct bpf_key *bkey;
> +
> +	/* Keep in sync with defs in include/linux/verification.h. */
> +	if (id > (unsigned long)VERIFY_USE_PLATFORM_KEYRING)
> +		return NULL;
> +
> +	bkey = kmalloc(sizeof(*bkey), GFP_KERNEL);

nit: Can't this be GFP_ATOMIC? Then bpf_lookup_system_key doesn't need KF_SLEEPABLE
attribute, fwiw. Overall, the bpf_lookup_{system,user}_key() look reasonable.

> +	if (!bkey)
> +		return bkey;
> +
> +	bkey->key = (struct key *)(unsigned long)id;
> +	bkey->valid_ptr = false;
> +
> +	return bkey;
> +}
> +
> +/**
> + * bpf_key_put - decrement key reference count if key is valid and free bpf_key
> + * @bkey: bpf_key structure
> + *
> + * Decrement the reference count of the key inside *bkey*, if the pointer
> + * is valid, and free *bkey*.
> + */
> +noinline __weak void bpf_key_put(struct bpf_key *bkey)
> +{
> +	if (bkey->valid_ptr)
> +		key_put(bkey->key);
> +
> +	kfree(bkey);
> +}
> +
> +__diag_pop();
> +
> +BTF_SET8_START(key_kfunc_set)
> +BTF_ID_FLAGS(func, bpf_lookup_user_key, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)
> +BTF_ID_FLAGS(func, bpf_lookup_system_key,
> +	     KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)
> +BTF_ID_FLAGS(func, bpf_key_put, KF_RELEASE)
> +BTF_SET8_END(key_kfunc_set)
> +
> +static const struct btf_kfunc_id_set bpf_key_kfunc_set = {
> +	.owner = THIS_MODULE,
> +	.set = &key_kfunc_set,
> +};
> +#endif /* CONFIG_KEYS */
> +
> +const struct btf_kfunc_id_set *kfunc_sets[] = {
> +#ifdef CONFIG_KEYS
> +	&bpf_key_kfunc_set,
> +#endif /* CONFIG_KEYS */
> +};
Roberto Sassu Aug. 10, 2022, 2:17 p.m. UTC | #2
> From: Daniel Borkmann [mailto:daniel@iogearbox.net]
> Sent: Wednesday, August 10, 2022 12:53 AM
> On 8/9/22 3:45 PM, Roberto Sassu wrote:
> > Add the bpf_lookup_user_key(), bpf_lookup_system_key() and bpf_key_put()
> > kfuncs, to respectively search a key with a given serial and flags, obtain
> > a key from a pre-determined ID defined in include/linux/verification.h, and
> > cleanup.
> >
> > Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> > ---
> >   include/linux/bpf.h      |   6 ++
> >   kernel/trace/bpf_trace.c | 151
> +++++++++++++++++++++++++++++++++++++++
> >   2 files changed, 157 insertions(+)
> >
> > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> > index 0d56c23cc504..564b9e0b8c16 100644
> > --- a/include/linux/bpf.h
> > +++ b/include/linux/bpf.h
> > @@ -2572,4 +2572,10 @@ static inline void bpf_cgroup_atype_get(u32
> attach_btf_id, int cgroup_atype) {}
> >   static inline void bpf_cgroup_atype_put(int cgroup_atype) {}
> >   #endif /* CONFIG_BPF_LSM */
> >
> > +#ifdef CONFIG_KEYS
> > +struct bpf_key {
> > +	struct key *key;
> > +	bool valid_ptr;
> > +};
> > +#endif /* CONFIG_KEYS */
> >   #endif /* _LINUX_BPF_H */
> > diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
> > index 68e5cdd24cef..33ca4cfe6e26 100644
> > --- a/kernel/trace/bpf_trace.c
> > +++ b/kernel/trace/bpf_trace.c
> > @@ -20,6 +20,7 @@
> >   #include <linux/fprobe.h>
> >   #include <linux/bsearch.h>
> >   #include <linux/sort.h>
> > +#include <linux/key.h>
> >
> >   #include <net/bpf_sk_storage.h>
> >
> > @@ -1181,6 +1182,156 @@ static const struct bpf_func_proto
> bpf_get_func_arg_cnt_proto = {
> >   	.arg1_type	= ARG_PTR_TO_CTX,
> >   };
> >
> > +#ifdef CONFIG_KEYS
> > +__diag_push();
> > +__diag_ignore_all("-Wmissing-prototypes",
> > +		  "kfuncs which will be used in BPF programs");
> > +
> > +/**
> > + * bpf_lookup_user_key - lookup a key by its serial
> > + * @serial: key serial
> > + * @flags: lookup-specific flags
> > + *
> > + * Search a key with a given *serial* and the provided *flags*. The
> > + * returned key, if found, has the reference count incremented by
> > + * one, and is stored in a bpf_key structure, returned to the caller.
> > + * The bpf_key structure must be passed to bpf_key_put() when done
> > + * with it, so that the key reference count is decremented and the
> > + * bpf_key structure is freed.
> > + *
> > + * Permission checks are deferred to the time the key is used by
> > + * one of the available key-specific kfuncs.
> > + *
> > + * Set *flags* with 1, to attempt creating a requested special
> > + * keyring (e.g. session keyring), if it doesn't yet exist. Set
> > + * *flags* with 2 to lookup a key without waiting for the key
> > + * construction, and to retrieve uninstantiated keys (keys without
> > + * data attached to them).
> > + *
> > + * Return: a bpf_key pointer with a valid key pointer if the key is found, a
> > + *         NULL pointer otherwise.
> > + */
> > +noinline __weak struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags)
> 
> Why the need for noinline and the __weak here and below? (If indeed needed
> this
> should probably be explained in the commit desc.)

Oh, I took from v3 of KP's patch set. It is gone in v5. Will do
the same as well.

> > +{
> > +	key_ref_t key_ref;
> > +	struct bpf_key *bkey;
> > +
> > +	/* Keep in sync with include/linux/key.h. */
> > +	if (flags > (KEY_LOOKUP_PARTIAL << 1) - 1)
> 
> Can't we just simplify and test flags &
> ~(KEY_LOOKUP_CREATE|KEY_LOOKUP_PARTIAL)?

I thought as if we have many flags.

> > +		return NULL;
> > +
> > +	/* Permission check is deferred until actual kfunc using the key. */
> > +	key_ref = lookup_user_key(serial, flags, KEY_DEFER_PERM_CHECK);
> > +	if (IS_ERR(key_ref))
> > +		return NULL;
> > +
> > +	bkey = kmalloc(sizeof(*bkey), GFP_KERNEL);
> > +	if (!bkey) {
> > +		key_put(key_ref_to_ptr(key_ref));
> > +		return bkey;
> 
> nit: just return NULL probably cleaner

Ok.

> > +	}
> > +
> > +	bkey->key = key_ref_to_ptr(key_ref);
> > +	bkey->valid_ptr = true;
> 
> nit: I'd probably rename s/valid_ptr/has_ref/.

Ok.

> > +	return bkey;
> > +}
> > +
> > +/**
> > + * bpf_lookup_system_key - lookup a key by a system-defined ID
> > + * @id: key ID
> > + *
> > + * Obtain a bpf_key structure with a key pointer set to the passed key ID.
> > + * The key pointer is marked as invalid, to prevent bpf_key_put() from
> > + * attempting to decrement the key reference count on that pointer. The key
> > + * pointer set in such way is currently understood only by
> > + * verify_pkcs7_signature().
> > + *
> > + * Set *id* to one of the values defined in include/linux/verification.h:
> > + * 0 for the primary keyring (immutable keyring of system keys); 1 for both
> > + * the primary and secondary keyring (where keys can be added only if they
> > + * are vouched for by existing keys in those keyrings); 2 for the platform
> > + * keyring (primarily used by the integrity subsystem to verify a kexec'ed
> > + * kerned image and, possibly, the initramfs signature).
> > + *
> > + * Return: a bpf_key pointer with an invalid key pointer set from the
> > + *         pre-determined ID on success, a NULL pointer otherwise
> > + */
> > +noinline __weak struct bpf_key *bpf_lookup_system_key(u64 id)
> > +{
> > +	struct bpf_key *bkey;
> > +
> > +	/* Keep in sync with defs in include/linux/verification.h. */
> > +	if (id > (unsigned long)VERIFY_USE_PLATFORM_KEYRING)
> > +		return NULL;
> > +
> > +	bkey = kmalloc(sizeof(*bkey), GFP_KERNEL);
> 
> nit: Can't this be GFP_ATOMIC? Then bpf_lookup_system_key doesn't need
> KF_SLEEPABLE
> attribute, fwiw. Overall, the bpf_lookup_{system,user}_key() look reasonable.

Ok, will do.

Thanks

Roberto

> > +	if (!bkey)
> > +		return bkey;
> > +
> > +	bkey->key = (struct key *)(unsigned long)id;
> > +	bkey->valid_ptr = false;
> > +
> > +	return bkey;
> > +}
> > +
> > +/**
> > + * bpf_key_put - decrement key reference count if key is valid and free
> bpf_key
> > + * @bkey: bpf_key structure
> > + *
> > + * Decrement the reference count of the key inside *bkey*, if the pointer
> > + * is valid, and free *bkey*.
> > + */
> > +noinline __weak void bpf_key_put(struct bpf_key *bkey)
> > +{
> > +	if (bkey->valid_ptr)
> > +		key_put(bkey->key);
> > +
> > +	kfree(bkey);
> > +}
> > +
> > +__diag_pop();
> > +
> > +BTF_SET8_START(key_kfunc_set)
> > +BTF_ID_FLAGS(func, bpf_lookup_user_key, KF_ACQUIRE | KF_RET_NULL |
> KF_SLEEPABLE)
> > +BTF_ID_FLAGS(func, bpf_lookup_system_key,
> > +	     KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)
> > +BTF_ID_FLAGS(func, bpf_key_put, KF_RELEASE)
> > +BTF_SET8_END(key_kfunc_set)
> > +
> > +static const struct btf_kfunc_id_set bpf_key_kfunc_set = {
> > +	.owner = THIS_MODULE,
> > +	.set = &key_kfunc_set,
> > +};
> > +#endif /* CONFIG_KEYS */
> > +
> > +const struct btf_kfunc_id_set *kfunc_sets[] = {
> > +#ifdef CONFIG_KEYS
> > +	&bpf_key_kfunc_set,
> > +#endif /* CONFIG_KEYS */
> > +};
Daniel Borkmann Aug. 10, 2022, 2:29 p.m. UTC | #3
[...]
>>> +noinline __weak struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags)
>>
>> Why the need for noinline and the __weak here and below? (If indeed needed
>> this
>> should probably be explained in the commit desc.)
> 
> Oh, I took from v3 of KP's patch set. It is gone in v5. Will do
> the same as well.
> 
>>> +{
>>> +	key_ref_t key_ref;
>>> +	struct bpf_key *bkey;
>>> +
>>> +	/* Keep in sync with include/linux/key.h. */
>>> +	if (flags > (KEY_LOOKUP_PARTIAL << 1) - 1)
>>
>> Can't we just simplify and test flags &
>> ~(KEY_LOOKUP_CREATE|KEY_LOOKUP_PARTIAL)?
> 
> I thought as if we have many flags.

I'd keep it simple for now, and if the actual need comes this can still be changed.

>>> +		return NULL;
>>> +
>>> +	/* Permission check is deferred until actual kfunc using the key. */
>>> +	key_ref = lookup_user_key(serial, flags, KEY_DEFER_PERM_CHECK);
>>> +	if (IS_ERR(key_ref))
>>> +		return NULL;
>>> +
>>> +	bkey = kmalloc(sizeof(*bkey), GFP_KERNEL);
>>> +	if (!bkey) {
>>> +		key_put(key_ref_to_ptr(key_ref));
>>> +		return bkey;
>>
>> nit: just return NULL probably cleaner
> 
> Ok.
diff mbox series

Patch

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 0d56c23cc504..564b9e0b8c16 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -2572,4 +2572,10 @@  static inline void bpf_cgroup_atype_get(u32 attach_btf_id, int cgroup_atype) {}
 static inline void bpf_cgroup_atype_put(int cgroup_atype) {}
 #endif /* CONFIG_BPF_LSM */
 
+#ifdef CONFIG_KEYS
+struct bpf_key {
+	struct key *key;
+	bool valid_ptr;
+};
+#endif /* CONFIG_KEYS */
 #endif /* _LINUX_BPF_H */
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index 68e5cdd24cef..33ca4cfe6e26 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -20,6 +20,7 @@ 
 #include <linux/fprobe.h>
 #include <linux/bsearch.h>
 #include <linux/sort.h>
+#include <linux/key.h>
 
 #include <net/bpf_sk_storage.h>
 
@@ -1181,6 +1182,156 @@  static const struct bpf_func_proto bpf_get_func_arg_cnt_proto = {
 	.arg1_type	= ARG_PTR_TO_CTX,
 };
 
+#ifdef CONFIG_KEYS
+__diag_push();
+__diag_ignore_all("-Wmissing-prototypes",
+		  "kfuncs which will be used in BPF programs");
+
+/**
+ * bpf_lookup_user_key - lookup a key by its serial
+ * @serial: key serial
+ * @flags: lookup-specific flags
+ *
+ * Search a key with a given *serial* and the provided *flags*. The
+ * returned key, if found, has the reference count incremented by
+ * one, and is stored in a bpf_key structure, returned to the caller.
+ * The bpf_key structure must be passed to bpf_key_put() when done
+ * with it, so that the key reference count is decremented and the
+ * bpf_key structure is freed.
+ *
+ * Permission checks are deferred to the time the key is used by
+ * one of the available key-specific kfuncs.
+ *
+ * Set *flags* with 1, to attempt creating a requested special
+ * keyring (e.g. session keyring), if it doesn't yet exist. Set
+ * *flags* with 2 to lookup a key without waiting for the key
+ * construction, and to retrieve uninstantiated keys (keys without
+ * data attached to them).
+ *
+ * Return: a bpf_key pointer with a valid key pointer if the key is found, a
+ *         NULL pointer otherwise.
+ */
+noinline __weak struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags)
+{
+	key_ref_t key_ref;
+	struct bpf_key *bkey;
+
+	/* Keep in sync with include/linux/key.h. */
+	if (flags > (KEY_LOOKUP_PARTIAL << 1) - 1)
+		return NULL;
+
+	/* Permission check is deferred until actual kfunc using the key. */
+	key_ref = lookup_user_key(serial, flags, KEY_DEFER_PERM_CHECK);
+	if (IS_ERR(key_ref))
+		return NULL;
+
+	bkey = kmalloc(sizeof(*bkey), GFP_KERNEL);
+	if (!bkey) {
+		key_put(key_ref_to_ptr(key_ref));
+		return bkey;
+	}
+
+	bkey->key = key_ref_to_ptr(key_ref);
+	bkey->valid_ptr = true;
+
+	return bkey;
+}
+
+/**
+ * bpf_lookup_system_key - lookup a key by a system-defined ID
+ * @id: key ID
+ *
+ * Obtain a bpf_key structure with a key pointer set to the passed key ID.
+ * The key pointer is marked as invalid, to prevent bpf_key_put() from
+ * attempting to decrement the key reference count on that pointer. The key
+ * pointer set in such way is currently understood only by
+ * verify_pkcs7_signature().
+ *
+ * Set *id* to one of the values defined in include/linux/verification.h:
+ * 0 for the primary keyring (immutable keyring of system keys); 1 for both
+ * the primary and secondary keyring (where keys can be added only if they
+ * are vouched for by existing keys in those keyrings); 2 for the platform
+ * keyring (primarily used by the integrity subsystem to verify a kexec'ed
+ * kerned image and, possibly, the initramfs signature).
+ *
+ * Return: a bpf_key pointer with an invalid key pointer set from the
+ *         pre-determined ID on success, a NULL pointer otherwise
+ */
+noinline __weak struct bpf_key *bpf_lookup_system_key(u64 id)
+{
+	struct bpf_key *bkey;
+
+	/* Keep in sync with defs in include/linux/verification.h. */
+	if (id > (unsigned long)VERIFY_USE_PLATFORM_KEYRING)
+		return NULL;
+
+	bkey = kmalloc(sizeof(*bkey), GFP_KERNEL);
+	if (!bkey)
+		return bkey;
+
+	bkey->key = (struct key *)(unsigned long)id;
+	bkey->valid_ptr = false;
+
+	return bkey;
+}
+
+/**
+ * bpf_key_put - decrement key reference count if key is valid and free bpf_key
+ * @bkey: bpf_key structure
+ *
+ * Decrement the reference count of the key inside *bkey*, if the pointer
+ * is valid, and free *bkey*.
+ */
+noinline __weak void bpf_key_put(struct bpf_key *bkey)
+{
+	if (bkey->valid_ptr)
+		key_put(bkey->key);
+
+	kfree(bkey);
+}
+
+__diag_pop();
+
+BTF_SET8_START(key_kfunc_set)
+BTF_ID_FLAGS(func, bpf_lookup_user_key, KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)
+BTF_ID_FLAGS(func, bpf_lookup_system_key,
+	     KF_ACQUIRE | KF_RET_NULL | KF_SLEEPABLE)
+BTF_ID_FLAGS(func, bpf_key_put, KF_RELEASE)
+BTF_SET8_END(key_kfunc_set)
+
+static const struct btf_kfunc_id_set bpf_key_kfunc_set = {
+	.owner = THIS_MODULE,
+	.set = &key_kfunc_set,
+};
+#endif /* CONFIG_KEYS */
+
+const struct btf_kfunc_id_set *kfunc_sets[] = {
+#ifdef CONFIG_KEYS
+	&bpf_key_kfunc_set,
+#endif /* CONFIG_KEYS */
+};
+
+static int __init bpf_kfuncs_init(void)
+{
+	int ret, i;
+
+	for (i = 0; i < ARRAY_SIZE(kfunc_sets); i++) {
+		ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING,
+						kfunc_sets[i]);
+		if (!ret)
+			continue;
+
+		ret = register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM,
+						kfunc_sets[i]);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+late_initcall(bpf_kfuncs_init);
+
 static const struct bpf_func_proto *
 bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
 {