* Re: [PATCH v4 03/34] lsm: consolidate lsm_allowed() and prepare_lsm() into lsm_prepare()
From: Mimi Zohar @ 2025-09-19 10:45 UTC (permalink / raw)
To: Paul Moore, linux-security-module, linux-integrity, selinux
Cc: John Johansen, Roberto Sassu, Fan Wu, Mickaël Salaün,
Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <20250916220355.252592-39-paul@paul-moore.com>
On Tue, 2025-09-16 at 18:03 -0400, Paul Moore wrote:
> Simplify and consolidate the lsm_allowed() and prepare_lsm() functions
> into a new function, lsm_prepare().
>
> Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> Reviewed-by: John Johansen <john.johhansen@canonical.com>
> Signed-off-by: Paul Moore <paul@paul-moore.com>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
> ---
> security/lsm_init.c | 109 +++++++++++++++++++-------------------------
> 1 file changed, 46 insertions(+), 63 deletions(-)
>
> diff --git a/security/lsm_init.c b/security/lsm_init.c
> index 124213b906af..6f40ab1d2f54 100644
> --- a/security/lsm_init.c
> +++ b/security/lsm_init.c
> @@ -123,22 +123,6 @@ static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
> is_enabled(lsm) ? "enabled" : "disabled");
> }
>
> -/* Is an LSM allowed to be initialized? */
> -static bool __init lsm_allowed(struct lsm_info *lsm)
> -{
> - /* Skip if the LSM is disabled. */
> - if (!is_enabled(lsm))
> - return false;
> -
> - /* Not allowed if another exclusive LSM already initialized. */
> - if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
> - init_debug("exclusive disabled: %s\n", lsm->name);
> - return false;
> - }
> -
> - return true;
> -}
> -
> static void __init lsm_set_blob_size(int *need, int *lbs)
> {
> int offset;
> @@ -151,54 +135,53 @@ static void __init lsm_set_blob_size(int *need, int *lbs)
> *need = offset;
> }
>
> -static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
> +/**
> + * lsm_prepare - Prepare the LSM framework for a new LSM
> + * @lsm: LSM definition
> + */
> +static void __init lsm_prepare(struct lsm_info *lsm)
> {
> - if (!needed)
> + struct lsm_blob_sizes *blobs;
> +
> + if (!is_enabled(lsm)) {
> + set_enabled(lsm, false);
> + return;
> + } else if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
> + init_debug("exclusive disabled: %s\n", lsm->name);
> + set_enabled(lsm, false);
> return;
> -
> - lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
> - lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
> - lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
> - /*
> - * The inode blob gets an rcu_head in addition to
> - * what the modules might need.
> - */
> - if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
> - blob_sizes.lbs_inode = sizeof(struct rcu_head);
> - lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
> - lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
> - lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key);
> - lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
> - lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event);
> - lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
> - lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
> - lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
> - lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
> - lsm_set_blob_size(&needed->lbs_xattr_count,
> - &blob_sizes.lbs_xattr_count);
> - lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
> - lsm_set_blob_size(&needed->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
> - lsm_set_blob_size(&needed->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
> - lsm_set_blob_size(&needed->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
> -}
> -
> -/* Prepare LSM for initialization. */
> -static void __init prepare_lsm(struct lsm_info *lsm)
> -{
> - int enabled = lsm_allowed(lsm);
> -
> - /* Record enablement (to handle any following exclusive LSMs). */
> - set_enabled(lsm, enabled);
> -
> - /* If enabled, do pre-initialization work. */
> - if (enabled) {
> - if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
> - exclusive = lsm;
> - init_debug("exclusive chosen: %s\n", lsm->name);
> - }
> -
> - lsm_set_blob_sizes(lsm->blobs);
> }
> +
> + /* Mark the LSM as enabled. */
> + set_enabled(lsm, true);
> + if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
> + init_debug("exclusive chosen: %s\n", lsm->name);
> + exclusive = lsm;
> + }
> +
> + /* Register the LSM blob sizes. */
> + blobs = lsm->blobs;
> + lsm_set_blob_size(&blobs->lbs_cred, &blob_sizes.lbs_cred);
> + lsm_set_blob_size(&blobs->lbs_file, &blob_sizes.lbs_file);
> + lsm_set_blob_size(&blobs->lbs_ib, &blob_sizes.lbs_ib);
> + /* inode blob gets an rcu_head in addition to LSM blobs. */
> + if (blobs->lbs_inode && blob_sizes.lbs_inode == 0)
> + blob_sizes.lbs_inode = sizeof(struct rcu_head);
> + lsm_set_blob_size(&blobs->lbs_inode, &blob_sizes.lbs_inode);
> + lsm_set_blob_size(&blobs->lbs_ipc, &blob_sizes.lbs_ipc);
> + lsm_set_blob_size(&blobs->lbs_key, &blob_sizes.lbs_key);
> + lsm_set_blob_size(&blobs->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
> + lsm_set_blob_size(&blobs->lbs_perf_event, &blob_sizes.lbs_perf_event);
> + lsm_set_blob_size(&blobs->lbs_sock, &blob_sizes.lbs_sock);
> + lsm_set_blob_size(&blobs->lbs_superblock, &blob_sizes.lbs_superblock);
> + lsm_set_blob_size(&blobs->lbs_task, &blob_sizes.lbs_task);
> + lsm_set_blob_size(&blobs->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
> + lsm_set_blob_size(&blobs->lbs_xattr_count,
> + &blob_sizes.lbs_xattr_count);
> + lsm_set_blob_size(&blobs->lbs_bdev, &blob_sizes.lbs_bdev);
> + lsm_set_blob_size(&blobs->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
> + lsm_set_blob_size(&blobs->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
> + lsm_set_blob_size(&blobs->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
> }
>
> /* Initialize a given LSM, if it is enabled. */
> @@ -361,7 +344,7 @@ static void __init ordered_lsm_init(void)
> ordered_lsm_parse(builtin_lsm_order, "builtin");
>
> for (lsm = ordered_lsms; *lsm; lsm++)
> - prepare_lsm(*lsm);
> + lsm_prepare(*lsm);
>
> report_lsm_order();
>
> @@ -505,7 +488,7 @@ int __init early_security_init(void)
> for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
> if (!lsm->enabled)
> lsm->enabled = &lsm_enabled_true;
> - prepare_lsm(lsm);
> + lsm_prepare(lsm);
> initialize_lsm(lsm);
> }
>
^ permalink raw reply
* Re: [PATCH v4 02/34] lsm: split the init code out into lsm_init.c
From: Mimi Zohar @ 2025-09-19 10:45 UTC (permalink / raw)
To: Paul Moore, linux-security-module, linux-integrity, selinux
Cc: John Johansen, Roberto Sassu, Fan Wu, Mickaël Salaün,
Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <20250916220355.252592-38-paul@paul-moore.com>
On Tue, 2025-09-16 at 18:03 -0400, Paul Moore wrote:
> Continue to pull code out of security/security.c to help improve
> readability by pulling all of the LSM framework initialization
> code out into a new file.
>
> No code changes.
>
> Reviewed-by: Kees Cook <kees@kernel.org>
> Reviewed-by: John Johansen <john.johansen@canonical.com>
> Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> Signed-off-by: Paul Moore <paul@paul-moore.com>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
> ---
> include/linux/lsm_hooks.h | 3 +-
> security/Makefile | 2 +-
> security/lsm.h | 22 ++
> security/lsm_init.c | 543 ++++++++++++++++++++++++++++++++++
> security/security.c | 597 +++-----------------------------------
> 5 files changed, 601 insertions(+), 566 deletions(-)
> create mode 100644 security/lsm.h
> create mode 100644 security/lsm_init.c
>
> diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
> index 79ec5a2bdcca..0112926ed923 100644
> --- a/include/linux/lsm_hooks.h
> +++ b/include/linux/lsm_hooks.h
> @@ -170,11 +170,10 @@ struct lsm_info {
> __used __section(".early_lsm_info.init") \
> __aligned(sizeof(unsigned long))
>
> +
> /* DO NOT tamper with these variables outside of the LSM framework */
> extern char *lsm_names;
> extern struct lsm_static_calls_table static_calls_table __ro_after_init;
> -extern struct lsm_info __start_lsm_info[], __end_lsm_info[];
> -extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[];
>
> /**
> * lsm_get_xattr_slot - Return the next available slot and increment the index
> diff --git a/security/Makefile b/security/Makefile
> index 14d87847bce8..4601230ba442 100644
> --- a/security/Makefile
> +++ b/security/Makefile
> @@ -11,7 +11,7 @@ obj-$(CONFIG_SECURITY) += lsm_syscalls.o
> obj-$(CONFIG_MMU) += min_addr.o
>
> # Object file lists
> -obj-$(CONFIG_SECURITY) += security.o lsm_notifier.o
> +obj-$(CONFIG_SECURITY) += security.o lsm_notifier.o lsm_init.o
> obj-$(CONFIG_SECURITYFS) += inode.o
> obj-$(CONFIG_SECURITY_SELINUX) += selinux/
> obj-$(CONFIG_SECURITY_SMACK) += smack/
> diff --git a/security/lsm.h b/security/lsm.h
> new file mode 100644
> index 000000000000..0e1731bad4a7
> --- /dev/null
> +++ b/security/lsm.h
> @@ -0,0 +1,22 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * LSM functions
> + */
> +
> +#ifndef _LSM_H_
> +#define _LSM_H_
> +
> +#include <linux/lsm_hooks.h>
> +
> +/* LSM blob configuration */
> +extern struct lsm_blob_sizes blob_sizes;
> +
> +/* LSM blob caches */
> +extern struct kmem_cache *lsm_file_cache;
> +extern struct kmem_cache *lsm_inode_cache;
> +
> +/* LSM blob allocators */
> +int lsm_cred_alloc(struct cred *cred, gfp_t gfp);
> +int lsm_task_alloc(struct task_struct *task);
> +
> +#endif /* _LSM_H_ */
> diff --git a/security/lsm_init.c b/security/lsm_init.c
> new file mode 100644
> index 000000000000..124213b906af
> --- /dev/null
> +++ b/security/lsm_init.c
> @@ -0,0 +1,543 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * LSM initialization functions
> + */
> +
> +#define pr_fmt(fmt) "LSM: " fmt
> +
> +#include <linux/init.h>
> +#include <linux/lsm_hooks.h>
> +
> +#include "lsm.h"
> +
> +char *lsm_names;
> +
> +/* Pointers to LSM sections defined in include/asm-generic/vmlinux.lds.h */
> +extern struct lsm_info __start_lsm_info[], __end_lsm_info[];
> +extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[];
> +
> +/* Boot-time LSM user choice */
> +static __initconst const char *const builtin_lsm_order = CONFIG_LSM;
> +static __initdata const char *chosen_lsm_order;
> +static __initdata const char *chosen_major_lsm;
> +
> +/* Ordered list of LSMs to initialize. */
> +static __initdata struct lsm_info *ordered_lsms[MAX_LSM_COUNT + 1];
> +static __initdata struct lsm_info *exclusive;
> +
> +static __initdata bool debug;
> +#define init_debug(...) \
> + do { \
> + if (debug) \
> + pr_info(__VA_ARGS__); \
> + } while (0)
> +
> +static int lsm_append(const char *new, char **result);
> +
> +/* Save user chosen LSM */
> +static int __init choose_major_lsm(char *str)
> +{
> + chosen_major_lsm = str;
> + return 1;
> +}
> +__setup("security=", choose_major_lsm);
> +
> +/* Explicitly choose LSM initialization order. */
> +static int __init choose_lsm_order(char *str)
> +{
> + chosen_lsm_order = str;
> + return 1;
> +}
> +__setup("lsm=", choose_lsm_order);
> +
> +/* Enable LSM order debugging. */
> +static int __init enable_debug(char *str)
> +{
> + debug = true;
> + return 1;
> +}
> +__setup("lsm.debug", enable_debug);
> +
> +/* Mark an LSM's enabled flag. */
> +static int lsm_enabled_true __initdata = 1;
> +static int lsm_enabled_false __initdata = 0;
> +static void __init set_enabled(struct lsm_info *lsm, bool enabled)
> +{
> + /*
> + * When an LSM hasn't configured an enable variable, we can use
> + * a hard-coded location for storing the default enabled state.
> + */
> + if (!lsm->enabled) {
> + if (enabled)
> + lsm->enabled = &lsm_enabled_true;
> + else
> + lsm->enabled = &lsm_enabled_false;
> + } else if (lsm->enabled == &lsm_enabled_true) {
> + if (!enabled)
> + lsm->enabled = &lsm_enabled_false;
> + } else if (lsm->enabled == &lsm_enabled_false) {
> + if (enabled)
> + lsm->enabled = &lsm_enabled_true;
> + } else {
> + *lsm->enabled = enabled;
> + }
> +}
> +
> +static inline bool is_enabled(struct lsm_info *lsm)
> +{
> + if (!lsm->enabled)
> + return false;
> +
> + return *lsm->enabled;
> +}
> +
> +/* Is an LSM already listed in the ordered LSMs list? */
> +static bool __init exists_ordered_lsm(struct lsm_info *lsm)
> +{
> + struct lsm_info **check;
> +
> + for (check = ordered_lsms; *check; check++)
> + if (*check == lsm)
> + return true;
> +
> + return false;
> +}
> +
> +/* Append an LSM to the list of ordered LSMs to initialize. */
> +static int last_lsm __initdata;
> +static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
> +{
> + /* Ignore duplicate selections. */
> + if (exists_ordered_lsm(lsm))
> + return;
> +
> + if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from))
> + return;
> +
> + /* Enable this LSM, if it is not already set. */
> + if (!lsm->enabled)
> + lsm->enabled = &lsm_enabled_true;
> + ordered_lsms[last_lsm++] = lsm;
> +
> + init_debug("%s ordered: %s (%s)\n", from, lsm->name,
> + is_enabled(lsm) ? "enabled" : "disabled");
> +}
> +
> +/* Is an LSM allowed to be initialized? */
> +static bool __init lsm_allowed(struct lsm_info *lsm)
> +{
> + /* Skip if the LSM is disabled. */
> + if (!is_enabled(lsm))
> + return false;
> +
> + /* Not allowed if another exclusive LSM already initialized. */
> + if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
> + init_debug("exclusive disabled: %s\n", lsm->name);
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static void __init lsm_set_blob_size(int *need, int *lbs)
> +{
> + int offset;
> +
> + if (*need <= 0)
> + return;
> +
> + offset = ALIGN(*lbs, sizeof(void *));
> + *lbs = offset + *need;
> + *need = offset;
> +}
> +
> +static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
> +{
> + if (!needed)
> + return;
> +
> + lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
> + lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
> + lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
> + /*
> + * The inode blob gets an rcu_head in addition to
> + * what the modules might need.
> + */
> + if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
> + blob_sizes.lbs_inode = sizeof(struct rcu_head);
> + lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
> + lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
> + lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key);
> + lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
> + lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event);
> + lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
> + lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
> + lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
> + lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
> + lsm_set_blob_size(&needed->lbs_xattr_count,
> + &blob_sizes.lbs_xattr_count);
> + lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
> + lsm_set_blob_size(&needed->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
> + lsm_set_blob_size(&needed->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
> + lsm_set_blob_size(&needed->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
> +}
> +
> +/* Prepare LSM for initialization. */
> +static void __init prepare_lsm(struct lsm_info *lsm)
> +{
> + int enabled = lsm_allowed(lsm);
> +
> + /* Record enablement (to handle any following exclusive LSMs). */
> + set_enabled(lsm, enabled);
> +
> + /* If enabled, do pre-initialization work. */
> + if (enabled) {
> + if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
> + exclusive = lsm;
> + init_debug("exclusive chosen: %s\n", lsm->name);
> + }
> +
> + lsm_set_blob_sizes(lsm->blobs);
> + }
> +}
> +
> +/* Initialize a given LSM, if it is enabled. */
> +static void __init initialize_lsm(struct lsm_info *lsm)
> +{
> + if (is_enabled(lsm)) {
> + int ret;
> +
> + init_debug("initializing %s\n", lsm->name);
> + ret = lsm->init();
> + WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret);
> + }
> +}
> +
> +/*
> + * Current index to use while initializing the lsm id list.
> + */
> +u32 lsm_active_cnt __ro_after_init;
> +const struct lsm_id *lsm_idlist[MAX_LSM_COUNT];
> +
> +/* Populate ordered LSMs list from comma-separated LSM name list. */
> +static void __init ordered_lsm_parse(const char *order, const char *origin)
> +{
> + struct lsm_info *lsm;
> + char *sep, *name, *next;
> +
> + /* LSM_ORDER_FIRST is always first. */
> + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + if (lsm->order == LSM_ORDER_FIRST)
> + append_ordered_lsm(lsm, " first");
> + }
> +
> + /* Process "security=", if given. */
> + if (chosen_major_lsm) {
> + struct lsm_info *major;
> +
> + /*
> + * To match the original "security=" behavior, this
> + * explicitly does NOT fallback to another Legacy Major
> + * if the selected one was separately disabled: disable
> + * all non-matching Legacy Major LSMs.
> + */
> + for (major = __start_lsm_info; major < __end_lsm_info;
> + major++) {
> + if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
> + strcmp(major->name, chosen_major_lsm) != 0) {
> + set_enabled(major, false);
> + init_debug("security=%s disabled: %s (only one legacy major LSM)\n",
> + chosen_major_lsm, major->name);
> + }
> + }
> + }
> +
> + sep = kstrdup(order, GFP_KERNEL);
> + next = sep;
> + /* Walk the list, looking for matching LSMs. */
> + while ((name = strsep(&next, ",")) != NULL) {
> + bool found = false;
> +
> + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + if (strcmp(lsm->name, name) == 0) {
> + if (lsm->order == LSM_ORDER_MUTABLE)
> + append_ordered_lsm(lsm, origin);
> + found = true;
> + }
> + }
> +
> + if (!found)
> + init_debug("%s ignored: %s (not built into kernel)\n",
> + origin, name);
> + }
> +
> + /* Process "security=", if given. */
> + if (chosen_major_lsm) {
> + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + if (exists_ordered_lsm(lsm))
> + continue;
> + if (strcmp(lsm->name, chosen_major_lsm) == 0)
> + append_ordered_lsm(lsm, "security=");
> + }
> + }
> +
> + /* LSM_ORDER_LAST is always last. */
> + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + if (lsm->order == LSM_ORDER_LAST)
> + append_ordered_lsm(lsm, " last");
> + }
> +
> + /* Disable all LSMs not in the ordered list. */
> + for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> + if (exists_ordered_lsm(lsm))
> + continue;
> + set_enabled(lsm, false);
> + init_debug("%s skipped: %s (not in requested order)\n",
> + origin, lsm->name);
> + }
> +
> + kfree(sep);
> +}
> +
> +static void __init report_lsm_order(void)
> +{
> + struct lsm_info **lsm, *early;
> + int first = 0;
> +
> + pr_info("initializing lsm=");
> +
> + /* Report each enabled LSM name, comma separated. */
> + for (early = __start_early_lsm_info;
> + early < __end_early_lsm_info; early++)
> + if (is_enabled(early))
> + pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);
> + for (lsm = ordered_lsms; *lsm; lsm++)
> + if (is_enabled(*lsm))
> + pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name);
> +
> + pr_cont("\n");
> +}
> +
> +/**
> + * lsm_early_cred - during initialization allocate a composite cred blob
> + * @cred: the cred that needs a blob
> + *
> + * Allocate the cred blob for all the modules
> + */
> +static void __init lsm_early_cred(struct cred *cred)
> +{
> + int rc = lsm_cred_alloc(cred, GFP_KERNEL);
> +
> + if (rc)
> + panic("%s: Early cred alloc failed.\n", __func__);
> +}
> +
> +/**
> + * lsm_early_task - during initialization allocate a composite task blob
> + * @task: the task that needs a blob
> + *
> + * Allocate the task blob for all the modules
> + */
> +static void __init lsm_early_task(struct task_struct *task)
> +{
> + int rc = lsm_task_alloc(task);
> +
> + if (rc)
> + panic("%s: Early task alloc failed.\n", __func__);
> +}
> +
> +static void __init ordered_lsm_init(void)
> +{
> + struct lsm_info **lsm;
> +
> + if (chosen_lsm_order) {
> + if (chosen_major_lsm) {
> + pr_warn("security=%s is ignored because it is superseded by lsm=%s\n",
> + chosen_major_lsm, chosen_lsm_order);
> + chosen_major_lsm = NULL;
> + }
> + ordered_lsm_parse(chosen_lsm_order, "cmdline");
> + } else
> + ordered_lsm_parse(builtin_lsm_order, "builtin");
> +
> + for (lsm = ordered_lsms; *lsm; lsm++)
> + prepare_lsm(*lsm);
> +
> + report_lsm_order();
> +
> + init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
> + init_debug("file blob size = %d\n", blob_sizes.lbs_file);
> + init_debug("ib blob size = %d\n", blob_sizes.lbs_ib);
> + init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
> + init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
> +#ifdef CONFIG_KEYS
> + init_debug("key blob size = %d\n", blob_sizes.lbs_key);
> +#endif /* CONFIG_KEYS */
> + init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
> + init_debug("sock blob size = %d\n", blob_sizes.lbs_sock);
> + init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
> + init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event);
> + init_debug("task blob size = %d\n", blob_sizes.lbs_task);
> + init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev);
> + init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count);
> + init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev);
> + init_debug("bpf map blob size = %d\n", blob_sizes.lbs_bpf_map);
> + init_debug("bpf prog blob size = %d\n", blob_sizes.lbs_bpf_prog);
> + init_debug("bpf token blob size = %d\n", blob_sizes.lbs_bpf_token);
> +
> + /*
> + * Create any kmem_caches needed for blobs
> + */
> + if (blob_sizes.lbs_file)
> + lsm_file_cache = kmem_cache_create("lsm_file_cache",
> + blob_sizes.lbs_file, 0,
> + SLAB_PANIC, NULL);
> + if (blob_sizes.lbs_inode)
> + lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
> + blob_sizes.lbs_inode, 0,
> + SLAB_PANIC, NULL);
> +
> + lsm_early_cred((struct cred *) current->cred);
> + lsm_early_task(current);
> + for (lsm = ordered_lsms; *lsm; lsm++)
> + initialize_lsm(*lsm);
> +}
> +
> +static bool match_last_lsm(const char *list, const char *lsm)
> +{
> + const char *last;
> +
> + if (WARN_ON(!list || !lsm))
> + return false;
> + last = strrchr(list, ',');
> + if (last)
> + /* Pass the comma, strcmp() will check for '\0' */
> + last++;
> + else
> + last = list;
> + return !strcmp(last, lsm);
> +}
> +
> +static int lsm_append(const char *new, char **result)
> +{
> + char *cp;
> +
> + if (*result == NULL) {
> + *result = kstrdup(new, GFP_KERNEL);
> + if (*result == NULL)
> + return -ENOMEM;
> + } else {
> + /* Check if it is the last registered name */
> + if (match_last_lsm(*result, new))
> + return 0;
> + cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
> + if (cp == NULL)
> + return -ENOMEM;
> + kfree(*result);
> + *result = cp;
> + }
> + return 0;
> +}
> +
> +static void __init lsm_static_call_init(struct security_hook_list *hl)
> +{
> + struct lsm_static_call *scall = hl->scalls;
> + int i;
> +
> + for (i = 0; i < MAX_LSM_COUNT; i++) {
> + /* Update the first static call that is not used yet */
> + if (!scall->hl) {
> + __static_call_update(scall->key, scall->trampoline,
> + hl->hook.lsm_func_addr);
> + scall->hl = hl;
> + static_branch_enable(scall->active);
> + return;
> + }
> + scall++;
> + }
> + panic("%s - Ran out of static slots.\n", __func__);
> +}
> +
> +/**
> + * security_add_hooks - Add a modules hooks to the hook lists.
> + * @hooks: the hooks to add
> + * @count: the number of hooks to add
> + * @lsmid: the identification information for the security module
> + *
> + * Each LSM has to register its hooks with the infrastructure.
> + */
> +void __init security_add_hooks(struct security_hook_list *hooks, int count,
> + const struct lsm_id *lsmid)
> +{
> + int i;
> +
> + /*
> + * A security module may call security_add_hooks() more
> + * than once during initialization, and LSM initialization
> + * is serialized. Landlock is one such case.
> + * Look at the previous entry, if there is one, for duplication.
> + */
> + if (lsm_active_cnt == 0 || lsm_idlist[lsm_active_cnt - 1] != lsmid) {
> + if (lsm_active_cnt >= MAX_LSM_COUNT)
> + panic("%s Too many LSMs registered.\n", __func__);
> + lsm_idlist[lsm_active_cnt++] = lsmid;
> + }
> +
> + for (i = 0; i < count; i++) {
> + hooks[i].lsmid = lsmid;
> + lsm_static_call_init(&hooks[i]);
> + }
> +
> + /*
> + * Don't try to append during early_security_init(), we'll come back
> + * and fix this up afterwards.
> + */
> + if (slab_is_available()) {
> + if (lsm_append(lsmid->name, &lsm_names) < 0)
> + panic("%s - Cannot get early memory.\n", __func__);
> + }
> +}
> +
> +int __init early_security_init(void)
> +{
> + struct lsm_info *lsm;
> +
> + for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
> + if (!lsm->enabled)
> + lsm->enabled = &lsm_enabled_true;
> + prepare_lsm(lsm);
> + initialize_lsm(lsm);
> + }
> +
> + return 0;
> +}
> +
> +/**
> + * security_init - initializes the security framework
> + *
> + * This should be called early in the kernel initialization sequence.
> + */
> +int __init security_init(void)
> +{
> + struct lsm_info *lsm;
> +
> + init_debug("legacy security=%s\n", chosen_major_lsm ? : " *unspecified*");
> + init_debug(" CONFIG_LSM=%s\n", builtin_lsm_order);
> + init_debug("boot arg lsm=%s\n", chosen_lsm_order ? : " *unspecified*");
> +
> + /*
> + * Append the names of the early LSM modules now that kmalloc() is
> + * available
> + */
> + for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
> + init_debug(" early started: %s (%s)\n", lsm->name,
> + is_enabled(lsm) ? "enabled" : "disabled");
> + if (lsm->enabled)
> + lsm_append(lsm->name, &lsm_names);
> + }
> +
> + /* Load LSMs in specified order. */
> + ordered_lsm_init();
> +
> + return 0;
> +}
> diff --git a/security/security.c b/security/security.c
> index 8cb049bebc57..ff6da6735e2a 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -32,24 +32,7 @@
> #include <net/flow.h>
> #include <net/sock.h>
>
> -#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX
> -
> -/*
> - * Identifier for the LSM static calls.
> - * HOOK is an LSM hook as defined in linux/lsm_hookdefs.h
> - * IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT
> - */
> -#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX
> -
> -/*
> - * Call the macro M for each LSM hook MAX_LSM_COUNT times.
> - */
> -#define LSM_LOOP_UNROLL(M, ...) \
> -do { \
> - UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__) \
> -} while (0)
> -
> -#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)
> +#include "lsm.h"
>
> /*
> * These are descriptions of the reasons that can be passed to the
> @@ -90,21 +73,29 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
> [LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
> };
>
> -static struct kmem_cache *lsm_file_cache;
> -static struct kmem_cache *lsm_inode_cache;
> +struct lsm_blob_sizes blob_sizes;
>
> -char *lsm_names;
> -static struct lsm_blob_sizes blob_sizes __ro_after_init;
> +struct kmem_cache *lsm_file_cache;
> +struct kmem_cache *lsm_inode_cache;
>
> -/* Boot-time LSM user choice */
> -static __initdata const char *chosen_lsm_order;
> -static __initdata const char *chosen_major_lsm;
> +#define SECURITY_HOOK_ACTIVE_KEY(HOOK, IDX) security_hook_active_##HOOK##_##IDX
>
> -static __initconst const char *const builtin_lsm_order = CONFIG_LSM;
> +/*
> + * Identifier for the LSM static calls.
> + * HOOK is an LSM hook as defined in linux/lsm_hookdefs.h
> + * IDX is the index of the static call. 0 <= NUM < MAX_LSM_COUNT
> + */
> +#define LSM_STATIC_CALL(HOOK, IDX) lsm_static_call_##HOOK##_##IDX
>
> -/* Ordered list of LSMs to initialize. */
> -static __initdata struct lsm_info *ordered_lsms[MAX_LSM_COUNT + 1];
> -static __initdata struct lsm_info *exclusive;
> +/*
> + * Call the macro M for each LSM hook MAX_LSM_COUNT times.
> + */
> +#define LSM_LOOP_UNROLL(M, ...) \
> +do { \
> + UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__) \
> +} while (0)
> +
> +#define LSM_DEFINE_UNROLL(M, ...) UNROLL(MAX_LSM_COUNT, M, __VA_ARGS__)
>
> #ifdef CONFIG_HAVE_STATIC_CALL
> #define LSM_HOOK_TRAMP(NAME, NUM) \
> @@ -155,496 +146,25 @@ struct lsm_static_calls_table
> #undef INIT_LSM_STATIC_CALL
> };
>
> -static __initdata bool debug;
> -#define init_debug(...) \
> - do { \
> - if (debug) \
> - pr_info(__VA_ARGS__); \
> - } while (0)
> -
> -static bool __init is_enabled(struct lsm_info *lsm)
> -{
> - if (!lsm->enabled)
> - return false;
> -
> - return *lsm->enabled;
> -}
> -
> -/* Mark an LSM's enabled flag. */
> -static int lsm_enabled_true __initdata = 1;
> -static int lsm_enabled_false __initdata = 0;
> -static void __init set_enabled(struct lsm_info *lsm, bool enabled)
> -{
> - /*
> - * When an LSM hasn't configured an enable variable, we can use
> - * a hard-coded location for storing the default enabled state.
> - */
> - if (!lsm->enabled) {
> - if (enabled)
> - lsm->enabled = &lsm_enabled_true;
> - else
> - lsm->enabled = &lsm_enabled_false;
> - } else if (lsm->enabled == &lsm_enabled_true) {
> - if (!enabled)
> - lsm->enabled = &lsm_enabled_false;
> - } else if (lsm->enabled == &lsm_enabled_false) {
> - if (enabled)
> - lsm->enabled = &lsm_enabled_true;
> - } else {
> - *lsm->enabled = enabled;
> - }
> -}
> -
> -/* Is an LSM already listed in the ordered LSMs list? */
> -static bool __init exists_ordered_lsm(struct lsm_info *lsm)
> -{
> - struct lsm_info **check;
> -
> - for (check = ordered_lsms; *check; check++)
> - if (*check == lsm)
> - return true;
> -
> - return false;
> -}
> -
> -/* Append an LSM to the list of ordered LSMs to initialize. */
> -static int last_lsm __initdata;
> -static void __init append_ordered_lsm(struct lsm_info *lsm, const char *from)
> -{
> - /* Ignore duplicate selections. */
> - if (exists_ordered_lsm(lsm))
> - return;
> -
> - if (WARN(last_lsm == MAX_LSM_COUNT, "%s: out of LSM static calls!?\n", from))
> - return;
> -
> - /* Enable this LSM, if it is not already set. */
> - if (!lsm->enabled)
> - lsm->enabled = &lsm_enabled_true;
> - ordered_lsms[last_lsm++] = lsm;
> -
> - init_debug("%s ordered: %s (%s)\n", from, lsm->name,
> - is_enabled(lsm) ? "enabled" : "disabled");
> -}
> -
> -/* Is an LSM allowed to be initialized? */
> -static bool __init lsm_allowed(struct lsm_info *lsm)
> -{
> - /* Skip if the LSM is disabled. */
> - if (!is_enabled(lsm))
> - return false;
> -
> - /* Not allowed if another exclusive LSM already initialized. */
> - if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && exclusive) {
> - init_debug("exclusive disabled: %s\n", lsm->name);
> - return false;
> - }
> -
> - return true;
> -}
> -
> -static void __init lsm_set_blob_size(int *need, int *lbs)
> -{
> - int offset;
> -
> - if (*need <= 0)
> - return;
> -
> - offset = ALIGN(*lbs, sizeof(void *));
> - *lbs = offset + *need;
> - *need = offset;
> -}
> -
> -static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
> -{
> - if (!needed)
> - return;
> -
> - lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
> - lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
> - lsm_set_blob_size(&needed->lbs_ib, &blob_sizes.lbs_ib);
> - /*
> - * The inode blob gets an rcu_head in addition to
> - * what the modules might need.
> - */
> - if (needed->lbs_inode && blob_sizes.lbs_inode == 0)
> - blob_sizes.lbs_inode = sizeof(struct rcu_head);
> - lsm_set_blob_size(&needed->lbs_inode, &blob_sizes.lbs_inode);
> - lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
> - lsm_set_blob_size(&needed->lbs_key, &blob_sizes.lbs_key);
> - lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
> - lsm_set_blob_size(&needed->lbs_perf_event, &blob_sizes.lbs_perf_event);
> - lsm_set_blob_size(&needed->lbs_sock, &blob_sizes.lbs_sock);
> - lsm_set_blob_size(&needed->lbs_superblock, &blob_sizes.lbs_superblock);
> - lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
> - lsm_set_blob_size(&needed->lbs_tun_dev, &blob_sizes.lbs_tun_dev);
> - lsm_set_blob_size(&needed->lbs_xattr_count,
> - &blob_sizes.lbs_xattr_count);
> - lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
> - lsm_set_blob_size(&needed->lbs_bpf_map, &blob_sizes.lbs_bpf_map);
> - lsm_set_blob_size(&needed->lbs_bpf_prog, &blob_sizes.lbs_bpf_prog);
> - lsm_set_blob_size(&needed->lbs_bpf_token, &blob_sizes.lbs_bpf_token);
> -}
> -
> -/* Prepare LSM for initialization. */
> -static void __init prepare_lsm(struct lsm_info *lsm)
> -{
> - int enabled = lsm_allowed(lsm);
> -
> - /* Record enablement (to handle any following exclusive LSMs). */
> - set_enabled(lsm, enabled);
> -
> - /* If enabled, do pre-initialization work. */
> - if (enabled) {
> - if ((lsm->flags & LSM_FLAG_EXCLUSIVE) && !exclusive) {
> - exclusive = lsm;
> - init_debug("exclusive chosen: %s\n", lsm->name);
> - }
> -
> - lsm_set_blob_sizes(lsm->blobs);
> - }
> -}
> -
> -/* Initialize a given LSM, if it is enabled. */
> -static void __init initialize_lsm(struct lsm_info *lsm)
> -{
> - if (is_enabled(lsm)) {
> - int ret;
> -
> - init_debug("initializing %s\n", lsm->name);
> - ret = lsm->init();
> - WARN(ret, "%s failed to initialize: %d\n", lsm->name, ret);
> - }
> -}
> -
> -/*
> - * Current index to use while initializing the lsm id list.
> - */
> -u32 lsm_active_cnt __ro_after_init;
> -const struct lsm_id *lsm_idlist[MAX_LSM_COUNT];
> -
> -/* Populate ordered LSMs list from comma-separated LSM name list. */
> -static void __init ordered_lsm_parse(const char *order, const char *origin)
> -{
> - struct lsm_info *lsm;
> - char *sep, *name, *next;
> -
> - /* LSM_ORDER_FIRST is always first. */
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> - if (lsm->order == LSM_ORDER_FIRST)
> - append_ordered_lsm(lsm, " first");
> - }
> -
> - /* Process "security=", if given. */
> - if (chosen_major_lsm) {
> - struct lsm_info *major;
> -
> - /*
> - * To match the original "security=" behavior, this
> - * explicitly does NOT fallback to another Legacy Major
> - * if the selected one was separately disabled: disable
> - * all non-matching Legacy Major LSMs.
> - */
> - for (major = __start_lsm_info; major < __end_lsm_info;
> - major++) {
> - if ((major->flags & LSM_FLAG_LEGACY_MAJOR) &&
> - strcmp(major->name, chosen_major_lsm) != 0) {
> - set_enabled(major, false);
> - init_debug("security=%s disabled: %s (only one legacy major LSM)\n",
> - chosen_major_lsm, major->name);
> - }
> - }
> - }
> -
> - sep = kstrdup(order, GFP_KERNEL);
> - next = sep;
> - /* Walk the list, looking for matching LSMs. */
> - while ((name = strsep(&next, ",")) != NULL) {
> - bool found = false;
> -
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> - if (strcmp(lsm->name, name) == 0) {
> - if (lsm->order == LSM_ORDER_MUTABLE)
> - append_ordered_lsm(lsm, origin);
> - found = true;
> - }
> - }
> -
> - if (!found)
> - init_debug("%s ignored: %s (not built into kernel)\n",
> - origin, name);
> - }
> -
> - /* Process "security=", if given. */
> - if (chosen_major_lsm) {
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> - if (exists_ordered_lsm(lsm))
> - continue;
> - if (strcmp(lsm->name, chosen_major_lsm) == 0)
> - append_ordered_lsm(lsm, "security=");
> - }
> - }
> -
> - /* LSM_ORDER_LAST is always last. */
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> - if (lsm->order == LSM_ORDER_LAST)
> - append_ordered_lsm(lsm, " last");
> - }
> -
> - /* Disable all LSMs not in the ordered list. */
> - for (lsm = __start_lsm_info; lsm < __end_lsm_info; lsm++) {
> - if (exists_ordered_lsm(lsm))
> - continue;
> - set_enabled(lsm, false);
> - init_debug("%s skipped: %s (not in requested order)\n",
> - origin, lsm->name);
> - }
> -
> - kfree(sep);
> -}
> -
> -static void __init lsm_static_call_init(struct security_hook_list *hl)
> -{
> - struct lsm_static_call *scall = hl->scalls;
> - int i;
> -
> - for (i = 0; i < MAX_LSM_COUNT; i++) {
> - /* Update the first static call that is not used yet */
> - if (!scall->hl) {
> - __static_call_update(scall->key, scall->trampoline,
> - hl->hook.lsm_func_addr);
> - scall->hl = hl;
> - static_branch_enable(scall->active);
> - return;
> - }
> - scall++;
> - }
> - panic("%s - Ran out of static slots.\n", __func__);
> -}
> -
> -static void __init lsm_early_cred(struct cred *cred);
> -static void __init lsm_early_task(struct task_struct *task);
> -
> -static int lsm_append(const char *new, char **result);
> -
> -static void __init report_lsm_order(void)
> -{
> - struct lsm_info **lsm, *early;
> - int first = 0;
> -
> - pr_info("initializing lsm=");
> -
> - /* Report each enabled LSM name, comma separated. */
> - for (early = __start_early_lsm_info;
> - early < __end_early_lsm_info; early++)
> - if (is_enabled(early))
> - pr_cont("%s%s", first++ == 0 ? "" : ",", early->name);
> - for (lsm = ordered_lsms; *lsm; lsm++)
> - if (is_enabled(*lsm))
> - pr_cont("%s%s", first++ == 0 ? "" : ",", (*lsm)->name);
> -
> - pr_cont("\n");
> -}
> -
> -static void __init ordered_lsm_init(void)
> -{
> - struct lsm_info **lsm;
> -
> - if (chosen_lsm_order) {
> - if (chosen_major_lsm) {
> - pr_warn("security=%s is ignored because it is superseded by lsm=%s\n",
> - chosen_major_lsm, chosen_lsm_order);
> - chosen_major_lsm = NULL;
> - }
> - ordered_lsm_parse(chosen_lsm_order, "cmdline");
> - } else
> - ordered_lsm_parse(builtin_lsm_order, "builtin");
> -
> - for (lsm = ordered_lsms; *lsm; lsm++)
> - prepare_lsm(*lsm);
> -
> - report_lsm_order();
> -
> - init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
> - init_debug("file blob size = %d\n", blob_sizes.lbs_file);
> - init_debug("ib blob size = %d\n", blob_sizes.lbs_ib);
> - init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
> - init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
> -#ifdef CONFIG_KEYS
> - init_debug("key blob size = %d\n", blob_sizes.lbs_key);
> -#endif /* CONFIG_KEYS */
> - init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
> - init_debug("sock blob size = %d\n", blob_sizes.lbs_sock);
> - init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
> - init_debug("perf event blob size = %d\n", blob_sizes.lbs_perf_event);
> - init_debug("task blob size = %d\n", blob_sizes.lbs_task);
> - init_debug("tun device blob size = %d\n", blob_sizes.lbs_tun_dev);
> - init_debug("xattr slots = %d\n", blob_sizes.lbs_xattr_count);
> - init_debug("bdev blob size = %d\n", blob_sizes.lbs_bdev);
> - init_debug("bpf map blob size = %d\n", blob_sizes.lbs_bpf_map);
> - init_debug("bpf prog blob size = %d\n", blob_sizes.lbs_bpf_prog);
> - init_debug("bpf token blob size = %d\n", blob_sizes.lbs_bpf_token);
> -
> - /*
> - * Create any kmem_caches needed for blobs
> - */
> - if (blob_sizes.lbs_file)
> - lsm_file_cache = kmem_cache_create("lsm_file_cache",
> - blob_sizes.lbs_file, 0,
> - SLAB_PANIC, NULL);
> - if (blob_sizes.lbs_inode)
> - lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
> - blob_sizes.lbs_inode, 0,
> - SLAB_PANIC, NULL);
> -
> - lsm_early_cred((struct cred *) current->cred);
> - lsm_early_task(current);
> - for (lsm = ordered_lsms; *lsm; lsm++)
> - initialize_lsm(*lsm);
> -}
> -
> -int __init early_security_init(void)
> -{
> - struct lsm_info *lsm;
> -
> - for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
> - if (!lsm->enabled)
> - lsm->enabled = &lsm_enabled_true;
> - prepare_lsm(lsm);
> - initialize_lsm(lsm);
> - }
> -
> - return 0;
> -}
> -
> /**
> - * security_init - initializes the security framework
> + * lsm_file_alloc - allocate a composite file blob
> + * @file: the file that needs a blob
> *
> - * This should be called early in the kernel initialization sequence.
> - */
> -int __init security_init(void)
> -{
> - struct lsm_info *lsm;
> -
> - init_debug("legacy security=%s\n", chosen_major_lsm ? : " *unspecified*");
> - init_debug(" CONFIG_LSM=%s\n", builtin_lsm_order);
> - init_debug("boot arg lsm=%s\n", chosen_lsm_order ? : " *unspecified*");
> -
> - /*
> - * Append the names of the early LSM modules now that kmalloc() is
> - * available
> - */
> - for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
> - init_debug(" early started: %s (%s)\n", lsm->name,
> - is_enabled(lsm) ? "enabled" : "disabled");
> - if (lsm->enabled)
> - lsm_append(lsm->name, &lsm_names);
> - }
> -
> - /* Load LSMs in specified order. */
> - ordered_lsm_init();
> -
> - return 0;
> -}
> -
> -/* Save user chosen LSM */
> -static int __init choose_major_lsm(char *str)
> -{
> - chosen_major_lsm = str;
> - return 1;
> -}
> -__setup("security=", choose_major_lsm);
> -
> -/* Explicitly choose LSM initialization order. */
> -static int __init choose_lsm_order(char *str)
> -{
> - chosen_lsm_order = str;
> - return 1;
> -}
> -__setup("lsm=", choose_lsm_order);
> -
> -/* Enable LSM order debugging. */
> -static int __init enable_debug(char *str)
> -{
> - debug = true;
> - return 1;
> -}
> -__setup("lsm.debug", enable_debug);
> -
> -static bool match_last_lsm(const char *list, const char *lsm)
> -{
> - const char *last;
> -
> - if (WARN_ON(!list || !lsm))
> - return false;
> - last = strrchr(list, ',');
> - if (last)
> - /* Pass the comma, strcmp() will check for '\0' */
> - last++;
> - else
> - last = list;
> - return !strcmp(last, lsm);
> -}
> -
> -static int lsm_append(const char *new, char **result)
> -{
> - char *cp;
> -
> - if (*result == NULL) {
> - *result = kstrdup(new, GFP_KERNEL);
> - if (*result == NULL)
> - return -ENOMEM;
> - } else {
> - /* Check if it is the last registered name */
> - if (match_last_lsm(*result, new))
> - return 0;
> - cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new);
> - if (cp == NULL)
> - return -ENOMEM;
> - kfree(*result);
> - *result = cp;
> - }
> - return 0;
> -}
> -
> -/**
> - * security_add_hooks - Add a modules hooks to the hook lists.
> - * @hooks: the hooks to add
> - * @count: the number of hooks to add
> - * @lsmid: the identification information for the security module
> + * Allocate the file blob for all the modules
> *
> - * Each LSM has to register its hooks with the infrastructure.
> + * Returns 0, or -ENOMEM if memory can't be allocated.
> */
> -void __init security_add_hooks(struct security_hook_list *hooks, int count,
> - const struct lsm_id *lsmid)
> +static int lsm_file_alloc(struct file *file)
> {
> - int i;
> -
> - /*
> - * A security module may call security_add_hooks() more
> - * than once during initialization, and LSM initialization
> - * is serialized. Landlock is one such case.
> - * Look at the previous entry, if there is one, for duplication.
> - */
> - if (lsm_active_cnt == 0 || lsm_idlist[lsm_active_cnt - 1] != lsmid) {
> - if (lsm_active_cnt >= MAX_LSM_COUNT)
> - panic("%s Too many LSMs registered.\n", __func__);
> - lsm_idlist[lsm_active_cnt++] = lsmid;
> + if (!lsm_file_cache) {
> + file->f_security = NULL;
> + return 0;
> }
>
> - for (i = 0; i < count; i++) {
> - hooks[i].lsmid = lsmid;
> - lsm_static_call_init(&hooks[i]);
> - }
> -
> - /*
> - * Don't try to append during early_security_init(), we'll come back
> - * and fix this up afterwards.
> - */
> - if (slab_is_available()) {
> - if (lsm_append(lsmid->name, &lsm_names) < 0)
> - panic("%s - Cannot get early memory.\n", __func__);
> - }
> + file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL);
> + if (file->f_security == NULL)
> + return -ENOMEM;
> + return 0;
> }
>
> /**
> @@ -679,46 +199,11 @@ static int lsm_blob_alloc(void **dest, size_t size, gfp_t gfp)
> *
> * Returns 0, or -ENOMEM if memory can't be allocated.
> */
> -static int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
> +int lsm_cred_alloc(struct cred *cred, gfp_t gfp)
> {
> return lsm_blob_alloc(&cred->security, blob_sizes.lbs_cred, gfp);
> }
>
> -/**
> - * lsm_early_cred - during initialization allocate a composite cred blob
> - * @cred: the cred that needs a blob
> - *
> - * Allocate the cred blob for all the modules
> - */
> -static void __init lsm_early_cred(struct cred *cred)
> -{
> - int rc = lsm_cred_alloc(cred, GFP_KERNEL);
> -
> - if (rc)
> - panic("%s: Early cred alloc failed.\n", __func__);
> -}
> -
> -/**
> - * lsm_file_alloc - allocate a composite file blob
> - * @file: the file that needs a blob
> - *
> - * Allocate the file blob for all the modules
> - *
> - * Returns 0, or -ENOMEM if memory can't be allocated.
> - */
> -static int lsm_file_alloc(struct file *file)
> -{
> - if (!lsm_file_cache) {
> - file->f_security = NULL;
> - return 0;
> - }
> -
> - file->f_security = kmem_cache_zalloc(lsm_file_cache, GFP_KERNEL);
> - if (file->f_security == NULL)
> - return -ENOMEM;
> - return 0;
> -}
> -
> /**
> * lsm_inode_alloc - allocate a composite inode blob
> * @inode: the inode that needs a blob
> @@ -749,7 +234,7 @@ static int lsm_inode_alloc(struct inode *inode, gfp_t gfp)
> *
> * Returns 0, or -ENOMEM if memory can't be allocated.
> */
> -static int lsm_task_alloc(struct task_struct *task)
> +int lsm_task_alloc(struct task_struct *task)
> {
> return lsm_blob_alloc(&task->security, blob_sizes.lbs_task, GFP_KERNEL);
> }
> @@ -851,20 +336,6 @@ static int lsm_bpf_token_alloc(struct bpf_token *token)
> }
> #endif /* CONFIG_BPF_SYSCALL */
>
> -/**
> - * lsm_early_task - during initialization allocate a composite task blob
> - * @task: the task that needs a blob
> - *
> - * Allocate the task blob for all the modules
> - */
> -static void __init lsm_early_task(struct task_struct *task)
> -{
> - int rc = lsm_task_alloc(task);
> -
> - if (rc)
> - panic("%s: Early task alloc failed.\n", __func__);
> -}
> -
> /**
> * lsm_superblock_alloc - allocate a composite superblock blob
> * @sb: the superblock that needs a blob
^ permalink raw reply
* Re: [PATCH v4 01/34] lsm: split the notifier code out into lsm_notifier.c
From: Mimi Zohar @ 2025-09-19 10:44 UTC (permalink / raw)
To: Paul Moore, linux-security-module, linux-integrity, selinux
Cc: John Johansen, Roberto Sassu, Fan Wu, Mickaël Salaün,
Günther Noack, Kees Cook, Micah Morton, Casey Schaufler,
Tetsuo Handa, Nicolas Bouchinet, Xiu Jianfeng
In-Reply-To: <20250916220355.252592-37-paul@paul-moore.com>
On Tue, 2025-09-16 at 18:03 -0400, Paul Moore wrote:
> In an effort to decompose security/security.c somewhat to make it less
> twisted and unwieldy, pull out the LSM notifier code into a new file
> as it is fairly well self-contained.
>
> No code changes.
>
> Reviewed-by: Kees Cook <kees@kernel.org>
> Reviewed-by: John Johansen <john.johansen@canonical.com>
> Reviewed-by: Casey Schaufler <casey@schaufler-ca.com>
> Signed-off-by: Paul Moore <paul@paul-moore.com>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
> ---
> security/Makefile | 2 +-
> security/lsm_notifier.c | 31 +++++++++++++++++++++++++++++++
> security/security.c | 23 -----------------------
> 3 files changed, 32 insertions(+), 24 deletions(-)
> create mode 100644 security/lsm_notifier.c
>
> diff --git a/security/Makefile b/security/Makefile
> index 22ff4c8bd8ce..14d87847bce8 100644
> --- a/security/Makefile
> +++ b/security/Makefile
> @@ -11,7 +11,7 @@ obj-$(CONFIG_SECURITY) += lsm_syscalls.o
> obj-$(CONFIG_MMU) += min_addr.o
>
> # Object file lists
> -obj-$(CONFIG_SECURITY) += security.o
> +obj-$(CONFIG_SECURITY) += security.o lsm_notifier.o
> obj-$(CONFIG_SECURITYFS) += inode.o
> obj-$(CONFIG_SECURITY_SELINUX) += selinux/
> obj-$(CONFIG_SECURITY_SMACK) += smack/
> diff --git a/security/lsm_notifier.c b/security/lsm_notifier.c
> new file mode 100644
> index 000000000000..c92fad5d57d4
> --- /dev/null
> +++ b/security/lsm_notifier.c
> @@ -0,0 +1,31 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * LSM notifier functions
> + *
> + */
> +
> +#include <linux/notifier.h>
> +#include <linux/security.h>
> +
> +static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
> +
> +int call_blocking_lsm_notifier(enum lsm_event event, void *data)
> +{
> + return blocking_notifier_call_chain(&blocking_lsm_notifier_chain,
> + event, data);
> +}
> +EXPORT_SYMBOL(call_blocking_lsm_notifier);
> +
> +int register_blocking_lsm_notifier(struct notifier_block *nb)
> +{
> + return blocking_notifier_chain_register(&blocking_lsm_notifier_chain,
> + nb);
> +}
> +EXPORT_SYMBOL(register_blocking_lsm_notifier);
> +
> +int unregister_blocking_lsm_notifier(struct notifier_block *nb)
> +{
> + return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain,
> + nb);
> +}
> +EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
> diff --git a/security/security.c b/security/security.c
> index ca126b02d2fe..8cb049bebc57 100644
> --- a/security/security.c
> +++ b/security/security.c
> @@ -90,8 +90,6 @@ const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX + 1] = {
> [LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
> };
>
> -static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
> -
> static struct kmem_cache *lsm_file_cache;
> static struct kmem_cache *lsm_inode_cache;
>
> @@ -649,27 +647,6 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
> }
> }
>
> -int call_blocking_lsm_notifier(enum lsm_event event, void *data)
> -{
> - return blocking_notifier_call_chain(&blocking_lsm_notifier_chain,
> - event, data);
> -}
> -EXPORT_SYMBOL(call_blocking_lsm_notifier);
> -
> -int register_blocking_lsm_notifier(struct notifier_block *nb)
> -{
> - return blocking_notifier_chain_register(&blocking_lsm_notifier_chain,
> - nb);
> -}
> -EXPORT_SYMBOL(register_blocking_lsm_notifier);
> -
> -int unregister_blocking_lsm_notifier(struct notifier_block *nb)
> -{
> - return blocking_notifier_chain_unregister(&blocking_lsm_notifier_chain,
> - nb);
> -}
> -EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
> -
> /**
> * lsm_blob_alloc - allocate a composite blob
> * @dest: the destination for the blob
^ permalink raw reply
* Re: [PATCH v3 00/35] Compiler-Based Capability- and Locking-Analysis
From: Marco Elver @ 2025-09-19 9:10 UTC (permalink / raw)
To: Linus Torvalds
Cc: Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon,
David S. Miller, Luc Van Oostenryck, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Bill Wendling, Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
Jann Horn, Joel Fernandes, Jonathan Corbet, Josh Triplett,
Justin Stitt, Kees Cook, Kentaro Takeda, Lukas Bulwahn,
Mark Rutland, Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
Neeraj Upadhyay, Nick Desaulniers, Steven Rostedt, Tetsuo Handa,
Thomas Gleixner, Thomas Graf, Uladzislau Rezki, Waiman Long,
kasan-dev, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
linux-mm, linux-security-module, linux-sparse, llvm, rcu
In-Reply-To: <CAHk-=wgQO7c0zc8_VwaVSzG3fEVFFcjWzVBKM4jYjv8UiD2dkg@mail.gmail.com>
On Thu, Sep 18, 2025 at 02:47PM -0700, Linus Torvalds wrote:
[...]
> But I don't mind your "Context guard" notion either. I'm not loving
> it, but it's not offensive to me either.
>
> Then the language would be feel fairly straightforward,
>
> Eg:
>
> > +Context analysis is a way to specify permissibility of operations to depend on
> > +contexts being held (or not held).
>
> That "contexts being held" sounds odd, but talking about "context
> markers", or "context tokens" would seem natural.
>
> An alternative would be to not talk about markers / tokens / guards at
> all, but simply about a context being *active*.
That works for high-level descriptions, but we need something for the
API, too, which specifically operates and refers to the objects which
are acquired/released to enter/exit a context.
> IOW, instead of wording it like this:
>
> > +The set of contexts that are actually held by a given thread at a given point
> > +in program execution is a run-time concept.
>
> that talks about "being held", you could just state it in the sense of
> the "set of contexts being active", and that immediately reads fairly
> naturally, doesn't it?
>
> Because a context is a *state* you are in, it's not something you hold on to.
>
> The tokens - or whatever - would be only some internal implementation
> detail of how the compiler keeps track of which state is active, not
> the conceptual idea itself.
>
> So you name states, and you have functions to mark those context
> states as being entered or exited, but you don't really even have to
> talk about "holding" anything.
It's a tough one -- because fundamentally we operate on objects, which
when acquired/released we enter/exit some context. I tried to balance
not venturing off too far from common terminology, while keeping it
general enough to allow eventual uses for "IRQ enable/disable", "preempt
enable/disable", or anything else where we might need to enter/exit some
context to access a resource (incl. perhaps Ian's suggestion of figuring
out if we can design a refcount_t API that uses context tracking).
I went with "context guard" to refer to the objects themselves, as that
doesn't look too odd. It does match the concept of "guard" in
<linux/cleanup.h>.
See second attempt below.
Preferences?
Thanks,
-- Marco
------ >8 ------
diff --git a/Documentation/dev-tools/capability-analysis.rst b/Documentation/dev-tools/capability-analysis.rst
index 3456132261c6..87125ec2db11 100644
--- a/Documentation/dev-tools/capability-analysis.rst
+++ b/Documentation/dev-tools/capability-analysis.rst
@@ -1,81 +1,80 @@
.. SPDX-License-Identifier: GPL-2.0
.. Copyright (C) 2025, Google LLC.
-.. _capability-analysis:
+.. _context-analysis:
-Compiler-Based Capability Analysis
-==================================
+Compiler-Based Context Analysis
+===============================
-Capability analysis is a C language extension, which enables statically
-checking that user-definable "capabilities" are acquired and released where
-required. An obvious application is lock-safety checking for the kernel's
-various synchronization primitives (each of which represents a "capability"),
-and checking that locking rules are not violated.
+Context analysis is a C language extension, which enables statically checking
+that user-definable context guards are acquired and released where required. An
+obvious application is lock-safety checking for the kernel's various
+synchronization primitives (each of which represents a context guard), and
+checking that locking rules are not violated.
-The Clang compiler currently supports the full set of capability analysis
+The Clang compiler currently supports the full set of context analysis
features. To enable for Clang, configure the kernel with::
- CONFIG_WARN_CAPABILITY_ANALYSIS=y
+ CONFIG_WARN_CONTEXT_ANALYSIS=y
The feature requires Clang 22 or later.
The analysis is *opt-in by default*, and requires declaring which modules and
subsystems should be analyzed in the respective `Makefile`::
- CAPABILITY_ANALYSIS_mymodule.o := y
+ CONTEXT_ANALYSIS_mymodule.o := y
Or for all translation units in the directory::
- CAPABILITY_ANALYSIS := y
+ CONTEXT_ANALYSIS := y
It is possible to enable the analysis tree-wide, however, which will result in
numerous false positive warnings currently and is *not* generally recommended::
- CONFIG_WARN_CAPABILITY_ANALYSIS_ALL=y
+ CONFIG_WARN_CONTEXT_ANALYSIS_ALL=y
Programming Model
-----------------
-The below describes the programming model around using capability-enabled
-types.
+The below describes the programming model around using context guard types.
.. note::
- Enabling capability analysis can be seen as enabling a dialect of Linux C with
- a Capability System. Some valid patterns involving complex control-flow are
+ Enabling context analysis can be seen as enabling a dialect of Linux C with
+ a Context System. Some valid patterns involving complex control-flow are
constrained (such as conditional acquisition and later conditional release
- in the same function, or returning pointers to capabilities from functions.
+ in the same function).
-Capability analysis is a way to specify permissibility of operations to depend
-on capabilities being held (or not held). Typically we are interested in
-protecting data and code by requiring some capability to be held, for example a
-specific lock. The analysis ensures that the caller cannot perform the
-operation without holding the appropriate capability.
+Context analysis is a way to specify permissibility of operations to depend on
+context guards being held (or not held). Typically we are interested in
+protecting data and code in a critical section by requiring a specific context
+to be active, for example by holding a specific lock. The analysis ensures that
+callers cannot perform an operation without the required context being active.
-Capabilities are associated with named structs, along with functions that
-operate on capability-enabled struct instances to acquire and release the
-associated capability.
+Context guards are associated with named structs, along with functions that
+operate on struct instances to acquire and release the associated context
+guard.
-Capabilities can be held either exclusively or shared. This mechanism allows
-assign more precise privileges when holding a capability, typically to
+Context guards can be held either exclusively or shared. This mechanism allows
+assigning more precise privileges when a context is active, typically to
distinguish where a thread may only read (shared) or also write (exclusive) to
-guarded data.
+data guarded within a context.
-The set of capabilities that are actually held by a given thread at a given
-point in program execution is a run-time concept. The static analysis works by
-calculating an approximation of that set, called the capability environment.
-The capability environment is calculated for every program point, and describes
-the set of capabilities that are statically known to be held, or not held, at
-that particular point. This environment is a conservative approximation of the
-full set of capabilities that will actually held by a thread at run-time.
+The set of contexts that are actually active in a given thread at a given point
+in program execution is a run-time concept. The static analysis works by
+calculating an approximation of that set, called the context environment. The
+context environment is calculated for every program point, and describes the
+set of contexts that are statically known to be active, or inactive, at that
+particular point. This environment is a conservative approximation of the full
+set of contexts that will actually be active in a thread at run-time.
More details are also documented `here
<https://clang.llvm.org/docs/ThreadSafetyAnalysis.html>`_.
.. note::
- Clang's analysis explicitly does not infer capabilities acquired or released
- by inline functions. It requires explicit annotations to (a) assert that
- it's not a bug if a capability is released or acquired, and (b) to retain
- consistency between inline and non-inline function declarations.
+ Clang's analysis explicitly does not infer context guards acquired or
+ released by inline functions. It requires explicit annotations to (a) assert
+ that it's not a bug if a context guard is released or acquired, and (b) to
+ retain consistency between inline and non-inline function declarations.
Supported Kernel Primitives
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -85,13 +84,12 @@ Currently the following synchronization primitives are supported:
`bit_spinlock`, RCU, SRCU (`srcu_struct`), `rw_semaphore`, `local_lock_t`,
`ww_mutex`.
-For capabilities with an initialization function (e.g., `spin_lock_init()`),
-calling this function on the capability instance before initializing any
-guarded members or globals prevents the compiler from issuing warnings about
-unguarded initialization.
+For context guards with an initialization function (e.g., `spin_lock_init()`),
+calling this function before initializing any guarded members or globals
+prevents the compiler from issuing warnings about unguarded initialization.
Lockdep assertions, such as `lockdep_assert_held()`, inform the compiler's
-capability analysis that the associated synchronization primitive is held after
+context analysis that the associated synchronization primitive is held after
the assertion. This avoids false positives in complex control-flow scenarios
and encourages the use of Lockdep where static analysis is limited. For
example, this is useful when a function doesn't *always* require a lock, making
@@ -100,9 +98,9 @@ example, this is useful when a function doesn't *always* require a lock, making
Keywords
~~~~~~~~
-.. kernel-doc:: include/linux/compiler-capability-analysis.h
- :identifiers: struct_with_capability
- token_capability token_capability_instance
+.. kernel-doc:: include/linux/compiler-context-analysis.h
+ :identifiers: context_guard_struct
+ token_context_guard token_context_guard_instance
__guarded_by __pt_guarded_by
__must_hold
__must_not_hold
@@ -117,32 +115,32 @@ Keywords
__release
__acquire_shared
__release_shared
- capability_unsafe
- __capability_unsafe
- disable_capability_analysis enable_capability_analysis
+ __acquire_ret
+ __acquire_shared_ret
+ context_unsafe
+ __context_unsafe
+ disable_context_analysis enable_context_analysis
.. note::
- The function attribute `__no_capability_analysis` is reserved for internal
- implementation of capability-enabled primitives, and should be avoided in
- normal code.
+ The function attribute `__no_context_analysis` is reserved for internal
+ implementation of context guard types, and should be avoided in normal code.
Background
----------
Clang originally called the feature `Thread Safety Analysis
-<https://clang.llvm.org/docs/ThreadSafetyAnalysis.html>`_, with some
-terminology still using the thread-safety-analysis-only names. This was later
-changed and the feature became more flexible, gaining the ability to define
-custom "capabilities".
-
-Indeed, its foundations can be found in `capability systems
-<https://www.cs.cornell.edu/talc/papers/capabilities.pdf>`_, used to specify
-the permissibility of operations to depend on some capability being held (or
-not held).
+<https://clang.llvm.org/docs/ThreadSafetyAnalysis.html>`_, with some keywords
+and documentation still using the thread-safety-analysis-only terminology. This
+was later changed and the feature became more flexible, gaining the ability to
+define custom "capabilities". Its foundations can be found in `Capability
+Systems <https://www.cs.cornell.edu/talc/papers/capabilities.pdf>`_, used to
+specify the permissibility of operations to depend on some "capability" being
+held (or not held).
Because the feature is not just able to express capabilities related to
-synchronization primitives, the naming chosen for the kernel departs from
-Clang's initial "Thread Safety" nomenclature and refers to the feature as
-"Capability Analysis" to avoid confusion. The implementation still makes
-references to the older terminology in some places, such as `-Wthread-safety`
+synchronization primitives, and "capability" is already overloaded in the
+kernel, the naming chosen for the kernel departs from Clang's initial "Thread
+Safety" and "capability" nomenclature; we refer to the feature as "Context
+Analysis" to avoid confusion. The internal implementation still makes
+references to Clang's terminology in a few places, such as `-Wthread-safety`
being the warning option that also still appears in diagnostic messages.
diff --git a/include/linux/compiler-capability-analysis.h b/include/linux/compiler-capability-analysis.h
index f8a1da67589c..b3804c5ac40d 100644
--- a/include/linux/compiler-capability-analysis.h
+++ b/include/linux/compiler-capability-analysis.h
@@ -1,42 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Macros and attributes for compiler-based static capability analysis.
+ * Macros and attributes for compiler-based static context analysis.
*/
-#ifndef _LINUX_COMPILER_CAPABILITY_ANALYSIS_H
-#define _LINUX_COMPILER_CAPABILITY_ANALYSIS_H
+#ifndef _LINUX_COMPILER_CONTEXT_ANALYSIS_H
+#define _LINUX_COMPILER_CONTEXT_ANALYSIS_H
-#if defined(WARN_CAPABILITY_ANALYSIS)
+#if defined(WARN_CONTEXT_ANALYSIS)
/*
- * The below attributes are used to define new capability types. Internal only.
- */
-# define __cap_type(name) __attribute__((capability(#name)))
-# define __reentrant_cap __attribute__((reentrant_capability))
-# define __acquires_cap(...) __attribute__((acquire_capability(__VA_ARGS__)))
-# define __acquires_shared_cap(...) __attribute__((acquire_shared_capability(__VA_ARGS__)))
-# define __try_acquires_cap(ret, var) __attribute__((try_acquire_capability(ret, var)))
-# define __try_acquires_shared_cap(ret, var) __attribute__((try_acquire_shared_capability(ret, var)))
-# define __releases_cap(...) __attribute__((release_capability(__VA_ARGS__)))
-# define __releases_shared_cap(...) __attribute__((release_shared_capability(__VA_ARGS__)))
-# define __assumes_cap(...) __attribute__((assert_capability(__VA_ARGS__)))
-# define __assumes_shared_cap(...) __attribute__((assert_shared_capability(__VA_ARGS__)))
-# define __returns_cap(var) __attribute__((lock_returned(var)))
+ * These attributes define new context guard (Clang: capability) types.
+ * Internal only.
+ */
+# define __ctx_guard_type(name) __attribute__((capability(#name)))
+# define __reentrant_ctx_guard __attribute__((reentrant_capability))
+# define __acquires_ctx_guard(...) __attribute__((acquire_capability(__VA_ARGS__)))
+# define __acquires_shared_ctx_guard(...) __attribute__((acquire_shared_capability(__VA_ARGS__)))
+# define __try_acquires_ctx_guard(ret, var) __attribute__((try_acquire_capability(ret, var)))
+# define __try_acquires_shared_ctx_guard(ret, var) __attribute__((try_acquire_shared_capability(ret, var)))
+# define __releases_ctx_guard(...) __attribute__((release_capability(__VA_ARGS__)))
+# define __releases_shared_ctx_guard(...) __attribute__((release_shared_capability(__VA_ARGS__)))
+# define __assumes_ctx_guard(...) __attribute__((assert_capability(__VA_ARGS__)))
+# define __assumes_shared_ctx_guard(...) __attribute__((assert_shared_capability(__VA_ARGS__)))
+# define __returns_ctx_guard(var) __attribute__((lock_returned(var)))
/*
* The below are used to annotate code being checked. Internal only.
*/
-# define __excludes_cap(...) __attribute__((locks_excluded(__VA_ARGS__)))
-# define __requires_cap(...) __attribute__((requires_capability(__VA_ARGS__)))
-# define __requires_shared_cap(...) __attribute__((requires_shared_capability(__VA_ARGS__)))
+# define __excludes_ctx_guard(...) __attribute__((locks_excluded(__VA_ARGS__)))
+# define __requires_ctx_guard(...) __attribute__((requires_capability(__VA_ARGS__)))
+# define __requires_shared_ctx_guard(...) __attribute__((requires_shared_capability(__VA_ARGS__)))
/**
* __guarded_by - struct member and globals attribute, declares variable
- * protected by capability
+ * only accessible within active context
*
- * Declares that the struct member or global variable must be guarded by the
- * given capabilities. Read operations on the data require shared access,
- * while write operations require exclusive access.
+ * Declares that the struct member or global variable is only accessible within
+ * the context entered by the given context guard. Read operations on the data
+ * require shared access, while write operations require exclusive access.
*
* .. code-block:: c
*
@@ -49,11 +50,12 @@
/**
* __pt_guarded_by - struct member and globals attribute, declares pointed-to
- * data is protected by capability
+ * data only accessible within active context
*
* Declares that the data pointed to by the struct member pointer or global
- * pointer must be guarded by the given capabilities. Read operations on the
- * data require shared access, while write operations require exclusive access.
+ * pointer is only accessible within the context entered by the given context
+ * guard. Read operations on the data require shared access, while write
+ * operations require exclusive access.
*
* .. code-block:: c
*
@@ -65,14 +67,14 @@
# define __pt_guarded_by(...) __attribute__((pt_guarded_by(__VA_ARGS__)))
/**
- * struct_with_capability() - declare or define a capability struct
+ * context_guard_struct() - declare or define a context guard struct
* @name: struct name
*
- * Helper to declare or define a struct type with capability of the same name.
+ * Helper to declare or define a struct type that is also a context guard.
*
* .. code-block:: c
*
- * struct_with_capability(my_handle) {
+ * context_guard_struct(my_handle) {
* int foo;
* long bar;
* };
@@ -81,98 +83,98 @@
* ...
* };
* // ... declared elsewhere ...
- * struct_with_capability(some_state);
- *
- * Note: The implementation defines several helper functions that can acquire,
- * release, and assert the capability.
- */
-# define struct_with_capability(name, ...) \
- struct __cap_type(name) __VA_ARGS__ name; \
- static __always_inline void __acquire_cap(const struct name *var) \
- __attribute__((overloadable)) __no_capability_analysis __acquires_cap(var) { } \
- static __always_inline void __acquire_shared_cap(const struct name *var) \
- __attribute__((overloadable)) __no_capability_analysis __acquires_shared_cap(var) { } \
- static __always_inline bool __try_acquire_cap(const struct name *var, bool ret) \
- __attribute__((overloadable)) __no_capability_analysis __try_acquires_cap(1, var) \
+ * context_guard_struct(some_state);
+ *
+ * Note: The implementation defines several helper functions that can acquire
+ * and release the context guard.
+ */
+# define context_guard_struct(name, ...) \
+ struct __ctx_guard_type(name) __VA_ARGS__ name; \
+ static __always_inline void __acquire_ctx_guard(const struct name *var) \
+ __attribute__((overloadable)) __no_context_analysis __acquires_ctx_guard(var) { } \
+ static __always_inline void __acquire_shared_ctx_guard(const struct name *var) \
+ __attribute__((overloadable)) __no_context_analysis __acquires_shared_ctx_guard(var) { } \
+ static __always_inline bool __try_acquire_ctx_guard(const struct name *var, bool ret) \
+ __attribute__((overloadable)) __no_context_analysis __try_acquires_ctx_guard(1, var) \
{ return ret; } \
- static __always_inline bool __try_acquire_shared_cap(const struct name *var, bool ret) \
- __attribute__((overloadable)) __no_capability_analysis __try_acquires_shared_cap(1, var) \
+ static __always_inline bool __try_acquire_shared_ctx_guard(const struct name *var, bool ret) \
+ __attribute__((overloadable)) __no_context_analysis __try_acquires_shared_ctx_guard(1, var) \
{ return ret; } \
- static __always_inline void __release_cap(const struct name *var) \
- __attribute__((overloadable)) __no_capability_analysis __releases_cap(var) { } \
- static __always_inline void __release_shared_cap(const struct name *var) \
- __attribute__((overloadable)) __no_capability_analysis __releases_shared_cap(var) { } \
- static __always_inline void __assume_cap(const struct name *var) \
- __attribute__((overloadable)) __assumes_cap(var) { } \
- static __always_inline void __assume_shared_cap(const struct name *var) \
- __attribute__((overloadable)) __assumes_shared_cap(var) { } \
+ static __always_inline void __release_ctx_guard(const struct name *var) \
+ __attribute__((overloadable)) __no_context_analysis __releases_ctx_guard(var) { } \
+ static __always_inline void __release_shared_ctx_guard(const struct name *var) \
+ __attribute__((overloadable)) __no_context_analysis __releases_shared_ctx_guard(var) { } \
+ static __always_inline void __assume_ctx_guard(const struct name *var) \
+ __attribute__((overloadable)) __assumes_ctx_guard(var) { } \
+ static __always_inline void __assume_shared_ctx_guard(const struct name *var) \
+ __attribute__((overloadable)) __assumes_shared_ctx_guard(var) { } \
struct name
/**
- * disable_capability_analysis() - disables capability analysis
+ * disable_context_analysis() - disables context analysis
*
- * Disables capability analysis. Must be paired with a later
- * enable_capability_analysis().
+ * Disables context analysis. Must be paired with a later
+ * enable_context_analysis().
*/
-# define disable_capability_analysis() \
+# define disable_context_analysis() \
__diag_push(); \
__diag_ignore_all("-Wunknown-warning-option", "") \
__diag_ignore_all("-Wthread-safety", "") \
__diag_ignore_all("-Wthread-safety-pointer", "")
/**
- * enable_capability_analysis() - re-enables capability analysis
+ * enable_context_analysis() - re-enables context analysis
*
- * Re-enables capability analysis. Must be paired with a prior
- * disable_capability_analysis().
+ * Re-enables context analysis. Must be paired with a prior
+ * disable_context_analysis().
*/
-# define enable_capability_analysis() __diag_pop()
+# define enable_context_analysis() __diag_pop()
/**
- * __no_capability_analysis - function attribute, disables capability analysis
- *
- * Function attribute denoting that capability analysis is disabled for the
- * whole function. Prefer use of `capability_unsafe()` where possible.
- */
-# define __no_capability_analysis __attribute__((no_thread_safety_analysis))
-
-#else /* !WARN_CAPABILITY_ANALYSIS */
-
-# define __cap_type(name)
-# define __reentrant_cap
-# define __acquires_cap(...)
-# define __acquires_shared_cap(...)
-# define __try_acquires_cap(ret, var)
-# define __try_acquires_shared_cap(ret, var)
-# define __releases_cap(...)
-# define __releases_shared_cap(...)
-# define __assumes_cap(...)
-# define __assumes_shared_cap(...)
-# define __returns_cap(var)
+ * __no_context_analysis - function attribute, disables context analysis
+ *
+ * Function attribute denoting that context analysis is disabled for the
+ * whole function. Prefer use of `context_unsafe()` where possible.
+ */
+# define __no_context_analysis __attribute__((no_thread_safety_analysis))
+
+#else /* !WARN_CONTEXT_ANALYSIS */
+
+# define __ctx_guard_type(name)
+# define __reentrant_ctx_guard
+# define __acquires_ctx_guard(...)
+# define __acquires_shared_ctx_guard(...)
+# define __try_acquires_ctx_guard(ret, var)
+# define __try_acquires_shared_ctx_guard(ret, var)
+# define __releases_ctx_guard(...)
+# define __releases_shared_ctx_guard(...)
+# define __assumes_ctx_guard(...)
+# define __assumes_shared_ctx_guard(...)
+# define __returns_ctx_guard(var)
# define __guarded_by(...)
# define __pt_guarded_by(...)
-# define __excludes_cap(...)
-# define __requires_cap(...)
-# define __requires_shared_cap(...)
-# define __acquire_cap(var) do { } while (0)
-# define __acquire_shared_cap(var) do { } while (0)
-# define __try_acquire_cap(var, ret) (ret)
-# define __try_acquire_shared_cap(var, ret) (ret)
-# define __release_cap(var) do { } while (0)
-# define __release_shared_cap(var) do { } while (0)
-# define __assume_cap(var) do { (void)(var); } while (0)
-# define __assume_shared_cap(var) do { (void)(var); } while (0)
-# define struct_with_capability(name, ...) struct __VA_ARGS__ name
-# define disable_capability_analysis()
-# define enable_capability_analysis()
-# define __no_capability_analysis
-
-#endif /* WARN_CAPABILITY_ANALYSIS */
+# define __excludes_ctx_guard(...)
+# define __requires_ctx_guard(...)
+# define __requires_shared_ctx_guard(...)
+# define __acquire_ctx_guard(var) do { } while (0)
+# define __acquire_shared_ctx_guard(var) do { } while (0)
+# define __try_acquire_ctx_guard(var, ret) (ret)
+# define __try_acquire_shared_ctx_guard(var, ret) (ret)
+# define __release_ctx_guard(var) do { } while (0)
+# define __release_shared_ctx_guard(var) do { } while (0)
+# define __assume_ctx_guard(var) do { (void)(var); } while (0)
+# define __assume_shared_ctx_guard(var) do { (void)(var); } while (0)
+# define context_guard_struct(name, ...) struct __VA_ARGS__ name
+# define disable_context_analysis()
+# define enable_context_analysis()
+# define __no_context_analysis
+
+#endif /* WARN_CONTEXT_ANALYSIS */
/**
- * capability_unsafe() - disable capability checking for contained code
+ * context_unsafe() - disable context checking for contained code
*
- * Disables capability checking for contained statements or expression.
+ * Disables context checking for contained statements or expression.
*
* .. code-block:: c
*
@@ -186,32 +188,32 @@
* // ...
* // other code that is still checked ...
* // ...
- * return capability_unsafe(d->counter);
+ * return context_unsafe(d->counter);
* }
*/
-#define capability_unsafe(...) \
+#define context_unsafe(...) \
({ \
- disable_capability_analysis(); \
+ disable_context_analysis(); \
__VA_ARGS__; \
- enable_capability_analysis() \
+ enable_context_analysis() \
})
/**
- * __capability_unsafe() - function attribute, disable capability checking
+ * __context_unsafe() - function attribute, disable context checking
* @comment: comment explaining why opt-out is safe
*
- * Function attribute denoting that capability analysis is disabled for the
+ * Function attribute denoting that context analysis is disabled for the
* whole function. Forces adding an inline comment as argument.
*/
-#define __capability_unsafe(comment) __no_capability_analysis
+#define __context_unsafe(comment) __no_context_analysis
/**
- * capability_unsafe_alias() - helper to insert a capability "alias barrier"
- * @p: pointer aliasing a capability or object containing capabilities
+ * context_unsafe_alias() - helper to insert a context guard "alias barrier"
+ * @p: pointer aliasing a context guard or object containing context guards
*
- * No-op function that acts as a "capability alias barrier", where the analysis
- * rightfully detects that we're switching aliases, but the switch is considered
- * safe but beyond the analysis reasoning abilities.
+ * No-op function that acts as a "context guard alias barrier", where the
+ * analysis rightfully detects that we're switching aliases, but the switch is
+ * considered safe but beyond the analysis reasoning abilities.
*
* This should be inserted before the first use of such an alias.
*
@@ -219,61 +221,61 @@
* their value cannot be determined (e.g. when passing a non-const pointer to an
* alias as a function argument).
*/
-#define capability_unsafe_alias(p) _capability_unsafe_alias((void **)&(p))
-static inline void _capability_unsafe_alias(void **p) { }
+#define context_unsafe_alias(p) _context_unsafe_alias((void **)&(p))
+static inline void _context_unsafe_alias(void **p) { }
/**
- * token_capability() - declare an abstract global capability instance
- * @name: token capability name
+ * token_context_guard() - declare an abstract global context guard instance
+ * @name: token context guard name
*
- * Helper that declares an abstract global capability instance @name that can be
- * used as a token capability, but not backed by a real data structure (linker
- * error if accidentally referenced). The type name is `__capability_@name`.
+ * Helper that declares an abstract global context guard instance @name, but not
+ * backed by a real data structure (linker error if accidentally referenced).
+ * The type name is `__context_@name`.
*/
-#define token_capability(name, ...) \
- struct_with_capability(__capability_##name, ##__VA_ARGS__) {}; \
- extern const struct __capability_##name *name
+#define token_context_guard(name, ...) \
+ context_guard_struct(__context_##name, ##__VA_ARGS__) {}; \
+ extern const struct __context_##name *name
/**
- * token_capability_instance() - declare another instance of a global capability
- * @cap: token capability previously declared with token_capability()
- * @name: name of additional global capability instance
+ * token_context_guard_instance() - declare another instance of a global context guard
+ * @ctx: token context guard previously declared with token_context_guard()
+ * @name: name of additional global context guard instance
*
- * Helper that declares an additional instance @name of the same token
- * capability class @name. This is helpful where multiple related token
- * capabilities are declared, as it also allows using the same underlying type
- * (`__capability_@cap`) as function arguments.
+ * Helper that declares an additional instance @name of the same token context
+ * guard class @ctx. This is helpful where multiple related token contexts are
+ * declared, as it also allows using the same underlying type (`__context_@ctx`)
+ * as function arguments.
*/
-#define token_capability_instance(cap, name) \
- extern const struct __capability_##cap *name
+#define token_context_guard_instance(ctx, name) \
+ extern const struct __context_##ctx *name
/*
- * Common keywords for static capability analysis.
+ * Common keywords for static context analysis.
*/
/**
- * __must_hold() - function attribute, caller must hold exclusive capability
+ * __must_hold() - function attribute, caller must hold exclusive context guard
*
- * Function attribute declaring that the caller must hold the given capability
- * instance(s) exclusively.
+ * Function attribute declaring that the caller must hold the given context
+ * guard instance(s) exclusively.
*/
-#define __must_hold(...) __requires_cap(__VA_ARGS__)
+#define __must_hold(...) __requires_ctx_guard(__VA_ARGS__)
/**
- * __must_not_hold() - function attribute, caller must not hold capability
+ * __must_not_hold() - function attribute, caller must not hold context guard
*
* Function attribute declaring that the caller must not hold the given
- * capability instance(s).
+ * context guard instance(s).
*/
-#define __must_not_hold(...) __excludes_cap(__VA_ARGS__)
+#define __must_not_hold(...) __excludes_ctx_guard(__VA_ARGS__)
/**
- * __acquires() - function attribute, function acquires capability exclusively
+ * __acquires() - function attribute, function acquires context guard exclusively
*
* Function attribute declaring that the function acquires the given
- * capability instance(s) exclusively, but does not release them.
+ * context guard instance(s) exclusively, but does not release them.
*/
-#define __acquires(...) __acquires_cap(__VA_ARGS__)
+#define __acquires(...) __acquires_ctx_guard(__VA_ARGS__)
/*
* Clang's analysis does not care precisely about the value, only that it is
@@ -281,75 +283,76 @@ static inline void _capability_unsafe_alias(void **p) { }
* misleading if we say that @ret is the value returned if acquired. Instead,
* provide symbolic variants which we translate.
*/
-#define __cond_acquires_impl_true(x, ...) __try_acquires##__VA_ARGS__##_cap(1, x)
-#define __cond_acquires_impl_false(x, ...) __try_acquires##__VA_ARGS__##_cap(0, x)
-#define __cond_acquires_impl_nonzero(x, ...) __try_acquires##__VA_ARGS__##_cap(1, x)
-#define __cond_acquires_impl_0(x, ...) __try_acquires##__VA_ARGS__##_cap(0, x)
-#define __cond_acquires_impl_nonnull(x, ...) __try_acquires##__VA_ARGS__##_cap(1, x)
-#define __cond_acquires_impl_NULL(x, ...) __try_acquires##__VA_ARGS__##_cap(0, x)
+#define __cond_acquires_impl_true(x, ...) __try_acquires##__VA_ARGS__##_ctx_guard(1, x)
+#define __cond_acquires_impl_false(x, ...) __try_acquires##__VA_ARGS__##_ctx_guard(0, x)
+#define __cond_acquires_impl_nonzero(x, ...) __try_acquires##__VA_ARGS__##_ctx_guard(1, x)
+#define __cond_acquires_impl_0(x, ...) __try_acquires##__VA_ARGS__##_ctx_guard(0, x)
+#define __cond_acquires_impl_nonnull(x, ...) __try_acquires##__VA_ARGS__##_ctx_guard(1, x)
+#define __cond_acquires_impl_NULL(x, ...) __try_acquires##__VA_ARGS__##_ctx_guard(0, x)
/**
* __cond_acquires() - function attribute, function conditionally
- * acquires a capability exclusively
- * @ret: abstract value returned by function if capability acquired
- * @x: capability instance pointer
+ * acquires a context guard exclusively
+ * @ret: abstract value returned by function if context guard acquired
+ * @x: context guard instance pointer
*
* Function attribute declaring that the function conditionally acquires the
- * given capability instance @x exclusively, but does not release it. The
- * function return value @ret denotes when the capability is acquired.
+ * given context guard instance @x exclusively, but does not release it. The
+ * function return value @ret denotes when the context guard is acquired.
*
* @ret may be one of: true, false, nonzero, 0, nonnull, NULL.
*/
#define __cond_acquires(ret, x) __cond_acquires_impl_##ret(x)
/**
- * __releases() - function attribute, function releases a capability exclusively
+ * __releases() - function attribute, function releases a context guard exclusively
*
- * Function attribute declaring that the function releases the given capability
- * instance(s) exclusively. The capability must be held on entry.
+ * Function attribute declaring that the function releases the given context
+ * guard instance(s) exclusively. The associated context must be active on
+ * entry.
*/
-#define __releases(...) __releases_cap(__VA_ARGS__)
+#define __releases(...) __releases_ctx_guard(__VA_ARGS__)
/**
- * __acquire() - function to acquire capability exclusively
- * @x: capability instance pointer
+ * __acquire() - function to acquire context guard exclusively
+ * @x: context guard instance pointer
*
- * No-op function that acquires the given capability instance @x exclusively.
+ * No-op function that acquires the given context guard instance @x exclusively.
*/
-#define __acquire(x) __acquire_cap(x)
+#define __acquire(x) __acquire_ctx_guard(x)
/**
- * __release() - function to release capability exclusively
- * @x: capability instance pointer
+ * __release() - function to release context guard exclusively
+ * @x: context guard instance pointer
*
- * No-op function that releases the given capability instance @x.
+ * No-op function that releases the given context guard instance @x.
*/
-#define __release(x) __release_cap(x)
+#define __release(x) __release_ctx_guard(x)
/**
- * __must_hold_shared() - function attribute, caller must hold shared capability
+ * __must_hold_shared() - function attribute, caller must hold shared context guard
*
- * Function attribute declaring that the caller must hold the given capability
- * instance(s) with shared access.
+ * Function attribute declaring that the caller must hold the given context
+ * guard instance(s) with shared access.
*/
-#define __must_hold_shared(...) __requires_shared_cap(__VA_ARGS__)
+#define __must_hold_shared(...) __requires_shared_ctx_guard(__VA_ARGS__)
/**
- * __acquires_shared() - function attribute, function acquires capability shared
+ * __acquires_shared() - function attribute, function acquires context guard shared
*
* Function attribute declaring that the function acquires the given
- * capability instance(s) with shared access, but does not release them.
+ * context guard instance(s) with shared access, but does not release them.
*/
-#define __acquires_shared(...) __acquires_shared_cap(__VA_ARGS__)
+#define __acquires_shared(...) __acquires_shared_ctx_guard(__VA_ARGS__)
/**
* __cond_acquires_shared() - function attribute, function conditionally
- * acquires a capability shared
- * @ret: abstract value returned by function if capability acquired
+ * acquires a context guard shared
+ * @ret: abstract value returned by function if context guard acquired
*
* Function attribute declaring that the function conditionally acquires the
- * given capability instance @x with shared access, but does not release it. The
- * function return value @ret denotes when the capability is acquired.
+ * given context guard instance @x with shared access, but does not release it.
+ * The function return value @ret denotes when the context guard is acquired.
*
* @ret may be one of: true, false, nonzero, 0, nonnull, NULL.
*/
@@ -357,33 +360,34 @@ static inline void _capability_unsafe_alias(void **p) { }
/**
* __releases_shared() - function attribute, function releases a
- * capability shared
+ * context guard shared
*
- * Function attribute declaring that the function releases the given capability
- * instance(s) with shared access. The capability must be held on entry.
+ * Function attribute declaring that the function releases the given context
+ * guard instance(s) with shared access. The associated context must be active
+ * on entry.
*/
-#define __releases_shared(...) __releases_shared_cap(__VA_ARGS__)
+#define __releases_shared(...) __releases_shared_ctx_guard(__VA_ARGS__)
/**
- * __acquire_shared() - function to acquire capability shared
- * @x: capability instance pointer
+ * __acquire_shared() - function to acquire context guard shared
+ * @x: context guard instance pointer
*
- * No-op function that acquires the given capability instance @x with shared
+ * No-op function that acquires the given context guard instance @x with shared
* access.
*/
-#define __acquire_shared(x) __acquire_shared_cap(x)
+#define __acquire_shared(x) __acquire_shared_ctx_guard(x)
/**
- * __release_shared() - function to release capability shared
- * @x: capability instance pointer
+ * __release_shared() - function to release context guard shared
+ * @x: context guard instance pointer
*
- * No-op function that releases the given capability instance @x with shared
+ * No-op function that releases the given context guard instance @x with shared
* access.
*/
-#define __release_shared(x) __release_shared_cap(x)
+#define __release_shared(x) __release_shared_ctx_guard(x)
/**
- * __acquire_ret() - helper to acquire capability of return value
+ * __acquire_ret() - helper to acquire context guard of return value
* @call: call expression
* @ret_expr: acquire expression that uses __ret
*/
@@ -395,7 +399,7 @@ static inline void _capability_unsafe_alias(void **p) { }
})
/**
- * __acquire_shared_ret() - helper to acquire capability shared of return value
+ * __acquire_shared_ret() - helper to acquire context guard shared of return value
* @call: call expression
* @ret_expr: acquire shared expression that uses __ret
*/
@@ -407,9 +411,9 @@ static inline void _capability_unsafe_alias(void **p) { }
})
/*
- * Attributes to mark functions returning acquired capabilities. This is purely
- * cosmetic to help readability, and should be used with the above macros as
- * follows:
+ * Attributes to mark functions returning acquired context guards. This is
+ * purely cosmetic to help readability, and should be used with the above macros
+ * as follows:
*
* struct foo { spinlock_t lock; ... };
* ...
@@ -417,7 +421,7 @@ static inline void _capability_unsafe_alias(void **p) { }
* struct foo *_myfunc(int bar) __acquires_ret;
* ...
*/
-#define __acquires_ret __no_capability_analysis
-#define __acquires_shared_ret __no_capability_analysis
+#define __acquires_ret __no_context_analysis
+#define __acquires_shared_ret __no_context_analysis
-#endif /* _LINUX_COMPILER_CAPABILITY_ANALYSIS_H */
+#endif /* _LINUX_COMPILER_CONTEXT_ANALYSIS_H */
^ permalink raw reply related
* Re: [syzbot ci] Re: Compiler-Based Capability- and Locking-Analysis
From: Marco Elver @ 2025-09-19 7:05 UTC (permalink / raw)
To: syzbot ci
Cc: arnd, boqun.feng, bvanassche, corbet, davem, dvyukov, edumazet,
frederic, glider, gregkh, hch, herbert, irogers, jannh,
joelagnelf, josh, justinstitt, kasan-dev, kees, linux-crypto,
linux-doc, linux-kbuild, linux-kernel, linux-mm,
linux-security-module, linux-sparse, llvm, longman,
luc.vanoostenryck, lukas.bulwahn, mark.rutland, mathieu.desnoyers,
mingo, mingo, morbo, nathan, neeraj.upadhyay, nick.desaulniers,
ojeda, paulmck, penguin-kernel, peterz, rcu, rostedt, takedakn,
tglx, tgraf, urezki, will, syzbot, syzkaller-bugs
In-Reply-To: <68cc6067.a00a0220.37dadf.0003.GAE@google.com>
On Thu, Sep 18, 2025 at 12:41PM -0700, syzbot ci wrote:
> syzbot ci has tested the following series
>
> [v3] Compiler-Based Capability- and Locking-Analysis
[...]
> and found the following issue:
> general protection fault in validate_page_before_insert
>
> Full report is available here:
> https://ci.syzbot.org/series/81182522-74c0-4494-bcf8-976133df7dc7
>
> ***
>
> general protection fault in validate_page_before_insert
Thanks, syzbot ci!
I messed up the type when moving kcov->area access inside the critical
section. This is the fix:
fixup! kcov: Enable capability analysis
diff --git a/kernel/kcov.c b/kernel/kcov.c
index 1897c8ca6209..e81e3c0d01c6 100644
--- a/kernel/kcov.c
+++ b/kernel/kcov.c
@@ -497,7 +497,7 @@ static int kcov_mmap(struct file *filep, struct vm_area_struct *vma)
unsigned long size, off;
struct page *page;
unsigned long flags;
- unsigned long *area;
+ void *area;
spin_lock_irqsave(&kcov->lock, flags);
size = kcov->size * sizeof(unsigned long);
^ permalink raw reply related
* Re: [PATCH v2] tpm: use a map for tpm2_calc_ordinal_duration()
From: Jarkko Sakkinen @ 2025-09-19 7:05 UTC (permalink / raw)
To: Serge E. Hallyn
Cc: linux-integrity, Frédéric Jouen, Peter Huewe,
Jason Gunthorpe, James Bottomley, Mimi Zohar, David Howells,
Paul Moore, James Morris, open list, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM
In-Reply-To: <aMzSyCQks3NlMhPI@mail.hallyn.com>
On Thu, Sep 18, 2025 at 10:49:28PM -0500, Serge E. Hallyn wrote:
> On Thu, Sep 18, 2025 at 10:30:18PM +0300, Jarkko Sakkinen wrote:
> > The current shenanigans for duration calculation introduce too much
> > complexity for a trivial problem, and further the code is hard to patch and
> > maintain.
> >
> > Address these issues with a flat look-up table, which is easy to understand
> > and patch. If leaf driver specific patching is required in future, it is
> > easy enough to make a copy of this table during driver initialization and
> > add the chip parameter back.
> >
> > 'chip->duration' is retained for TPM 1.x.
> >
> > As the first entry for this new behavior address TCG spec update mentioned
> > in this issue:
> >
> > https://github.com/raspberrypi/linux/issues/7054
> >
> > Therefore, for TPM_SelfTest the duration is set to 3000 ms.
> >
> > This does not categorize a as bug, given that this is introduced to the
> > spec after the feature was originally made.
> >
> > Cc: Frédéric Jouen <fjouen@sealsq.com>
> > Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
>
> fwiw (which shouldn't be much) looks good to me, but two questions,
> one here and one below.
>
> First, it looks like in the existing code it is possible for a tpm2
> chip to set its own timeouts and then set the TPM_CHIP_FLAG_HAVE_TIMEOUTS
> flag to avoid using the defaults, but I don't see anything using that
> in-tree. Is it possible that there are out of tree drivers that will be
> sabotaged here? Or am I misunderstanding that completely?
Good questions, and I can brief a bit about the context of the
pre-existing art and this change.
This complexity was formed in 2014 when I originally developed TPM2
support and the only available testing plaform was early Intel PTT with
a flakky version of TPM2 support (e.g., no localities).
Since then we haven't had per leaf-driver divergence.
Further, I think that this type of layout is actually a better fit if
we ever need to quirks for command durations for a particular device, as
then we can migrate to "copy and patch" semantics i.e., have a copy of
this map in the chip structure.
As per out-of-tree drivers, it's unfortunate reality of out-of-tree
drivers :-) However, this will definitely add some extra work, when
backporting fixes (not overwhelmingly much).
BR, Jarkko
^ permalink raw reply
* Re: [PATCH v2] tpm: use a map for tpm2_calc_ordinal_duration()
From: Serge E. Hallyn @ 2025-09-19 3:49 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: linux-integrity, Frédéric Jouen, Peter Huewe,
Jason Gunthorpe, James Bottomley, Mimi Zohar, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, open list,
open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
In-Reply-To: <20250918193019.4018706-1-jarkko@kernel.org>
On Thu, Sep 18, 2025 at 10:30:18PM +0300, Jarkko Sakkinen wrote:
> The current shenanigans for duration calculation introduce too much
> complexity for a trivial problem, and further the code is hard to patch and
> maintain.
>
> Address these issues with a flat look-up table, which is easy to understand
> and patch. If leaf driver specific patching is required in future, it is
> easy enough to make a copy of this table during driver initialization and
> add the chip parameter back.
>
> 'chip->duration' is retained for TPM 1.x.
>
> As the first entry for this new behavior address TCG spec update mentioned
> in this issue:
>
> https://github.com/raspberrypi/linux/issues/7054
>
> Therefore, for TPM_SelfTest the duration is set to 3000 ms.
>
> This does not categorize a as bug, given that this is introduced to the
> spec after the feature was originally made.
>
> Cc: Frédéric Jouen <fjouen@sealsq.com>
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
fwiw (which shouldn't be much) looks good to me, but two questions,
one here and one below.
First, it looks like in the existing code it is possible for a tpm2
chip to set its own timeouts and then set the TPM_CHIP_FLAG_HAVE_TIMEOUTS
flag to avoid using the defaults, but I don't see anything using that
in-tree. Is it possible that there are out of tree drivers that will be
sabotaged here? Or am I misunderstanding that completely?
> ---
> v2:
> - Add the missing msec_to_jiffies() calls.
> - Drop redundant stuff.
> ---
> drivers/char/tpm/tpm-interface.c | 2 +-
> drivers/char/tpm/tpm.h | 2 +-
> drivers/char/tpm/tpm2-cmd.c | 127 ++++++++-----------------------
> include/linux/tpm.h | 5 +-
> 4 files changed, 37 insertions(+), 99 deletions(-)
>
> diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
> index b71725827743..c9f173001d0e 100644
> --- a/drivers/char/tpm/tpm-interface.c
> +++ b/drivers/char/tpm/tpm-interface.c
> @@ -52,7 +52,7 @@ MODULE_PARM_DESC(suspend_pcr,
> unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
> {
> if (chip->flags & TPM_CHIP_FLAG_TPM2)
> - return tpm2_calc_ordinal_duration(chip, ordinal);
> + return tpm2_calc_ordinal_duration(ordinal);
> else
> return tpm1_calc_ordinal_duration(chip, ordinal);
> }
> diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
> index 7bb87fa5f7a1..2726bd38e5ac 100644
> --- a/drivers/char/tpm/tpm.h
> +++ b/drivers/char/tpm/tpm.h
> @@ -299,7 +299,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,
> ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip);
> int tpm2_auto_startup(struct tpm_chip *chip);
> void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type);
> -unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal);
> +unsigned long tpm2_calc_ordinal_duration(u32 ordinal);
> int tpm2_probe(struct tpm_chip *chip);
> int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip);
> int tpm2_find_cc(struct tpm_chip *chip, u32 cc);
> diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
> index 524d802ede26..7d77f6fbc152 100644
> --- a/drivers/char/tpm/tpm2-cmd.c
> +++ b/drivers/char/tpm/tpm2-cmd.c
> @@ -28,120 +28,57 @@ static struct tpm2_hash tpm2_hash_map[] = {
>
> int tpm2_get_timeouts(struct tpm_chip *chip)
> {
> - /* Fixed timeouts for TPM2 */
> chip->timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A);
> chip->timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B);
> chip->timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C);
> chip->timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D);
> -
> - /* PTP spec timeouts */
> - chip->duration[TPM_SHORT] = msecs_to_jiffies(TPM2_DURATION_SHORT);
> - chip->duration[TPM_MEDIUM] = msecs_to_jiffies(TPM2_DURATION_MEDIUM);
> - chip->duration[TPM_LONG] = msecs_to_jiffies(TPM2_DURATION_LONG);
> -
> - /* Key creation commands long timeouts */
> - chip->duration[TPM_LONG_LONG] =
> - msecs_to_jiffies(TPM2_DURATION_LONG_LONG);
> -
> chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS;
> -
> return 0;
> }
>
> -/**
> - * tpm2_ordinal_duration_index() - returns an index to the chip duration table
> - * @ordinal: TPM command ordinal.
> - *
> - * The function returns an index to the chip duration table
> - * (enum tpm_duration), that describes the maximum amount of
> - * time the chip could take to return the result for a particular ordinal.
> - *
> - * The values of the MEDIUM, and LONG durations are taken
> - * from the PC Client Profile (PTP) specification (750, 2000 msec)
> - *
> - * LONG_LONG is for commands that generates keys which empirically takes
> - * a longer time on some systems.
> - *
> - * Return:
> - * * TPM_MEDIUM
> - * * TPM_LONG
> - * * TPM_LONG_LONG
> - * * TPM_UNDEFINED
> +/*
> + * Contains the maximum durations in milliseconds for TPM2 commands.
> */
> -static u8 tpm2_ordinal_duration_index(u32 ordinal)
> -{
> - switch (ordinal) {
> - /* Startup */
> - case TPM2_CC_STARTUP: /* 144 */
> - return TPM_MEDIUM;
> -
> - case TPM2_CC_SELF_TEST: /* 143 */
> - return TPM_LONG;
> -
> - case TPM2_CC_GET_RANDOM: /* 17B */
> - return TPM_LONG;
> -
> - case TPM2_CC_SEQUENCE_UPDATE: /* 15C */
> - return TPM_MEDIUM;
> - case TPM2_CC_SEQUENCE_COMPLETE: /* 13E */
> - return TPM_MEDIUM;
> - case TPM2_CC_EVENT_SEQUENCE_COMPLETE: /* 185 */
> - return TPM_MEDIUM;
> - case TPM2_CC_HASH_SEQUENCE_START: /* 186 */
> - return TPM_MEDIUM;
> -
> - case TPM2_CC_VERIFY_SIGNATURE: /* 177 */
> - return TPM_LONG_LONG;
> -
> - case TPM2_CC_PCR_EXTEND: /* 182 */
> - return TPM_MEDIUM;
> -
> - case TPM2_CC_HIERARCHY_CONTROL: /* 121 */
> - return TPM_LONG;
> - case TPM2_CC_HIERARCHY_CHANGE_AUTH: /* 129 */
> - return TPM_LONG;
> -
> - case TPM2_CC_GET_CAPABILITY: /* 17A */
> - return TPM_MEDIUM;
> -
> - case TPM2_CC_NV_READ: /* 14E */
> - return TPM_LONG;
> -
> - case TPM2_CC_CREATE_PRIMARY: /* 131 */
> - return TPM_LONG_LONG;
> - case TPM2_CC_CREATE: /* 153 */
> - return TPM_LONG_LONG;
> - case TPM2_CC_CREATE_LOADED: /* 191 */
> - return TPM_LONG_LONG;
> -
> - default:
> - return TPM_UNDEFINED;
> - }
> -}
> +static const struct {
> + unsigned long ordinal;
> + unsigned long duration;
> +} tpm2_ordinal_duration_map[] = {
> + {TPM2_CC_STARTUP, 750},
> + {TPM2_CC_SELF_TEST, 3000},
I assume you intended to increase TPM2_CC_SELF_TEST from 2000 to 3000
here? But it's not mentioned in the commit, so making sure...
> + {TPM2_CC_GET_RANDOM, 2000},
> + {TPM2_CC_SEQUENCE_UPDATE, 750},
> + {TPM2_CC_SEQUENCE_COMPLETE, 750},
> + {TPM2_CC_EVENT_SEQUENCE_COMPLETE, 750},
> + {TPM2_CC_HASH_SEQUENCE_START, 750},
> + {TPM2_CC_VERIFY_SIGNATURE, 30000},
> + {TPM2_CC_PCR_EXTEND, 750},
> + {TPM2_CC_HIERARCHY_CONTROL, 2000},
> + {TPM2_CC_HIERARCHY_CHANGE_AUTH, 2000},
> + {TPM2_CC_GET_CAPABILITY, 750},
> + {TPM2_CC_NV_READ, 2000},
> + {TPM2_CC_CREATE_PRIMARY, 30000},
> + {TPM2_CC_CREATE, 30000},
> + {TPM2_CC_CREATE_LOADED, 30000},
> +};
>
> /**
> - * tpm2_calc_ordinal_duration() - calculate the maximum command duration
> - * @chip: TPM chip to use.
> + * tpm2_calc_ordinal_duration() - Calculate the maximum command duration
> * @ordinal: TPM command ordinal.
> *
> - * The function returns the maximum amount of time the chip could take
> - * to return the result for a particular ordinal in jiffies.
> - *
> - * Return: A maximal duration time for an ordinal in jiffies.
> + * Returns the maximum amount of time the chip is expected by kernel to
> + * take in jiffies.
> */
> -unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
> +unsigned long tpm2_calc_ordinal_duration(u32 ordinal)
> {
> - unsigned int index;
> + int i;
>
> - index = tpm2_ordinal_duration_index(ordinal);
> + for (i = 0; i < ARRAY_SIZE(tpm2_ordinal_duration_map); i++)
> + if (ordinal == tpm2_ordinal_duration_map[i].ordinal)
> + return msecs_to_jiffies(tpm2_ordinal_duration_map[i].duration);
>
> - if (index != TPM_UNDEFINED)
> - return chip->duration[index];
> - else
> - return msecs_to_jiffies(TPM2_DURATION_DEFAULT);
> + return msecs_to_jiffies(TPM2_DURATION_DEFAULT);
> }
>
> -
> struct tpm2_pcr_read_out {
> __be32 update_cnt;
> __be32 pcr_selects_cnt;
> diff --git a/include/linux/tpm.h b/include/linux/tpm.h
> index b0e9eb5ef022..dc0338a783f3 100644
> --- a/include/linux/tpm.h
> +++ b/include/linux/tpm.h
> @@ -228,10 +228,11 @@ enum tpm2_timeouts {
> TPM2_TIMEOUT_B = 4000,
> TPM2_TIMEOUT_C = 200,
> TPM2_TIMEOUT_D = 30,
> +};
> +
> +enum tpm2_durations {
> TPM2_DURATION_SHORT = 20,
> - TPM2_DURATION_MEDIUM = 750,
> TPM2_DURATION_LONG = 2000,
> - TPM2_DURATION_LONG_LONG = 300000,
> TPM2_DURATION_DEFAULT = 120000,
> };
>
> --
> 2.39.5
^ permalink raw reply
* Re: [PATCH v4 00/12] Signed BPF programs
From: patchwork-bot+netdevbpf @ 2025-09-19 2:20 UTC (permalink / raw)
To: KP Singh
Cc: bpf, linux-security-module, bboscaccy, paul, kys, ast, daniel,
andrii
In-Reply-To: <20250914215141.15144-1-kpsingh@kernel.org>
Hello:
This series was applied to bpf/bpf-next.git (master)
by Alexei Starovoitov <ast@kernel.org>:
On Sun, 14 Sep 2025 23:51:29 +0200 you wrote:
> # v3 -> v4
>
> * Dropped the use of session keyring by default from skeletons.
> * Andrii's feedback on exclusive map creation libbpf changes.
> * Cleaned up some more typos I found.
>
> # v2 -> v3
>
> [...]
Here is the summary with links:
- [v4,01/12] bpf: Update the bpf_prog_calc_tag to use SHA256
(no matching commit)
- [v4,02/12] bpf: Implement exclusive map creation
https://git.kernel.org/bpf/bpf-next/c/baefdbdf6812
- [v4,03/12] libbpf: Implement SHA256 internal helper
https://git.kernel.org/bpf/bpf-next/c/c297fe3e9f99
- [v4,04/12] libbpf: Support exclusive map creation
https://git.kernel.org/bpf/bpf-next/c/567010a5478f
- [v4,05/12] selftests/bpf: Add tests for exclusive maps
https://git.kernel.org/bpf/bpf-next/c/6c850cbca82c
- [v4,06/12] bpf: Return hashes of maps in BPF_OBJ_GET_INFO_BY_FD
https://git.kernel.org/bpf/bpf-next/c/ea2e6467ac36
- [v4,07/12] bpf: Move the signature kfuncs to helpers.c
https://git.kernel.org/bpf/bpf-next/c/8cd189e414bb
- [v4,08/12] bpf: Implement signature verification for BPF programs
(no matching commit)
- [v4,09/12] libbpf: Update light skeleton for signing
(no matching commit)
- [v4,10/12] libbpf: Embed and verify the metadata hash in the loader
(no matching commit)
- [v4,11/12] bpftool: Add support for signing BPF programs
(no matching commit)
- [v4,12/12] selftests/bpf: Enable signature verification for some lskel tests
(no matching commit)
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v4 01/12] bpf: Update the bpf_prog_calc_tag to use SHA256
From: Alexei Starovoitov @ 2025-09-19 2:19 UTC (permalink / raw)
To: KP Singh
Cc: bpf, LSM List, Blaise Boscaccy, Paul Moore, K. Y. Srinivasan,
Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko
In-Reply-To: <20250914215141.15144-2-kpsingh@kernel.org>
On Sun, Sep 14, 2025 at 2:51 PM KP Singh <kpsingh@kernel.org> wrote:
>
> int bpf_prog_calc_tag(struct bpf_prog *fp)
> {
> - size_t size = bpf_prog_insn_size(fp);
> - u8 digest[SHA1_DIGEST_SIZE];
> + u32 insn_size = bpf_prog_insn_size(fp);
> struct bpf_insn *dst;
> bool was_ld_map;
> - u32 i;
> + int i, ret = 0;
I undid all of the above extra noise and removed unnecessary 'ret'
while applying the first 7 patches.
Pls address comments and respin.
^ permalink raw reply
* Re: [PATCH v3 00/35] Compiler-Based Capability- and Locking-Analysis
From: Linus Torvalds @ 2025-09-18 21:47 UTC (permalink / raw)
To: Marco Elver
Cc: Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon,
David S. Miller, Luc Van Oostenryck, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Bill Wendling, Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
Jann Horn, Joel Fernandes, Jonathan Corbet, Josh Triplett,
Justin Stitt, Kees Cook, Kentaro Takeda, Lukas Bulwahn,
Mark Rutland, Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
Neeraj Upadhyay, Nick Desaulniers, Steven Rostedt, Tetsuo Handa,
Thomas Gleixner, Thomas Graf, Uladzislau Rezki, Waiman Long,
kasan-dev, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
linux-mm, linux-security-module, linux-sparse, llvm, rcu
In-Reply-To: <aMx4-B_WAtX2aiKx@elver.google.com>
On Thu, 18 Sept 2025 at 14:26, Marco Elver <elver@google.com> wrote:
>
> Fair points. "Context Analysis" makes sense, but it makes the thing
> (e.g. lock) used to establish that context a little awkward to refer to
> -- see half-baked attempt at reworking the documentation below.
Yeah, I agree that some of that reads more than a bit oddly.
I wonder if we could talk about "context analysis", but then when
discussing what is *held* for a particular context, call that a
"context token" or something like that?
But I don't mind your "Context guard" notion either. I'm not loving
it, but it's not offensive to me either.
Then the language would be feel fairly straightforward,
Eg:
> +Context analysis is a way to specify permissibility of operations to depend on
> +contexts being held (or not held).
That "contexts being held" sounds odd, but talking about "context
markers", or "context tokens" would seem natural.
An alternative would be to not talk about markers / tokens / guards at
all, but simply about a context being *active*.
IOW, instead of wording it like this:
> +The set of contexts that are actually held by a given thread at a given point
> +in program execution is a run-time concept.
that talks about "being held", you could just state it in the sense of
the "set of contexts being active", and that immediately reads fairly
naturally, doesn't it?
Because a context is a *state* you are in, it's not something you hold on to.
The tokens - or whatever - would be only some internal implementation
detail of how the compiler keeps track of which state is active, not
the conceptual idea itself.
So you name states, and you have functions to mark those context
states as being entered or exited, but you don't really even have to
talk about "holding" anything.
No?
Linus
^ permalink raw reply
* Re: [PATCH v3 00/35] Compiler-Based Capability- and Locking-Analysis
From: Marco Elver @ 2025-09-18 21:26 UTC (permalink / raw)
To: Linus Torvalds
Cc: Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon,
David S. Miller, Luc Van Oostenryck, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Bill Wendling, Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
Jann Horn, Joel Fernandes, Jonathan Corbet, Josh Triplett,
Justin Stitt, Kees Cook, Kentaro Takeda, Lukas Bulwahn,
Mark Rutland, Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
Neeraj Upadhyay, Nick Desaulniers, Steven Rostedt, Tetsuo Handa,
Thomas Gleixner, Thomas Graf, Uladzislau Rezki, Waiman Long,
kasan-dev, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
linux-mm, linux-security-module, linux-sparse, llvm, rcu
In-Reply-To: <CAHk-=wgd-Wcp0GpYaQnU7S9ci+FvFmaNw1gm75mzf0ZWdNLxvw@mail.gmail.com>
On Thu, Sep 18, 2025 at 08:49AM -0700, Linus Torvalds wrote:
> I'd suggest just doing a search-and-replace of 's/capability/context/'
> and it would already make things a ton better. But maybe there are
> better names for this still?
Fair points. "Context Analysis" makes sense, but it makes the thing
(e.g. lock) used to establish that context a little awkward to refer to
-- see half-baked attempt at reworking the documentation below.
Maybe this:
Instance that must be acquired to enter context = "Context Guard"?
We can then still call it "Context Analysis". And I need to be mindful
of calling the objects themselves "Context Guard" throughout that
search-and-replace. E.g. the macro to create a context-guard-enabled
struct would be "context_guard_struct(spinlock) { ..".
I also thought about "Guard Analysis", but that sounds wrong, too.
Because we also have overloaded "guard(..)" (<linux/cleanup.h>).
Preferences?
[...]
> And if not "context", maybe some other word? But really, absolutely
> *not* "capability". Because that's just crazy talk.
>
> Please? Because other than this naming issue, I think this really is a
> good idea.
Thanks,
-- Marco
------ >8 ------
diff --git a/Documentation/dev-tools/capability-analysis.rst b/Documentation/dev-tools/capability-analysis.rst
index 3456132261c6..b0c0961d6af5 100644
--- a/Documentation/dev-tools/capability-analysis.rst
+++ b/Documentation/dev-tools/capability-analysis.rst
@@ -1,80 +1,79 @@
.. SPDX-License-Identifier: GPL-2.0
.. Copyright (C) 2025, Google LLC.
-.. _capability-analysis:
+.. _context-analysis:
-Compiler-Based Capability Analysis
-==================================
+Compiler-Based Context Analysis
+===============================
-Capability analysis is a C language extension, which enables statically
-checking that user-definable "capabilities" are acquired and released where
-required. An obvious application is lock-safety checking for the kernel's
-various synchronization primitives (each of which represents a "capability"),
-and checking that locking rules are not violated.
+Context analysis is a C language extension, which enables statically checking
+that user-definable contexts are acquired and released where required. An
+obvious application is lock-safety checking for the kernel's various
+synchronization primitives (each of which represents a context if held), and
+checking that locking rules are not violated.
-The Clang compiler currently supports the full set of capability analysis
+The Clang compiler currently supports the full set of context analysis
features. To enable for Clang, configure the kernel with::
- CONFIG_WARN_CAPABILITY_ANALYSIS=y
+ CONFIG_WARN_CONTEXT_ANALYSIS=y
The feature requires Clang 22 or later.
The analysis is *opt-in by default*, and requires declaring which modules and
subsystems should be analyzed in the respective `Makefile`::
- CAPABILITY_ANALYSIS_mymodule.o := y
+ CONTEXT_ANALYSIS_mymodule.o := y
Or for all translation units in the directory::
- CAPABILITY_ANALYSIS := y
+ CONTEXT_ANALYSIS := y
It is possible to enable the analysis tree-wide, however, which will result in
numerous false positive warnings currently and is *not* generally recommended::
- CONFIG_WARN_CAPABILITY_ANALYSIS_ALL=y
+ CONFIG_WARN_CONTEXT_ANALYSIS_ALL=y
Programming Model
-----------------
-The below describes the programming model around using capability-enabled
-types.
+The below describes the programming model around using context-enabled types.
.. note::
- Enabling capability analysis can be seen as enabling a dialect of Linux C with
- a Capability System. Some valid patterns involving complex control-flow are
+ Enabling context analysis can be seen as enabling a dialect of Linux C with
+ a Context System. Some valid patterns involving complex control-flow are
constrained (such as conditional acquisition and later conditional release
- in the same function, or returning pointers to capabilities from functions.
+ in the same function).
-Capability analysis is a way to specify permissibility of operations to depend
-on capabilities being held (or not held). Typically we are interested in
-protecting data and code by requiring some capability to be held, for example a
-specific lock. The analysis ensures that the caller cannot perform the
-operation without holding the appropriate capability.
+Context analysis is a way to specify permissibility of operations to depend on
+contexts being held (or not held). Typically we are interested in protecting
+data and code in a critical section by requiring a specific context to be held,
+for example a specific lock. The analysis ensures that the caller cannot
+perform the operation without holding the appropriate context.
-Capabilities are associated with named structs, along with functions that
-operate on capability-enabled struct instances to acquire and release the
-associated capability.
+Contexts are associated with named structs, along with functions that operate
+on context-enabled struct instances to acquire and release the associated
+context.
-Capabilities can be held either exclusively or shared. This mechanism allows
-assign more precise privileges when holding a capability, typically to
+Contexts can be held either exclusively or shared. This mechanism allows
+assigning more precise privileges when holding a context, typically to
distinguish where a thread may only read (shared) or also write (exclusive) to
guarded data.
-The set of capabilities that are actually held by a given thread at a given
-point in program execution is a run-time concept. The static analysis works by
-calculating an approximation of that set, called the capability environment.
-The capability environment is calculated for every program point, and describes
-the set of capabilities that are statically known to be held, or not held, at
-that particular point. This environment is a conservative approximation of the
-full set of capabilities that will actually held by a thread at run-time.
+The set of contexts that are actually held by a given thread at a given point
+in program execution is a run-time concept. The static analysis works by
+calculating an approximation of that set, called the context environment. The
+context environment is calculated for every program point, and describes the
+set of contexts that are statically known to be held, or not held, at that
+particular point. This environment is a conservative approximation of the full
+set of contexts that will actually held by a thread at run-time.
More details are also documented `here
<https://clang.llvm.org/docs/ThreadSafetyAnalysis.html>`_.
.. note::
- Clang's analysis explicitly does not infer capabilities acquired or released
+ Clang's analysis explicitly does not infer contexts acquired or released
by inline functions. It requires explicit annotations to (a) assert that
- it's not a bug if a capability is released or acquired, and (b) to retain
+ it's not a bug if a context is released or acquired, and (b) to retain
consistency between inline and non-inline function declarations.
Supported Kernel Primitives
@@ -85,13 +84,13 @@ Currently the following synchronization primitives are supported:
`bit_spinlock`, RCU, SRCU (`srcu_struct`), `rw_semaphore`, `local_lock_t`,
`ww_mutex`.
-For capabilities with an initialization function (e.g., `spin_lock_init()`),
-calling this function on the capability instance before initializing any
-guarded members or globals prevents the compiler from issuing warnings about
-unguarded initialization.
+For contexts with an initialization function (e.g., `spin_lock_init()`),
+calling this function on the context instance before initializing any guarded
+members or globals prevents the compiler from issuing warnings about unguarded
+initialization.
Lockdep assertions, such as `lockdep_assert_held()`, inform the compiler's
-capability analysis that the associated synchronization primitive is held after
+context analysis that the associated synchronization primitive is held after
the assertion. This avoids false positives in complex control-flow scenarios
and encourages the use of Lockdep where static analysis is limited. For
example, this is useful when a function doesn't *always* require a lock, making
@@ -100,9 +99,9 @@ example, this is useful when a function doesn't *always* require a lock, making
Keywords
~~~~~~~~
-.. kernel-doc:: include/linux/compiler-capability-analysis.h
- :identifiers: struct_with_capability
- token_capability token_capability_instance
+.. kernel-doc:: include/linux/compiler-context-analysis.h
+ :identifiers: struct_with_context
+ token_context token_context_instance
__guarded_by __pt_guarded_by
__must_hold
__must_not_hold
@@ -117,13 +116,13 @@ Keywords
__release
__acquire_shared
__release_shared
- capability_unsafe
- __capability_unsafe
- disable_capability_analysis enable_capability_analysis
+ context_unsafe
+ __context_unsafe
+ disable_context_analysis enable_context_analysis
.. note::
- The function attribute `__no_capability_analysis` is reserved for internal
- implementation of capability-enabled primitives, and should be avoided in
+ The function attribute `__no_context_analysis` is reserved for internal
+ implementation of context-enabled primitives, and should be avoided in
normal code.
Background
@@ -140,9 +139,10 @@ Indeed, its foundations can be found in `capability systems
the permissibility of operations to depend on some capability being held (or
not held).
-Because the feature is not just able to express capabilities related to
-synchronization primitives, the naming chosen for the kernel departs from
-Clang's initial "Thread Safety" nomenclature and refers to the feature as
-"Capability Analysis" to avoid confusion. The implementation still makes
-references to the older terminology in some places, such as `-Wthread-safety`
-being the warning option that also still appears in diagnostic messages.
+Because the feature is not just able to express contexts related to
+synchronization primitives, and "capability" is already overloaded in the
+kernel, the naming chosen for the kernel departs from Clang's initial "Thread
+Safety" and "Capability" nomenclature and refers to the feature as "Context
+Analysis" to avoid confusion. The internal implementation still makes
+references to Clang's terminology, such as `-Wthread-safety` being the warning
+option that also still appears in diagnostic messages.
diff --git a/include/linux/compiler-capability-analysis.h b/include/linux/compiler-capability-analysis.h
index f8a1da67589c..7882684a8308 100644
--- a/include/linux/compiler-capability-analysis.h
+++ b/include/linux/compiler-capability-analysis.h
@@ -1,42 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Macros and attributes for compiler-based static capability analysis.
+ * Macros and attributes for compiler-based static context analysis.
*/
-#ifndef _LINUX_COMPILER_CAPABILITY_ANALYSIS_H
-#define _LINUX_COMPILER_CAPABILITY_ANALYSIS_H
+#ifndef _LINUX_COMPILER_CONTEXT_ANALYSIS_H
+#define _LINUX_COMPILER_CONTEXT_ANALYSIS_H
-#if defined(WARN_CAPABILITY_ANALYSIS)
+#if defined(WARN_CONTEXT_ANALYSIS)
/*
- * The below attributes are used to define new capability types. Internal only.
- */
-# define __cap_type(name) __attribute__((capability(#name)))
-# define __reentrant_cap __attribute__((reentrant_capability))
-# define __acquires_cap(...) __attribute__((acquire_capability(__VA_ARGS__)))
-# define __acquires_shared_cap(...) __attribute__((acquire_shared_capability(__VA_ARGS__)))
-# define __try_acquires_cap(ret, var) __attribute__((try_acquire_capability(ret, var)))
-# define __try_acquires_shared_cap(ret, var) __attribute__((try_acquire_shared_capability(ret, var)))
-# define __releases_cap(...) __attribute__((release_capability(__VA_ARGS__)))
-# define __releases_shared_cap(...) __attribute__((release_shared_capability(__VA_ARGS__)))
-# define __assumes_cap(...) __attribute__((assert_capability(__VA_ARGS__)))
-# define __assumes_shared_cap(...) __attribute__((assert_shared_capability(__VA_ARGS__)))
-# define __returns_cap(var) __attribute__((lock_returned(var)))
+ * The below attributes are used to define new context (Clang: capability) types.
+ * Internal only.
+ */
+# define __ctx_type(name) __attribute__((capability(#name)))
+# define __reentrant_ctx __attribute__((reentrant_capability))
+# define __acquires_ctx(...) __attribute__((acquire_capability(__VA_ARGS__)))
+# define __acquires_shared_ctx(...) __attribute__((acquire_shared_capability(__VA_ARGS__)))
+# define __try_acquires_ctx(ret, var) __attribute__((try_acquire_capability(ret, var)))
+# define __try_acquires_shared_ctx(ret, var) __attribute__((try_acquire_shared_capability(ret, var)))
+# define __releases_ctx(...) __attribute__((release_capability(__VA_ARGS__)))
+# define __releases_shared_ctx(...) __attribute__((release_shared_capability(__VA_ARGS__)))
+# define __assumes_ctx(...) __attribute__((assert_capability(__VA_ARGS__)))
+# define __assumes_shared_ctx(...) __attribute__((assert_shared_capability(__VA_ARGS__)))
+# define __returns_ctx(var) __attribute__((lock_returned(var)))
/*
* The below are used to annotate code being checked. Internal only.
*/
-# define __excludes_cap(...) __attribute__((locks_excluded(__VA_ARGS__)))
-# define __requires_cap(...) __attribute__((requires_capability(__VA_ARGS__)))
-# define __requires_shared_cap(...) __attribute__((requires_shared_capability(__VA_ARGS__)))
+# define __excludes_ctx(...) __attribute__((locks_excluded(__VA_ARGS__)))
+# define __requires_ctx(...) __attribute__((requires_capability(__VA_ARGS__)))
+# define __requires_shared_ctx(...) __attribute__((requires_shared_capability(__VA_ARGS__)))
/**
* __guarded_by - struct member and globals attribute, declares variable
- * protected by capability
+ * protected by context
*
* Declares that the struct member or global variable must be guarded by the
- * given capabilities. Read operations on the data require shared access,
- * while write operations require exclusive access.
+ * given context. Read operations on the data require shared access, while write
+ * operations require exclusive access.
*
* .. code-block:: c
*
@@ -49,11 +50,11 @@
/**
* __pt_guarded_by - struct member and globals attribute, declares pointed-to
- * data is protected by capability
+ * data is protected by context
*
* Declares that the data pointed to by the struct member pointer or global
- * pointer must be guarded by the given capabilities. Read operations on the
- * data require shared access, while write operations require exclusive access.
+ * pointer must be guarded by the given contexts. Read operations on the data
+ * require shared access, while write operations require exclusive access.
*
* .. code-block:: c
*
@@ -65,14 +66,14 @@
# define __pt_guarded_by(...) __attribute__((pt_guarded_by(__VA_ARGS__)))
/**
- * struct_with_capability() - declare or define a capability struct
+ * struct_with_context() - declare or define a context struct
* @name: struct name
*
- * Helper to declare or define a struct type with capability of the same name.
+ * Helper to declare or define a struct type with context of the same name.
*
* .. code-block:: c
*
- * struct_with_capability(my_handle) {
+ * struct_with_context(my_handle) {
* int foo;
* long bar;
* };
@@ -81,98 +82,98 @@
* ...
* };
* // ... declared elsewhere ...
- * struct_with_capability(some_state);
+ * struct_with_context(some_state);
*
* Note: The implementation defines several helper functions that can acquire,
- * release, and assert the capability.
- */
-# define struct_with_capability(name, ...) \
- struct __cap_type(name) __VA_ARGS__ name; \
- static __always_inline void __acquire_cap(const struct name *var) \
- __attribute__((overloadable)) __no_capability_analysis __acquires_cap(var) { } \
- static __always_inline void __acquire_shared_cap(const struct name *var) \
- __attribute__((overloadable)) __no_capability_analysis __acquires_shared_cap(var) { } \
- static __always_inline bool __try_acquire_cap(const struct name *var, bool ret) \
- __attribute__((overloadable)) __no_capability_analysis __try_acquires_cap(1, var) \
+ * release, and assert the context is held.
+ */
+# define struct_with_context(name, ...) \
+ struct __ctx_type(name) __VA_ARGS__ name; \
+ static __always_inline void __acquire_ctx(const struct name *var) \
+ __attribute__((overloadable)) __no_context_analysis __acquires_ctx(var) { } \
+ static __always_inline void __acquire_shared_ctx(const struct name *var) \
+ __attribute__((overloadable)) __no_context_analysis __acquires_shared_ctx(var) { } \
+ static __always_inline bool __try_acquire_ctx(const struct name *var, bool ret) \
+ __attribute__((overloadable)) __no_context_analysis __try_acquires_ctx(1, var) \
{ return ret; } \
- static __always_inline bool __try_acquire_shared_cap(const struct name *var, bool ret) \
- __attribute__((overloadable)) __no_capability_analysis __try_acquires_shared_cap(1, var) \
+ static __always_inline bool __try_acquire_shared_ctx(const struct name *var, bool ret) \
+ __attribute__((overloadable)) __no_context_analysis __try_acquires_shared_ctx(1, var) \
{ return ret; } \
- static __always_inline void __release_cap(const struct name *var) \
- __attribute__((overloadable)) __no_capability_analysis __releases_cap(var) { } \
- static __always_inline void __release_shared_cap(const struct name *var) \
- __attribute__((overloadable)) __no_capability_analysis __releases_shared_cap(var) { } \
- static __always_inline void __assume_cap(const struct name *var) \
- __attribute__((overloadable)) __assumes_cap(var) { } \
- static __always_inline void __assume_shared_cap(const struct name *var) \
- __attribute__((overloadable)) __assumes_shared_cap(var) { } \
+ static __always_inline void __release_ctx(const struct name *var) \
+ __attribute__((overloadable)) __no_context_analysis __releases_ctx(var) { } \
+ static __always_inline void __release_shared_ctx(const struct name *var) \
+ __attribute__((overloadable)) __no_context_analysis __releases_shared_ctx(var) { } \
+ static __always_inline void __assume_ctx(const struct name *var) \
+ __attribute__((overloadable)) __assumes_ctx(var) { } \
+ static __always_inline void __assume_shared_ctx(const struct name *var) \
+ __attribute__((overloadable)) __assumes_shared_ctx(var) { } \
struct name
/**
- * disable_capability_analysis() - disables capability analysis
+ * disable_context_analysis() - disables context analysis
*
- * Disables capability analysis. Must be paired with a later
- * enable_capability_analysis().
+ * Disables context analysis. Must be paired with a later
+ * enable_context_analysis().
*/
-# define disable_capability_analysis() \
+# define disable_context_analysis() \
__diag_push(); \
__diag_ignore_all("-Wunknown-warning-option", "") \
__diag_ignore_all("-Wthread-safety", "") \
__diag_ignore_all("-Wthread-safety-pointer", "")
/**
- * enable_capability_analysis() - re-enables capability analysis
+ * enable_context_analysis() - re-enables context analysis
*
- * Re-enables capability analysis. Must be paired with a prior
- * disable_capability_analysis().
+ * Re-enables context analysis. Must be paired with a prior
+ * disable_context_analysis().
*/
-# define enable_capability_analysis() __diag_pop()
+# define enable_context_analysis() __diag_pop()
/**
- * __no_capability_analysis - function attribute, disables capability analysis
- *
- * Function attribute denoting that capability analysis is disabled for the
- * whole function. Prefer use of `capability_unsafe()` where possible.
- */
-# define __no_capability_analysis __attribute__((no_thread_safety_analysis))
-
-#else /* !WARN_CAPABILITY_ANALYSIS */
-
-# define __cap_type(name)
-# define __reentrant_cap
-# define __acquires_cap(...)
-# define __acquires_shared_cap(...)
-# define __try_acquires_cap(ret, var)
-# define __try_acquires_shared_cap(ret, var)
-# define __releases_cap(...)
-# define __releases_shared_cap(...)
-# define __assumes_cap(...)
-# define __assumes_shared_cap(...)
-# define __returns_cap(var)
+ * __no_context_analysis - function attribute, disables context analysis
+ *
+ * Function attribute denoting that context analysis is disabled for the
+ * whole function. Prefer use of `context_unsafe()` where possible.
+ */
+# define __no_context_analysis __attribute__((no_thread_safety_analysis))
+
+#else /* !WARN_CONTEXT_ANALYSIS */
+
+# define __ctx_type(name)
+# define __reentrant_ctx
+# define __acquires_ctx(...)
+# define __acquires_shared_ctx(...)
+# define __try_acquires_ctx(ret, var)
+# define __try_acquires_shared_ctx(ret, var)
+# define __releases_ctx(...)
+# define __releases_shared_ctx(...)
+# define __assumes_ctx(...)
+# define __assumes_shared_ctx(...)
+# define __returns_ctx(var)
# define __guarded_by(...)
# define __pt_guarded_by(...)
-# define __excludes_cap(...)
-# define __requires_cap(...)
-# define __requires_shared_cap(...)
-# define __acquire_cap(var) do { } while (0)
-# define __acquire_shared_cap(var) do { } while (0)
-# define __try_acquire_cap(var, ret) (ret)
-# define __try_acquire_shared_cap(var, ret) (ret)
-# define __release_cap(var) do { } while (0)
-# define __release_shared_cap(var) do { } while (0)
-# define __assume_cap(var) do { (void)(var); } while (0)
-# define __assume_shared_cap(var) do { (void)(var); } while (0)
-# define struct_with_capability(name, ...) struct __VA_ARGS__ name
-# define disable_capability_analysis()
-# define enable_capability_analysis()
-# define __no_capability_analysis
-
-#endif /* WARN_CAPABILITY_ANALYSIS */
+# define __excludes_ctx(...)
+# define __requires_ctx(...)
+# define __requires_shared_ctx(...)
+# define __acquire_ctx(var) do { } while (0)
+# define __acquire_shared_ctx(var) do { } while (0)
+# define __try_acquire_ctx(var, ret) (ret)
+# define __try_acquire_shared_ctx(var, ret) (ret)
+# define __release_ctx(var) do { } while (0)
+# define __release_shared_ctx(var) do { } while (0)
+# define __assume_ctx(var) do { (void)(var); } while (0)
+# define __assume_shared_ctx(var) do { (void)(var); } while (0)
+# define struct_with_context(name, ...) struct __VA_ARGS__ name
+# define disable_context_analysis()
+# define enable_context_analysis()
+# define __no_context_analysis
+
+#endif /* WARN_CONTEXT_ANALYSIS */
/**
- * capability_unsafe() - disable capability checking for contained code
+ * context_unsafe() - disable context checking for contained code
*
- * Disables capability checking for contained statements or expression.
+ * Disables context checking for contained statements or expression.
*
* .. code-block:: c
*
@@ -186,30 +187,30 @@
* // ...
* // other code that is still checked ...
* // ...
- * return capability_unsafe(d->counter);
+ * return context_unsafe(d->counter);
* }
*/
-#define capability_unsafe(...) \
+#define context_unsafe(...) \
({ \
- disable_capability_analysis(); \
+ disable_context_analysis(); \
__VA_ARGS__; \
- enable_capability_analysis() \
+ enable_context_analysis() \
})
/**
- * __capability_unsafe() - function attribute, disable capability checking
+ * __context_unsafe() - function attribute, disable context checking
* @comment: comment explaining why opt-out is safe
*
- * Function attribute denoting that capability analysis is disabled for the
+ * Function attribute denoting that context analysis is disabled for the
* whole function. Forces adding an inline comment as argument.
*/
-#define __capability_unsafe(comment) __no_capability_analysis
+#define __context_unsafe(comment) __no_context_analysis
/**
- * capability_unsafe_alias() - helper to insert a capability "alias barrier"
- * @p: pointer aliasing a capability or object containing capabilities
+ * context_unsafe_alias() - helper to insert a context "alias barrier"
+ * @p: pointer aliasing a context or object containing context pointers
*
- * No-op function that acts as a "capability alias barrier", where the analysis
+ * No-op function that acts as a "context alias barrier", where the analysis
* rightfully detects that we're switching aliases, but the switch is considered
* safe but beyond the analysis reasoning abilities.
*
@@ -219,61 +220,61 @@
* their value cannot be determined (e.g. when passing a non-const pointer to an
* alias as a function argument).
*/
-#define capability_unsafe_alias(p) _capability_unsafe_alias((void **)&(p))
-static inline void _capability_unsafe_alias(void **p) { }
+#define context_unsafe_alias(p) _context_unsafe_alias((void **)&(p))
+static inline void _context_unsafe_alias(void **p) { }
/**
- * token_capability() - declare an abstract global capability instance
- * @name: token capability name
+ * token_context() - declare an abstract global context instance
+ * @name: token context name
*
- * Helper that declares an abstract global capability instance @name that can be
- * used as a token capability, but not backed by a real data structure (linker
- * error if accidentally referenced). The type name is `__capability_@name`.
+ * Helper that declares an abstract global context instance @name that can be
+ * used as a token context, but not backed by a real data structure (linker
+ * error if accidentally referenced). The type name is `__context_@name`.
*/
-#define token_capability(name, ...) \
- struct_with_capability(__capability_##name, ##__VA_ARGS__) {}; \
- extern const struct __capability_##name *name
+#define token_context(name, ...) \
+ struct_with_context(__context_##name, ##__VA_ARGS__) {}; \
+ extern const struct __context_##name *name
/**
- * token_capability_instance() - declare another instance of a global capability
- * @cap: token capability previously declared with token_capability()
- * @name: name of additional global capability instance
+ * token_context_instance() - declare another instance of a global context
+ * @ctx: token context previously declared with token_context()
+ * @name: name of additional global context instance
*
* Helper that declares an additional instance @name of the same token
- * capability class @name. This is helpful where multiple related token
- * capabilities are declared, as it also allows using the same underlying type
- * (`__capability_@cap`) as function arguments.
+ * context class @name. This is helpful where multiple related token
+ * contexts are declared, as it also allows using the same underlying type
+ * (`__context_@ctx`) as function arguments.
*/
-#define token_capability_instance(cap, name) \
- extern const struct __capability_##cap *name
+#define token_context_instance(ctx, name) \
+ extern const struct __context_##ctx *name
/*
- * Common keywords for static capability analysis.
+ * Common keywords for static context analysis.
*/
/**
- * __must_hold() - function attribute, caller must hold exclusive capability
+ * __must_hold() - function attribute, caller must hold exclusive context
*
- * Function attribute declaring that the caller must hold the given capability
+ * Function attribute declaring that the caller must hold the given context
* instance(s) exclusively.
*/
-#define __must_hold(...) __requires_cap(__VA_ARGS__)
+#define __must_hold(...) __requires_ctx(__VA_ARGS__)
/**
- * __must_not_hold() - function attribute, caller must not hold capability
+ * __must_not_hold() - function attribute, caller must not hold context
*
* Function attribute declaring that the caller must not hold the given
- * capability instance(s).
+ * context instance(s).
*/
-#define __must_not_hold(...) __excludes_cap(__VA_ARGS__)
+#define __must_not_hold(...) __excludes_ctx(__VA_ARGS__)
/**
- * __acquires() - function attribute, function acquires capability exclusively
+ * __acquires() - function attribute, function acquires context exclusively
*
* Function attribute declaring that the function acquires the given
- * capability instance(s) exclusively, but does not release them.
+ * context instance(s) exclusively, but does not release them.
*/
-#define __acquires(...) __acquires_cap(__VA_ARGS__)
+#define __acquires(...) __acquires_ctx(__VA_ARGS__)
/*
* Clang's analysis does not care precisely about the value, only that it is
@@ -281,75 +282,75 @@ static inline void _capability_unsafe_alias(void **p) { }
* misleading if we say that @ret is the value returned if acquired. Instead,
* provide symbolic variants which we translate.
*/
-#define __cond_acquires_impl_true(x, ...) __try_acquires##__VA_ARGS__##_cap(1, x)
-#define __cond_acquires_impl_false(x, ...) __try_acquires##__VA_ARGS__##_cap(0, x)
-#define __cond_acquires_impl_nonzero(x, ...) __try_acquires##__VA_ARGS__##_cap(1, x)
-#define __cond_acquires_impl_0(x, ...) __try_acquires##__VA_ARGS__##_cap(0, x)
-#define __cond_acquires_impl_nonnull(x, ...) __try_acquires##__VA_ARGS__##_cap(1, x)
-#define __cond_acquires_impl_NULL(x, ...) __try_acquires##__VA_ARGS__##_cap(0, x)
+#define __cond_acquires_impl_true(x, ...) __try_acquires##__VA_ARGS__##_ctx(1, x)
+#define __cond_acquires_impl_false(x, ...) __try_acquires##__VA_ARGS__##_ctx(0, x)
+#define __cond_acquires_impl_nonzero(x, ...) __try_acquires##__VA_ARGS__##_ctx(1, x)
+#define __cond_acquires_impl_0(x, ...) __try_acquires##__VA_ARGS__##_ctx(0, x)
+#define __cond_acquires_impl_nonnull(x, ...) __try_acquires##__VA_ARGS__##_ctx(1, x)
+#define __cond_acquires_impl_NULL(x, ...) __try_acquires##__VA_ARGS__##_ctx(0, x)
/**
* __cond_acquires() - function attribute, function conditionally
- * acquires a capability exclusively
- * @ret: abstract value returned by function if capability acquired
- * @x: capability instance pointer
+ * acquires a context exclusively
+ * @ret: abstract value returned by function if context acquired
+ * @x: context instance pointer
*
* Function attribute declaring that the function conditionally acquires the
- * given capability instance @x exclusively, but does not release it. The
- * function return value @ret denotes when the capability is acquired.
+ * given context instance @x exclusively, but does not release it. The
+ * function return value @ret denotes when the context is acquired.
*
* @ret may be one of: true, false, nonzero, 0, nonnull, NULL.
*/
#define __cond_acquires(ret, x) __cond_acquires_impl_##ret(x)
/**
- * __releases() - function attribute, function releases a capability exclusively
+ * __releases() - function attribute, function releases a context exclusively
*
- * Function attribute declaring that the function releases the given capability
- * instance(s) exclusively. The capability must be held on entry.
+ * Function attribute declaring that the function releases the given context
+ * instance(s) exclusively. The context must be held on entry.
*/
-#define __releases(...) __releases_cap(__VA_ARGS__)
+#define __releases(...) __releases_ctx(__VA_ARGS__)
/**
- * __acquire() - function to acquire capability exclusively
- * @x: capability instance pointer
+ * __acquire() - function to acquire context exclusively
+ * @x: context instance pointer
*
- * No-op function that acquires the given capability instance @x exclusively.
+ * No-op function that acquires the given context instance @x exclusively.
*/
-#define __acquire(x) __acquire_cap(x)
+#define __acquire(x) __acquire_ctx(x)
/**
- * __release() - function to release capability exclusively
- * @x: capability instance pointer
+ * __release() - function to release context exclusively
+ * @x: context instance pointer
*
- * No-op function that releases the given capability instance @x.
+ * No-op function that releases the given context instance @x.
*/
-#define __release(x) __release_cap(x)
+#define __release(x) __release_ctx(x)
/**
- * __must_hold_shared() - function attribute, caller must hold shared capability
+ * __must_hold_shared() - function attribute, caller must hold shared context
*
- * Function attribute declaring that the caller must hold the given capability
+ * Function attribute declaring that the caller must hold the given context
* instance(s) with shared access.
*/
-#define __must_hold_shared(...) __requires_shared_cap(__VA_ARGS__)
+#define __must_hold_shared(...) __requires_shared_ctx(__VA_ARGS__)
/**
- * __acquires_shared() - function attribute, function acquires capability shared
+ * __acquires_shared() - function attribute, function acquires context shared
*
* Function attribute declaring that the function acquires the given
- * capability instance(s) with shared access, but does not release them.
+ * context instance(s) with shared access, but does not release them.
*/
-#define __acquires_shared(...) __acquires_shared_cap(__VA_ARGS__)
+#define __acquires_shared(...) __acquires_shared_ctx(__VA_ARGS__)
/**
* __cond_acquires_shared() - function attribute, function conditionally
- * acquires a capability shared
- * @ret: abstract value returned by function if capability acquired
+ * acquires a context shared
+ * @ret: abstract value returned by function if context acquired
*
* Function attribute declaring that the function conditionally acquires the
- * given capability instance @x with shared access, but does not release it. The
- * function return value @ret denotes when the capability is acquired.
+ * given context instance @x with shared access, but does not release it. The
+ * function return value @ret denotes when the context is acquired.
*
* @ret may be one of: true, false, nonzero, 0, nonnull, NULL.
*/
@@ -357,33 +358,33 @@ static inline void _capability_unsafe_alias(void **p) { }
/**
* __releases_shared() - function attribute, function releases a
- * capability shared
+ * context shared
*
- * Function attribute declaring that the function releases the given capability
- * instance(s) with shared access. The capability must be held on entry.
+ * Function attribute declaring that the function releases the given context
+ * instance(s) with shared access. The context must be held on entry.
*/
-#define __releases_shared(...) __releases_shared_cap(__VA_ARGS__)
+#define __releases_shared(...) __releases_shared_ctx(__VA_ARGS__)
/**
- * __acquire_shared() - function to acquire capability shared
- * @x: capability instance pointer
+ * __acquire_shared() - function to acquire context shared
+ * @x: context instance pointer
*
- * No-op function that acquires the given capability instance @x with shared
+ * No-op function that acquires the given context instance @x with shared
* access.
*/
-#define __acquire_shared(x) __acquire_shared_cap(x)
+#define __acquire_shared(x) __acquire_shared_ctx(x)
/**
- * __release_shared() - function to release capability shared
- * @x: capability instance pointer
+ * __release_shared() - function to release context shared
+ * @x: context instance pointer
*
- * No-op function that releases the given capability instance @x with shared
+ * No-op function that releases the given context instance @x with shared
* access.
*/
-#define __release_shared(x) __release_shared_cap(x)
+#define __release_shared(x) __release_shared_ctx(x)
/**
- * __acquire_ret() - helper to acquire capability of return value
+ * __acquire_ret() - helper to acquire context of return value
* @call: call expression
* @ret_expr: acquire expression that uses __ret
*/
@@ -395,7 +396,7 @@ static inline void _capability_unsafe_alias(void **p) { }
})
/**
- * __acquire_shared_ret() - helper to acquire capability shared of return value
+ * __acquire_shared_ret() - helper to acquire context shared of return value
* @call: call expression
* @ret_expr: acquire shared expression that uses __ret
*/
@@ -407,7 +408,7 @@ static inline void _capability_unsafe_alias(void **p) { }
})
/*
- * Attributes to mark functions returning acquired capabilities. This is purely
+ * Attributes to mark functions returning acquired contexts. This is purely
* cosmetic to help readability, and should be used with the above macros as
* follows:
*
@@ -417,7 +418,7 @@ static inline void _capability_unsafe_alias(void **p) { }
* struct foo *_myfunc(int bar) __acquires_ret;
* ...
*/
-#define __acquires_ret __no_capability_analysis
-#define __acquires_shared_ret __no_capability_analysis
+#define __acquires_ret __no_context_analysis
+#define __acquires_shared_ret __no_context_analysis
-#endif /* _LINUX_COMPILER_CAPABILITY_ANALYSIS_H */
+#endif /* _LINUX_COMPILER_CONTEXT_ANALYSIS_H */
^ permalink raw reply related
* Re: [PATCH v4 11/12] bpftool: Add support for signing BPF programs
From: Quentin Monnet @ 2025-09-18 21:04 UTC (permalink / raw)
To: KP Singh, bpf, linux-security-module
Cc: bboscaccy, paul, kys, ast, daniel, andrii
In-Reply-To: <20250914215141.15144-12-kpsingh@kernel.org>
2025-09-14 23:51 UTC+0200 ~ KP Singh <kpsingh@kernel.org>
> Two modes of operation being added:
>
> Add two modes of operation:
>
> * For prog load, allow signing a program immediately before loading. This
> is essential for command-line testing and administration.
>
> bpftool prog load -S -k <private_key> -i <identity_cert> fentry_test.bpf.o
>
> * For gen skeleton, embed a pre-generated signature into the C skeleton
> file. This supports the use of signed programs in compiled applications.
>
> bpftool gen skeleton -S -k <private_key> -i <identity_cert> fentry_test.bpf.o
>
> Generation of the loader program and its metadata map is implemented in
> libbpf (bpf_obj__gen_loader). bpftool generates a skeleton that loads
> the program and automates the required steps: freezing the map, creating
> an exclusive map, loading, and running. Users can use standard libbpf
> APIs directly or integrate loader program generation into their own
> toolchains.
>
> Signed-off-by: KP Singh <kpsingh@kernel.org>
Hi KP, thanks for this work! Apologies for the delay, I know I've missed
v3 - and I still have some small nits from bpftool's side.
> ---
> .../bpf/bpftool/Documentation/bpftool-gen.rst | 16 +-
> .../bpftool/Documentation/bpftool-prog.rst | 18 +-
> tools/bpf/bpftool/Makefile | 6 +-
> tools/bpf/bpftool/cgroup.c | 4 +
> tools/bpf/bpftool/gen.c | 66 +++++-
> tools/bpf/bpftool/main.c | 26 ++-
> tools/bpf/bpftool/main.h | 11 +
> tools/bpf/bpftool/prog.c | 27 ++-
> tools/bpf/bpftool/sign.c | 212 ++++++++++++++++++
We miss the bash completion update.
> 9 files changed, 373 insertions(+), 13 deletions(-)
> create mode 100644 tools/bpf/bpftool/sign.c
>
> diff --git a/tools/bpf/bpftool/Documentation/bpftool-gen.rst b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> index ca860fd97d8d..cef469d758ed 100644
> --- a/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> +++ b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
> @@ -16,7 +16,8 @@ SYNOPSIS
>
> **bpftool** [*OPTIONS*] **gen** *COMMAND*
>
> -*OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } }
> +*OPTIONS* := { |COMMON_OPTIONS| [ { **-L** | **--use-loader** } ]
> +[ { { **-S** | **--sign** } **-k** <private_key.pem> **-i** <certificate.x509> } ] }}
Please don't remove the "|" separators. I understand we may use several
of these options on the command line, but if we remove them this should
be done consistently over all documentation pages.
>
> *COMMAND* := { **object** | **skeleton** | **help** }
>
> @@ -186,6 +187,19 @@ OPTIONS
> skeleton). A light skeleton contains a loader eBPF program. It does not use
> the majority of the libbpf infrastructure, and does not need libelf.
>
> +-S, --sign
> + For skeletons, generate a signed skeleton. This option must be used with
> + **-k** and **-i**. Using this flag implicitly enables **--use-loader**.
> + See the "Signed Skeletons" section in the description of the
> + **gen skeleton** command for more details.
404: Section not found!
> +
> +-k <private_key.pem>
> + Path to the private key file in PEM format, required for signing.
> +
> +-i <certificate.x509>
> + Path to the X.509 certificate file in PEM or DER format, required for
> + signing.
> +
> EXAMPLES
> ========
> **$ cat example1.bpf.c**
> diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
> index f69fd92df8d8..55b812761df2 100644
> --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst
> +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
> @@ -16,9 +16,9 @@ SYNOPSIS
>
> **bpftool** [*OPTIONS*] **prog** *COMMAND*
>
> -*OPTIONS* := { |COMMON_OPTIONS| |
> -{ **-f** | **--bpffs** } | { **-m** | **--mapcompat** } | { **-n** | **--nomount** } |
> -{ **-L** | **--use-loader** } }
> +*OPTIONS* := { |COMMON_OPTIONS| [ { **-f** | **--bpffs** } ] [ { **-m** | **--mapcompat** } ]
> +[ { **-n** | **--nomount** } ] [ { **-L** | **--use-loader** } ]
> +[ { { **-S** | **--sign** } **-k** <private_key.pem> **-i** <certificate.x509> } ] }
Same for "|" separators
>
> *COMMANDS* :=
> { **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load** |
> @@ -248,6 +248,18 @@ OPTIONS
> creating the maps, and loading the programs (see **bpftool prog tracelog**
> as a way to dump those messages).
>
> +-S, --sign
> + Enable signing of the BPF program before loading. This option must be
> + used with **-k** and **-i**. Using this flag implicitly enables
> + **--use-loader**.
> +
> +-k <private_key.pem>
> + Path to the private key file in PEM format, required when signing.
> +
> +-i <certificate.x509>
> + Path to the X.509 certificate file in PEM or DER format, required when
> + signing.
> +
> EXAMPLES
> ========
> **# bpftool prog show**
> diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
> index 67a60114368f..694e61f1909e 100644
> --- a/tools/bpf/bpftool/gen.c
> +++ b/tools/bpf/bpftool/gen.c
> @@ -1930,7 +1988,7 @@ static int do_help(int argc, char **argv)
> " %1$s %2$s help\n"
> "\n"
> " " HELP_SPEC_OPTIONS " |\n"
> - " {-L|--use-loader} }\n"
> + " {-L|--use-loader} | [ {-S|--sign } {-k} <private_key.pem> {-i} <certificate.x509> ]}\n"
Nit: No need for curly braces when you just have a short option name,
for "-k" and "-i".
> "",
> bin_name, "gen");
>
> diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
> index 0f1183b2ed0a..c78eb80b9c94 100644
> --- a/tools/bpf/bpftool/main.c
> +++ b/tools/bpf/bpftool/main.c
> @@ -33,6 +33,9 @@ bool relaxed_maps;
> bool use_loader;
> struct btf *base_btf;
> struct hashmap *refs_table;
> +bool sign_progs;
> +const char *private_key_path;
> +const char *cert_path;
>
> static void __noreturn clean_and_exit(int i)
> {
> @@ -448,6 +451,7 @@ int main(int argc, char **argv)
> { "nomount", no_argument, NULL, 'n' },
> { "debug", no_argument, NULL, 'd' },
> { "use-loader", no_argument, NULL, 'L' },
> + { "sign", no_argument, NULL, 'S' },
> { "base-btf", required_argument, NULL, 'B' },
> { 0 }
> };
> @@ -474,7 +478,7 @@ int main(int argc, char **argv)
> bin_name = "bpftool";
>
> opterr = 0;
> - while ((opt = getopt_long(argc, argv, "VhpjfLmndB:l",
> + while ((opt = getopt_long(argc, argv, "VhpjfLmndSi:k:B:l",
> options, NULL)) >= 0) {
> switch (opt) {
> case 'V':
> @@ -520,6 +524,16 @@ int main(int argc, char **argv)
> case 'L':
> use_loader = true;
> break;
> + case 'S':
> + sign_progs = true;
> + use_loader = true;
> + break;
> + case 'k':
> + private_key_path = optarg;
> + break;
> + case 'i':
> + cert_path = optarg;
> + break;
> default:
> p_err("unrecognized option '%s'", argv[optind - 1]);
> if (json_output)
> @@ -534,6 +548,16 @@ int main(int argc, char **argv)
> if (argc < 0)
> usage();
>
> + if (sign_progs && (private_key_path == NULL || cert_path == NULL)) {
> + p_err("-i <identity_x509_cert> and -k <private> key must be supplied with -S for signing");
> + return -EINVAL;
> + }
> +
> + if (!sign_progs && (private_key_path != NULL || cert_path != NULL)) {
> + p_err("-i <identity_x509_cert> and -k <private> also need --sign to be used for sign programs");
Typo: s/to be used for sign/to sign/
> + return -EINVAL;
> + }
> +
> if (version_requested)
> ret = do_version(argc, argv);
> else
> diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
> index cf18c3879680..f78a5135f104 100644
> --- a/tools/bpf/bpftool/prog.c
> +++ b/tools/bpf/bpftool/prog.c
> @@ -1953,6 +1956,24 @@ static int try_loader(struct gen_loader_opts *gen)
> opts.insns = gen->insns;
> opts.insns_sz = gen->insns_sz;
> fds_before = count_open_fds();
> +
> + if (sign_progs) {
> + opts.excl_prog_hash = prog_sha;
> + opts.excl_prog_hash_sz = sizeof(prog_sha);
> + opts.signature = sig_buf;
> + opts.signature_sz = MAX_SIG_SIZE;
> + opts.keyring_id = KEY_SPEC_SESSION_KEYRING;
> +
> + err = bpftool_prog_sign(&opts);
> + if (err < 0)
> + return err;
On error here, I think you need the same as below: an error message, and
a "goto out" to free log_buf.
> +
> + err = register_session_key(cert_path);
> + if (err < 0) {
> + p_err("failed to add session key");
> + goto out;
> + }
> + }
> err = bpf_load_and_run(&opts);
> fd_delta = count_open_fds() - fds_before;
> if (err < 0 || verifier_logs) {
> @@ -1961,6 +1982,7 @@ static int try_loader(struct gen_loader_opts *gen)
> fprintf(stderr, "loader prog leaked %d FDs\n",
> fd_delta);
> }
> +out:
> free(log_buf);
> return err;
> }
> @@ -1988,6 +2010,9 @@ static int do_loader(int argc, char **argv)
> goto err_close_obj;
> }
>
> + if (sign_progs)
> + gen.gen_hash = true;
> +
> err = bpf_object__gen_loader(obj, &gen);
> if (err)
> goto err_close_obj;
> @@ -2562,7 +2587,7 @@ static int do_help(int argc, char **argv)
> " METRIC := { cycles | instructions | l1d_loads | llc_misses | itlb_misses | dtlb_misses }\n"
> " " HELP_SPEC_OPTIONS " |\n"
> " {-f|--bpffs} | {-m|--mapcompat} | {-n|--nomount} |\n"
> - " {-L|--use-loader} }\n"
> + " {-L|--use-loader} | [ {-S|--sign } {-k} <private_key.pem> {-i} <certificate.x509> ] \n"
"... -k <private_key.pem> -i <certificate.x509> ..."
The rest of the patch looks good.
Thanks,
Quentin
^ permalink raw reply
* Re: [PATCH] tpm: Disable TPM2_TCG_HMAC by default
From: Jarkko Sakkinen @ 2025-09-18 20:52 UTC (permalink / raw)
To: Chris Fenner
Cc: Jonathan McDowell, linux-integrity, stable, Peter Huewe,
Jason Gunthorpe, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, James Bottomley, open list,
open list:KEYS/KEYRINGS, open list:SECURITY SUBSYSTEM
In-Reply-To: <CAMigqh2gJ+ALqxb8RcNFENJg-Z0FfKE2DZjaGdOER7G3AGZvKg@mail.gmail.com>
On Thu, Sep 18, 2025 at 12:50:57PM -0700, Chris Fenner wrote:
> Agreed, the feature needs some work in order to provide meaningful
> security value, and disabling it by default facilitates that work.
>
> Reviewed-By: Chris Fenner <cfenn@google.com>
Thanks!
BR, Jarkko
^ permalink raw reply
* Re: [PATCH v3 05/35] checkpatch: Warn about capability_unsafe() without comment
From: Joe Perches @ 2025-09-18 20:36 UTC (permalink / raw)
To: Marco Elver, Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon
Cc: David S. Miller, Luc Van Oostenryck, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Bill Wendling, Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
Jann Horn, Joel Fernandes, Jonathan Corbet, Josh Triplett,
Justin Stitt, Kees Cook, Kentaro Takeda, Lukas Bulwahn,
Mark Rutland, Mathieu Desnoyers, Miguel Ojeda, Nathan Chancellor,
Neeraj Upadhyay, Nick Desaulniers, Steven Rostedt, Tetsuo Handa,
Thomas Gleixner, Thomas Graf, Uladzislau Rezki, Waiman Long,
kasan-dev, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
linux-mm, linux-security-module, linux-sparse, llvm, rcu
In-Reply-To: <20250918140451.1289454-6-elver@google.com>
On Thu, 2025-09-18 at 15:59 +0200, Marco Elver wrote:
> Warn about applications of capability_unsafe() without a comment, to
> encourage documenting the reasoning behind why it was deemed safe.
[]
> diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
[]
> @@ -6717,6 +6717,14 @@ sub process {
> }
> }
>
> +# check for capability_unsafe without a comment.
> + if ($line =~ /\bcapability_unsafe\b/) {
> + if (!ctx_has_comment($first_line, $linenr)) {
> + WARN("CAPABILITY_UNSAFE",
> + "capability_unsafe without comment\n" . $herecurr);
while most of these are using the same multi-line style
I'd prefer combining and reducing indentation
if ($line =~ /\bcapability_unsafe\b/ &&
!ctx_has_comment($first_line, $linenr)) {
WARN(etc...
^ permalink raw reply
* Re: [PATCH] tpm: Disable TPM2_TCG_HMAC by default
From: Chris Fenner @ 2025-09-18 19:50 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: Jonathan McDowell, linux-integrity, stable, Peter Huewe,
Jason Gunthorpe, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, James Bottomley, open list,
open list:KEYS/KEYRINGS, open list:SECURITY SUBSYSTEM
In-Reply-To: <aMxZlHn9bfa5LGEU@kernel.org>
Agreed, the feature needs some work in order to provide meaningful
security value, and disabling it by default facilitates that work.
Reviewed-By: Chris Fenner <cfenn@google.com>
On Thu, Sep 18, 2025 at 12:12 PM Jarkko Sakkinen <jarkko@kernel.org> wrote:
>
> On Thu, Sep 18, 2025 at 07:56:53PM +0100, Jonathan McDowell wrote:
> > On Mon, Aug 25, 2025 at 11:32:23PM +0300, Jarkko Sakkinen wrote:
> > > After reading all the feedback, right now disabling the TPM2_TCG_HMAC
> > > is the right call.
> > >
> > > Other views discussed:
> > >
> > > A. Having a kernel command-line parameter or refining the feature
> > > otherwise. This goes to the area of improvements. E.g., one
> > > example is my own idea where the null key specific code would be
> > > replaced with a persistent handle parameter (which can be
> > > *unambigously* defined as part of attestation process when
> > > done correctly).
> > >
> > > B. Removing the code. I don't buy this because that is same as saying
> > > that HMAC encryption cannot work at all (if really nitpicking) in
> > > any form. Also I disagree on the view that the feature could not
> > > be refined to something more reasoable.
> > >
> > > Also, both A and B are worst options in terms of backporting.
> > >
> > > Thus, this is the best possible choice.
> >
> > I think this is reasonable; it's adding runtime overhead and not adding
> > enough benefit to be the default upstream.
>
> Yes, I think this is a balanced change. I agree what you say and at the
> same time this gives more space to refine it something usable. Right now
> it is much harder to tackle those issue, as it is part of the default
> config. By looking at things from this angle, the change is also
> benefical for the feature itself (in the long run).
>
> > Reviewed-By: Jonathan McDowell <noodles@earth.li>
>
> Thank you! I appreciate this and will append this to the commit.
>
> BR, Jarkko
^ permalink raw reply
* [syzbot ci] Re: Compiler-Based Capability- and Locking-Analysis
From: syzbot ci @ 2025-09-18 19:41 UTC (permalink / raw)
To: arnd, boqun.feng, bvanassche, corbet, davem, dvyukov, edumazet,
elver, frederic, glider, gregkh, hch, herbert, irogers, jannh,
joelagnelf, josh, justinstitt, kasan-dev, kees, linux-crypto,
linux-doc, linux-kbuild, linux-kernel, linux-mm,
linux-security-module, linux-sparse, llvm, longman,
luc.vanoostenryck, lukas.bulwahn, mark.rutland, mathieu.desnoyers,
mingo, mingo, morbo, nathan, neeraj.upadhyay, nick.desaulniers,
ojeda, paulmck, penguin-kernel, peterz, rcu, rostedt, takedakn,
tglx, tgraf, urezki, will
Cc: syzbot, syzkaller-bugs
In-Reply-To: <20250918140451.1289454-1-elver@google.com>
syzbot ci has tested the following series
[v3] Compiler-Based Capability- and Locking-Analysis
https://lore.kernel.org/all/20250918140451.1289454-1-elver@google.com
* [PATCH v3 01/35] compiler_types: Move lock checking attributes to compiler-capability-analysis.h
* [PATCH v3 02/35] compiler-capability-analysis: Add infrastructure for Clang's capability analysis
* [PATCH v3 03/35] compiler-capability-analysis: Add test stub
* [PATCH v3 04/35] Documentation: Add documentation for Compiler-Based Capability Analysis
* [PATCH v3 05/35] checkpatch: Warn about capability_unsafe() without comment
* [PATCH v3 06/35] cleanup: Basic compatibility with capability analysis
* [PATCH v3 07/35] lockdep: Annotate lockdep assertions for capability analysis
* [PATCH v3 08/35] locking/rwlock, spinlock: Support Clang's capability analysis
* [PATCH v3 09/35] compiler-capability-analysis: Change __cond_acquires to take return value
* [PATCH v3 10/35] locking/mutex: Support Clang's capability analysis
* [PATCH v3 11/35] locking/seqlock: Support Clang's capability analysis
* [PATCH v3 12/35] bit_spinlock: Include missing <asm/processor.h>
* [PATCH v3 13/35] bit_spinlock: Support Clang's capability analysis
* [PATCH v3 14/35] rcu: Support Clang's capability analysis
* [PATCH v3 15/35] srcu: Support Clang's capability analysis
* [PATCH v3 16/35] kref: Add capability-analysis annotations
* [PATCH v3 17/35] locking/rwsem: Support Clang's capability analysis
* [PATCH v3 18/35] locking/local_lock: Include missing headers
* [PATCH v3 19/35] locking/local_lock: Support Clang's capability analysis
* [PATCH v3 20/35] locking/ww_mutex: Support Clang's capability analysis
* [PATCH v3 21/35] debugfs: Make debugfs_cancellation a capability struct
* [PATCH v3 22/35] compiler-capability-analysis: Remove Sparse support
* [PATCH v3 23/35] compiler-capability-analysis: Remove __cond_lock() function-like helper
* [PATCH v3 24/35] compiler-capability-analysis: Introduce header suppressions
* [PATCH v3 25/35] compiler: Let data_race() imply disabled capability analysis
* [PATCH v3 26/35] MAINTAINERS: Add entry for Capability Analysis
* [PATCH v3 27/35] kfence: Enable capability analysis
* [PATCH v3 28/35] kcov: Enable capability analysis
* [PATCH v3 29/35] kcsan: Enable capability analysis
* [PATCH v3 30/35] stackdepot: Enable capability analysis
* [PATCH v3 31/35] rhashtable: Enable capability analysis
* [PATCH v3 32/35] printk: Move locking annotation to printk.c
* [PATCH v3 33/35] security/tomoyo: Enable capability analysis
* [PATCH v3 34/35] crypto: Enable capability analysis
* [PATCH v3 35/35] sched: Enable capability analysis for core.c and fair.c
and found the following issue:
general protection fault in validate_page_before_insert
Full report is available here:
https://ci.syzbot.org/series/81182522-74c0-4494-bcf8-976133df7dc7
***
general protection fault in validate_page_before_insert
tree: torvalds
URL: https://kernel.googlesource.com/pub/scm/linux/kernel/git/torvalds/linux
base: f83ec76bf285bea5727f478a68b894f5543ca76e
arch: amd64
compiler: Debian clang version 20.1.8 (++20250708063551+0c9f909b7976-1~exp1~20250708183702.136), Debian LLD 20.1.8
config: https://ci.syzbot.org/builds/8f7ff868-4cf7-40da-b62b-45ebfec4e994/config
cgroup: Unknown subsys name 'net'
cgroup: Unknown subsys name 'cpuset'
cgroup: Unknown subsys name 'rlimit'
Oops: general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] SMP KASAN PTI
KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f]
CPU: 0 UID: 0 PID: 5775 Comm: syz-executor Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
RIP: 0010:validate_page_before_insert+0x2a/0x300
Code: 55 41 57 41 56 41 55 41 54 53 48 89 f3 49 89 fe 49 bd 00 00 00 00 00 fc ff df e8 f1 3f b3 ff 4c 8d 7b 08 4c 89 f8 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 ff e8 17 b3 16 00 4d 8b 3f 4c 89 fe 48
RSP: 0018:ffffc90002a5f608 EFLAGS: 00010202
RAX: 0000000000000001 RBX: 0000000000000000 RCX: ffff888022891cc0
RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff888028c71200
RBP: ffffc90002a5f720 R08: 0000000000000000 R09: 1ffff11021cf81e0
R10: dffffc0000000000 R11: ffffed1021cf81e1 R12: dffffc0000000000
R13: dffffc0000000000 R14: ffff888028c71200 R15: 0000000000000008
FS: 00005555815ad500(0000) GS:ffff8880b8615000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f1788fd20b8 CR3: 000000010d8a4000 CR4: 00000000000006f0
Call Trace:
<TASK>
insert_page+0x90/0x2c0
kcov_mmap+0xc3/0x130
mmap_region+0x18ae/0x20c0
do_mmap+0xc45/0x10d0
vm_mmap_pgoff+0x2a6/0x4d0
ksys_mmap_pgoff+0x51f/0x760
do_syscall_64+0xfa/0x3b0
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f1788d8ebe3
Code: f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 00 41 89 ca 41 f7 c1 ff 0f 00 00 75 14 b8 09 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 25 c3 0f 1f 40 00 48 c7 c0 a8 ff ff ff 64 c7
RSP: 002b:00007ffc8a37e638 EFLAGS: 00000246 ORIG_RAX: 0000000000000009
RAX: ffffffffffffffda RBX: 00007ffc8a37e670 RCX: 00007f1788d8ebe3
RDX: 0000000000000003 RSI: 0000000000400000 RDI: 00007f17867ff000
RBP: 00007ffc8a37e940 R08: 00000000000000d8 R09: 0000000000000000
R10: 0000000000000011 R11: 0000000000000246 R12: 0000000000000003
R13: 0000000000000000 R14: 00007f1788fa11c0 R15: 00007f1788e2e478
</TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:validate_page_before_insert+0x2a/0x300
Code: 55 41 57 41 56 41 55 41 54 53 48 89 f3 49 89 fe 49 bd 00 00 00 00 00 fc ff df e8 f1 3f b3 ff 4c 8d 7b 08 4c 89 f8 48 c1 e8 03 <42> 80 3c 28 00 74 08 4c 89 ff e8 17 b3 16 00 4d 8b 3f 4c 89 fe 48
RSP: 0018:ffffc90002a5f608 EFLAGS: 00010202
RAX: 0000000000000001 RBX: 0000000000000000 RCX: ffff888022891cc0
RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff888028c71200
RBP: ffffc90002a5f720 R08: 0000000000000000 R09: 1ffff11021cf81e0
R10: dffffc0000000000 R11: ffffed1021cf81e1 R12: dffffc0000000000
R13: dffffc0000000000 R14: ffff888028c71200 R15: 0000000000000008
FS: 00005555815ad500(0000) GS:ffff8880b8615000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f1788fd20b8 CR3: 000000010d8a4000 CR4: 00000000000006f0
***
If these findings have caused you to resend the series or submit a
separate fix, please add the following tag to your commit message:
Tested-by: syzbot@syzkaller.appspotmail.com
---
This report is generated by a bot. It may contain errors.
syzbot ci engineers can be reached at syzkaller@googlegroups.com.
^ permalink raw reply
* Re: [PATCH v3 00/35] Compiler-Based Capability- and Locking-Analysis
From: Nathan Chancellor @ 2025-09-18 19:40 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Marco Elver, Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon,
David S. Miller, Luc Van Oostenryck, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Bill Wendling, Dmitry Vyukov, Eric Dumazet, Frederic Weisbecker,
Greg Kroah-Hartman, Herbert Xu, Ian Rogers, Jann Horn,
Joel Fernandes, Jonathan Corbet, Josh Triplett, Justin Stitt,
Kees Cook, Kentaro Takeda, Lukas Bulwahn, Mark Rutland,
Mathieu Desnoyers, Miguel Ojeda, Neeraj Upadhyay,
Nick Desaulniers, Steven Rostedt, Tetsuo Handa, Thomas Gleixner,
Thomas Graf, Uladzislau Rezki, Waiman Long, kasan-dev,
linux-crypto, linux-doc, linux-kbuild, linux-kernel, linux-mm,
linux-security-module, linux-sparse, llvm, rcu
In-Reply-To: <20250918174555.GA3366400@ax162>
On Thu, Sep 18, 2025 at 10:45:55AM -0700, Nathan Chancellor wrote:
> On Thu, Sep 18, 2025 at 04:15:11PM +0200, Christoph Hellwig wrote:
> > On Thu, Sep 18, 2025 at 03:59:11PM +0200, Marco Elver wrote:
> > > A Clang version that supports `-Wthread-safety-pointer` and the new
> > > alias-analysis of capability pointers is required (from this version
> > > onwards):
> > >
> > > https://github.com/llvm/llvm-project/commit/b4c98fcbe1504841203e610c351a3227f36c92a4 [3]
> >
> > There's no chance to make say x86 pre-built binaries for that available?
>
> I can use my existing kernel.org LLVM [1] build infrastructure to
> generate prebuilt x86 binaries. Just give me a bit to build and upload
> them. You may not be the only developer or maintainer who may want to
> play with this.
This should include Marco's change, let me know if there are any issues.
https://kernel.org/pub/tools/llvm/files/prerelease/llvm-22.0.0-e19fa930ca838715028c00c234874d1db4f93154-20250918-184558-x86_64.tar.xz
Cheers,
Nathan
^ permalink raw reply
* Re: [PATCH v2] tpm: use a map for tpm2_calc_ordinal_duration()
From: Jarkko Sakkinen @ 2025-09-18 19:37 UTC (permalink / raw)
To: linux-integrity
Cc: Frédéric Jouen, Peter Huewe, Jason Gunthorpe,
James Bottomley, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM
In-Reply-To: <20250918193019.4018706-1-jarkko@kernel.org>
On Thu, Sep 18, 2025 at 10:30:18PM +0300, Jarkko Sakkinen wrote:
> The current shenanigans for duration calculation introduce too much
> complexity for a trivial problem, and further the code is hard to patch and
> maintain.
>
> Address these issues with a flat look-up table, which is easy to understand
> and patch. If leaf driver specific patching is required in future, it is
> easy enough to make a copy of this table during driver initialization and
> add the chip parameter back.
>
> 'chip->duration' is retained for TPM 1.x.
>
> As the first entry for this new behavior address TCG spec update mentioned
> in this issue:
>
> https://github.com/raspberrypi/linux/issues/7054
>
> Therefore, for TPM_SelfTest the duration is set to 3000 ms.
>
> This does not categorize a as bug, given that this is introduced to the
> spec after the feature was originally made.
>
> Cc: Frédéric Jouen <fjouen@sealsq.com>
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> ---
> v2:
> - Add the missing msec_to_jiffies() calls.
> - Drop redundant stuff.
Run also through kselftest.
BR, Jarkko
^ permalink raw reply
* [PATCH v2] tpm: use a map for tpm2_calc_ordinal_duration()
From: Jarkko Sakkinen @ 2025-09-18 19:30 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Frédéric Jouen, Peter Huewe,
Jason Gunthorpe, James Bottomley, Mimi Zohar, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, open list,
open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
The current shenanigans for duration calculation introduce too much
complexity for a trivial problem, and further the code is hard to patch and
maintain.
Address these issues with a flat look-up table, which is easy to understand
and patch. If leaf driver specific patching is required in future, it is
easy enough to make a copy of this table during driver initialization and
add the chip parameter back.
'chip->duration' is retained for TPM 1.x.
As the first entry for this new behavior address TCG spec update mentioned
in this issue:
https://github.com/raspberrypi/linux/issues/7054
Therefore, for TPM_SelfTest the duration is set to 3000 ms.
This does not categorize a as bug, given that this is introduced to the
spec after the feature was originally made.
Cc: Frédéric Jouen <fjouen@sealsq.com>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
v2:
- Add the missing msec_to_jiffies() calls.
- Drop redundant stuff.
---
drivers/char/tpm/tpm-interface.c | 2 +-
drivers/char/tpm/tpm.h | 2 +-
drivers/char/tpm/tpm2-cmd.c | 127 ++++++++-----------------------
include/linux/tpm.h | 5 +-
4 files changed, 37 insertions(+), 99 deletions(-)
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index b71725827743..c9f173001d0e 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -52,7 +52,7 @@ MODULE_PARM_DESC(suspend_pcr,
unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
{
if (chip->flags & TPM_CHIP_FLAG_TPM2)
- return tpm2_calc_ordinal_duration(chip, ordinal);
+ return tpm2_calc_ordinal_duration(ordinal);
else
return tpm1_calc_ordinal_duration(chip, ordinal);
}
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 7bb87fa5f7a1..2726bd38e5ac 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -299,7 +299,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,
ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip);
int tpm2_auto_startup(struct tpm_chip *chip);
void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type);
-unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal);
+unsigned long tpm2_calc_ordinal_duration(u32 ordinal);
int tpm2_probe(struct tpm_chip *chip);
int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip);
int tpm2_find_cc(struct tpm_chip *chip, u32 cc);
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 524d802ede26..7d77f6fbc152 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -28,120 +28,57 @@ static struct tpm2_hash tpm2_hash_map[] = {
int tpm2_get_timeouts(struct tpm_chip *chip)
{
- /* Fixed timeouts for TPM2 */
chip->timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A);
chip->timeout_b = msecs_to_jiffies(TPM2_TIMEOUT_B);
chip->timeout_c = msecs_to_jiffies(TPM2_TIMEOUT_C);
chip->timeout_d = msecs_to_jiffies(TPM2_TIMEOUT_D);
-
- /* PTP spec timeouts */
- chip->duration[TPM_SHORT] = msecs_to_jiffies(TPM2_DURATION_SHORT);
- chip->duration[TPM_MEDIUM] = msecs_to_jiffies(TPM2_DURATION_MEDIUM);
- chip->duration[TPM_LONG] = msecs_to_jiffies(TPM2_DURATION_LONG);
-
- /* Key creation commands long timeouts */
- chip->duration[TPM_LONG_LONG] =
- msecs_to_jiffies(TPM2_DURATION_LONG_LONG);
-
chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS;
-
return 0;
}
-/**
- * tpm2_ordinal_duration_index() - returns an index to the chip duration table
- * @ordinal: TPM command ordinal.
- *
- * The function returns an index to the chip duration table
- * (enum tpm_duration), that describes the maximum amount of
- * time the chip could take to return the result for a particular ordinal.
- *
- * The values of the MEDIUM, and LONG durations are taken
- * from the PC Client Profile (PTP) specification (750, 2000 msec)
- *
- * LONG_LONG is for commands that generates keys which empirically takes
- * a longer time on some systems.
- *
- * Return:
- * * TPM_MEDIUM
- * * TPM_LONG
- * * TPM_LONG_LONG
- * * TPM_UNDEFINED
+/*
+ * Contains the maximum durations in milliseconds for TPM2 commands.
*/
-static u8 tpm2_ordinal_duration_index(u32 ordinal)
-{
- switch (ordinal) {
- /* Startup */
- case TPM2_CC_STARTUP: /* 144 */
- return TPM_MEDIUM;
-
- case TPM2_CC_SELF_TEST: /* 143 */
- return TPM_LONG;
-
- case TPM2_CC_GET_RANDOM: /* 17B */
- return TPM_LONG;
-
- case TPM2_CC_SEQUENCE_UPDATE: /* 15C */
- return TPM_MEDIUM;
- case TPM2_CC_SEQUENCE_COMPLETE: /* 13E */
- return TPM_MEDIUM;
- case TPM2_CC_EVENT_SEQUENCE_COMPLETE: /* 185 */
- return TPM_MEDIUM;
- case TPM2_CC_HASH_SEQUENCE_START: /* 186 */
- return TPM_MEDIUM;
-
- case TPM2_CC_VERIFY_SIGNATURE: /* 177 */
- return TPM_LONG_LONG;
-
- case TPM2_CC_PCR_EXTEND: /* 182 */
- return TPM_MEDIUM;
-
- case TPM2_CC_HIERARCHY_CONTROL: /* 121 */
- return TPM_LONG;
- case TPM2_CC_HIERARCHY_CHANGE_AUTH: /* 129 */
- return TPM_LONG;
-
- case TPM2_CC_GET_CAPABILITY: /* 17A */
- return TPM_MEDIUM;
-
- case TPM2_CC_NV_READ: /* 14E */
- return TPM_LONG;
-
- case TPM2_CC_CREATE_PRIMARY: /* 131 */
- return TPM_LONG_LONG;
- case TPM2_CC_CREATE: /* 153 */
- return TPM_LONG_LONG;
- case TPM2_CC_CREATE_LOADED: /* 191 */
- return TPM_LONG_LONG;
-
- default:
- return TPM_UNDEFINED;
- }
-}
+static const struct {
+ unsigned long ordinal;
+ unsigned long duration;
+} tpm2_ordinal_duration_map[] = {
+ {TPM2_CC_STARTUP, 750},
+ {TPM2_CC_SELF_TEST, 3000},
+ {TPM2_CC_GET_RANDOM, 2000},
+ {TPM2_CC_SEQUENCE_UPDATE, 750},
+ {TPM2_CC_SEQUENCE_COMPLETE, 750},
+ {TPM2_CC_EVENT_SEQUENCE_COMPLETE, 750},
+ {TPM2_CC_HASH_SEQUENCE_START, 750},
+ {TPM2_CC_VERIFY_SIGNATURE, 30000},
+ {TPM2_CC_PCR_EXTEND, 750},
+ {TPM2_CC_HIERARCHY_CONTROL, 2000},
+ {TPM2_CC_HIERARCHY_CHANGE_AUTH, 2000},
+ {TPM2_CC_GET_CAPABILITY, 750},
+ {TPM2_CC_NV_READ, 2000},
+ {TPM2_CC_CREATE_PRIMARY, 30000},
+ {TPM2_CC_CREATE, 30000},
+ {TPM2_CC_CREATE_LOADED, 30000},
+};
/**
- * tpm2_calc_ordinal_duration() - calculate the maximum command duration
- * @chip: TPM chip to use.
+ * tpm2_calc_ordinal_duration() - Calculate the maximum command duration
* @ordinal: TPM command ordinal.
*
- * The function returns the maximum amount of time the chip could take
- * to return the result for a particular ordinal in jiffies.
- *
- * Return: A maximal duration time for an ordinal in jiffies.
+ * Returns the maximum amount of time the chip is expected by kernel to
+ * take in jiffies.
*/
-unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
+unsigned long tpm2_calc_ordinal_duration(u32 ordinal)
{
- unsigned int index;
+ int i;
- index = tpm2_ordinal_duration_index(ordinal);
+ for (i = 0; i < ARRAY_SIZE(tpm2_ordinal_duration_map); i++)
+ if (ordinal == tpm2_ordinal_duration_map[i].ordinal)
+ return msecs_to_jiffies(tpm2_ordinal_duration_map[i].duration);
- if (index != TPM_UNDEFINED)
- return chip->duration[index];
- else
- return msecs_to_jiffies(TPM2_DURATION_DEFAULT);
+ return msecs_to_jiffies(TPM2_DURATION_DEFAULT);
}
-
struct tpm2_pcr_read_out {
__be32 update_cnt;
__be32 pcr_selects_cnt;
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
index b0e9eb5ef022..dc0338a783f3 100644
--- a/include/linux/tpm.h
+++ b/include/linux/tpm.h
@@ -228,10 +228,11 @@ enum tpm2_timeouts {
TPM2_TIMEOUT_B = 4000,
TPM2_TIMEOUT_C = 200,
TPM2_TIMEOUT_D = 30,
+};
+
+enum tpm2_durations {
TPM2_DURATION_SHORT = 20,
- TPM2_DURATION_MEDIUM = 750,
TPM2_DURATION_LONG = 2000,
- TPM2_DURATION_LONG_LONG = 300000,
TPM2_DURATION_DEFAULT = 120000,
};
--
2.39.5
^ permalink raw reply related
* Re: [PATCH] tpm: use a map for tpm2_calc_ordinal_duration()
From: Jarkko Sakkinen @ 2025-09-18 19:14 UTC (permalink / raw)
To: linux-integrity
Cc: Frédéric Jouen, Peter Huewe, Jason Gunthorpe,
James Bottomley, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, open list, open list:KEYS-TRUSTED,
open list:SECURITY SUBSYSTEM
In-Reply-To: <20250918185730.3529317-1-jarkko@kernel.org>
On Thu, Sep 18, 2025 at 09:57:30PM +0300, Jarkko Sakkinen wrote:
> The current shenanigans for duration calculation introduce too much
> complexity for a trivial problem, and further the code is hard to patch and
> maintain.
>
> Address these issues with a flat look-up table, which is easy to understand
> and patch. If leaf driver specific patching is required in future, it is
> easy enough to make a copy of this table during driver initialization and
> add the chip parameter back.
>
> 'chip->duration' is retained for TPM 1.x.
>
> As the first entry for this new behavior address TCG spec update mentioned
> in this issue:
>
> https://github.com/raspberrypi/linux/issues/7054
>
> Therefore, for TPM_SelfTest the duration is set to 3000 ms.
>
> This does not categorize a as bug, given that this is introduced to the
> spec after the feature was originally made.
>
> Cc: Frédéric Jouen <fjouen@sealsq.com>
> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
> ---
> drivers/char/tpm/tpm-interface.c | 2 +-
> drivers/char/tpm/tpm.h | 2 +-
> drivers/char/tpm/tpm2-cmd.c | 115 +++++++++----------------------
> 3 files changed, 34 insertions(+), 85 deletions(-)
>
> diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
> index b71725827743..c9f173001d0e 100644
> --- a/drivers/char/tpm/tpm-interface.c
> +++ b/drivers/char/tpm/tpm-interface.c
> @@ -52,7 +52,7 @@ MODULE_PARM_DESC(suspend_pcr,
> unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
> {
> if (chip->flags & TPM_CHIP_FLAG_TPM2)
> - return tpm2_calc_ordinal_duration(chip, ordinal);
> + return tpm2_calc_ordinal_duration(ordinal);
> else
> return tpm1_calc_ordinal_duration(chip, ordinal);
> }
> diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
> index 7bb87fa5f7a1..2726bd38e5ac 100644
> --- a/drivers/char/tpm/tpm.h
> +++ b/drivers/char/tpm/tpm.h
> @@ -299,7 +299,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,
> ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip);
> int tpm2_auto_startup(struct tpm_chip *chip);
> void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type);
> -unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal);
> +unsigned long tpm2_calc_ordinal_duration(u32 ordinal);
> int tpm2_probe(struct tpm_chip *chip);
> int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip);
> int tpm2_find_cc(struct tpm_chip *chip, u32 cc);
> diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
> index 524d802ede26..29c0d6a8ec20 100644
> --- a/drivers/char/tpm/tpm2-cmd.c
> +++ b/drivers/char/tpm/tpm2-cmd.c
> @@ -48,100 +48,49 @@ int tpm2_get_timeouts(struct tpm_chip *chip)
> return 0;
> }
>
> -/**
> - * tpm2_ordinal_duration_index() - returns an index to the chip duration table
> - * @ordinal: TPM command ordinal.
> - *
> - * The function returns an index to the chip duration table
> - * (enum tpm_duration), that describes the maximum amount of
> - * time the chip could take to return the result for a particular ordinal.
> - *
> - * The values of the MEDIUM, and LONG durations are taken
> - * from the PC Client Profile (PTP) specification (750, 2000 msec)
> - *
> - * LONG_LONG is for commands that generates keys which empirically takes
> - * a longer time on some systems.
> - *
> - * Return:
> - * * TPM_MEDIUM
> - * * TPM_LONG
> - * * TPM_LONG_LONG
> - * * TPM_UNDEFINED
> +/*
> + * Contains the maximum durations in milliseconds for TPM2 commands.
> */
> -static u8 tpm2_ordinal_duration_index(u32 ordinal)
> -{
> - switch (ordinal) {
> - /* Startup */
> - case TPM2_CC_STARTUP: /* 144 */
> - return TPM_MEDIUM;
> -
> - case TPM2_CC_SELF_TEST: /* 143 */
> - return TPM_LONG;
> -
> - case TPM2_CC_GET_RANDOM: /* 17B */
> - return TPM_LONG;
> -
> - case TPM2_CC_SEQUENCE_UPDATE: /* 15C */
> - return TPM_MEDIUM;
> - case TPM2_CC_SEQUENCE_COMPLETE: /* 13E */
> - return TPM_MEDIUM;
> - case TPM2_CC_EVENT_SEQUENCE_COMPLETE: /* 185 */
> - return TPM_MEDIUM;
> - case TPM2_CC_HASH_SEQUENCE_START: /* 186 */
> - return TPM_MEDIUM;
> -
> - case TPM2_CC_VERIFY_SIGNATURE: /* 177 */
> - return TPM_LONG_LONG;
> -
> - case TPM2_CC_PCR_EXTEND: /* 182 */
> - return TPM_MEDIUM;
> -
> - case TPM2_CC_HIERARCHY_CONTROL: /* 121 */
> - return TPM_LONG;
> - case TPM2_CC_HIERARCHY_CHANGE_AUTH: /* 129 */
> - return TPM_LONG;
> -
> - case TPM2_CC_GET_CAPABILITY: /* 17A */
> - return TPM_MEDIUM;
> -
> - case TPM2_CC_NV_READ: /* 14E */
> - return TPM_LONG;
> -
> - case TPM2_CC_CREATE_PRIMARY: /* 131 */
> - return TPM_LONG_LONG;
> - case TPM2_CC_CREATE: /* 153 */
> - return TPM_LONG_LONG;
> - case TPM2_CC_CREATE_LOADED: /* 191 */
> - return TPM_LONG_LONG;
> -
> - default:
> - return TPM_UNDEFINED;
> - }
> -}
> +static const struct {
> + unsigned long ordinal;
> + unsigned long duration;
> +} tpm2_ordinal_duration_map[] = {
> + {TPM2_CC_STARTUP, 750},
> + {TPM2_CC_SELF_TEST, 3000},
> + {TPM2_CC_GET_RANDOM, 2000},
> + {TPM2_CC_SEQUENCE_UPDATE, 750},
> + {TPM2_CC_SEQUENCE_COMPLETE, 750},
> + {TPM2_CC_EVENT_SEQUENCE_COMPLETE, 750},
> + {TPM2_CC_HASH_SEQUENCE_START, 750},
> + {TPM2_CC_VERIFY_SIGNATURE, 30000},
> + {TPM2_CC_PCR_EXTEND, 750},
> + {TPM2_CC_HIERARCHY_CONTROL, 2000},
> + {TPM2_CC_HIERARCHY_CHANGE_AUTH, 2000},
> + {TPM2_CC_GET_CAPABILITY, 750},
> + {TPM2_CC_NV_READ, 2000},
> + {TPM2_CC_CREATE_PRIMARY, 30000},
> + {TPM2_CC_CREATE, 30000},
> + {TPM2_CC_CREATE_LOADED, 30000},
> +};
>
> /**
> - * tpm2_calc_ordinal_duration() - calculate the maximum command duration
> - * @chip: TPM chip to use.
> + * tpm2_calc_ordinal_duration() - Calculate the maximum command duration
> * @ordinal: TPM command ordinal.
> *
> - * The function returns the maximum amount of time the chip could take
> - * to return the result for a particular ordinal in jiffies.
> - *
> - * Return: A maximal duration time for an ordinal in jiffies.
> + * Returns the maximum amount of time the chip is expected by kernel to
> + * take in jiffies.
> */
> -unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
> +unsigned long tpm2_calc_ordinal_duration(u32 ordinal)
> {
> - unsigned int index;
> + int i;
>
> - index = tpm2_ordinal_duration_index(ordinal);
> + for (i = 0; i < ARRAY_SIZE(tpm2_ordinal_duration_map); i++)
> + if (ordinal == tpm2_ordinal_duration_map[i].ordinal)
> + return tpm2_ordinal_duration_map[i].duration;
>
> - if (index != TPM_UNDEFINED)
> - return chip->duration[index];
> - else
> - return msecs_to_jiffies(TPM2_DURATION_DEFAULT);
> + return TPM2_DURATION_DEFAULT;
Ouch, I'm fully acknowledged that msecs_to_jiffies() is missing from the patch.
I'll post +1 so that it is testable.
> }
>
> -
> struct tpm2_pcr_read_out {
> __be32 update_cnt;
> __be32 pcr_selects_cnt;
> --
> 2.39.5
>
>
BR, Jarkko
^ permalink raw reply
* Re: [PATCH] tpm: Disable TPM2_TCG_HMAC by default
From: Jarkko Sakkinen @ 2025-09-18 19:12 UTC (permalink / raw)
To: Jonathan McDowell
Cc: linux-integrity, stable, Chris Fenner, Peter Huewe,
Jason Gunthorpe, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, James Bottomley, open list,
open list:KEYS/KEYRINGS, open list:SECURITY SUBSYSTEM
In-Reply-To: <aMxV9fB0E72QQY2G@earth.li>
On Thu, Sep 18, 2025 at 07:56:53PM +0100, Jonathan McDowell wrote:
> On Mon, Aug 25, 2025 at 11:32:23PM +0300, Jarkko Sakkinen wrote:
> > After reading all the feedback, right now disabling the TPM2_TCG_HMAC
> > is the right call.
> >
> > Other views discussed:
> >
> > A. Having a kernel command-line parameter or refining the feature
> > otherwise. This goes to the area of improvements. E.g., one
> > example is my own idea where the null key specific code would be
> > replaced with a persistent handle parameter (which can be
> > *unambigously* defined as part of attestation process when
> > done correctly).
> >
> > B. Removing the code. I don't buy this because that is same as saying
> > that HMAC encryption cannot work at all (if really nitpicking) in
> > any form. Also I disagree on the view that the feature could not
> > be refined to something more reasoable.
> >
> > Also, both A and B are worst options in terms of backporting.
> >
> > Thus, this is the best possible choice.
>
> I think this is reasonable; it's adding runtime overhead and not adding
> enough benefit to be the default upstream.
Yes, I think this is a balanced change. I agree what you say and at the
same time this gives more space to refine it something usable. Right now
it is much harder to tackle those issue, as it is part of the default
config. By looking at things from this angle, the change is also
benefical for the feature itself (in the long run).
> Reviewed-By: Jonathan McDowell <noodles@earth.li>
Thank you! I appreciate this and will append this to the commit.
BR, Jarkko
^ permalink raw reply
* [PATCH] tpm: use a map for tpm2_calc_ordinal_duration()
From: Jarkko Sakkinen @ 2025-09-18 18:57 UTC (permalink / raw)
To: linux-integrity
Cc: Jarkko Sakkinen, Frédéric Jouen, Peter Huewe,
Jason Gunthorpe, James Bottomley, Mimi Zohar, David Howells,
Paul Moore, James Morris, Serge E. Hallyn, open list,
open list:KEYS-TRUSTED, open list:SECURITY SUBSYSTEM
The current shenanigans for duration calculation introduce too much
complexity for a trivial problem, and further the code is hard to patch and
maintain.
Address these issues with a flat look-up table, which is easy to understand
and patch. If leaf driver specific patching is required in future, it is
easy enough to make a copy of this table during driver initialization and
add the chip parameter back.
'chip->duration' is retained for TPM 1.x.
As the first entry for this new behavior address TCG spec update mentioned
in this issue:
https://github.com/raspberrypi/linux/issues/7054
Therefore, for TPM_SelfTest the duration is set to 3000 ms.
This does not categorize a as bug, given that this is introduced to the
spec after the feature was originally made.
Cc: Frédéric Jouen <fjouen@sealsq.com>
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
drivers/char/tpm/tpm-interface.c | 2 +-
drivers/char/tpm/tpm.h | 2 +-
drivers/char/tpm/tpm2-cmd.c | 115 +++++++++----------------------
3 files changed, 34 insertions(+), 85 deletions(-)
diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c
index b71725827743..c9f173001d0e 100644
--- a/drivers/char/tpm/tpm-interface.c
+++ b/drivers/char/tpm/tpm-interface.c
@@ -52,7 +52,7 @@ MODULE_PARM_DESC(suspend_pcr,
unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
{
if (chip->flags & TPM_CHIP_FLAG_TPM2)
- return tpm2_calc_ordinal_duration(chip, ordinal);
+ return tpm2_calc_ordinal_duration(ordinal);
else
return tpm1_calc_ordinal_duration(chip, ordinal);
}
diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h
index 7bb87fa5f7a1..2726bd38e5ac 100644
--- a/drivers/char/tpm/tpm.h
+++ b/drivers/char/tpm/tpm.h
@@ -299,7 +299,7 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id,
ssize_t tpm2_get_pcr_allocation(struct tpm_chip *chip);
int tpm2_auto_startup(struct tpm_chip *chip);
void tpm2_shutdown(struct tpm_chip *chip, u16 shutdown_type);
-unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal);
+unsigned long tpm2_calc_ordinal_duration(u32 ordinal);
int tpm2_probe(struct tpm_chip *chip);
int tpm2_get_cc_attrs_tbl(struct tpm_chip *chip);
int tpm2_find_cc(struct tpm_chip *chip, u32 cc);
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 524d802ede26..29c0d6a8ec20 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -48,100 +48,49 @@ int tpm2_get_timeouts(struct tpm_chip *chip)
return 0;
}
-/**
- * tpm2_ordinal_duration_index() - returns an index to the chip duration table
- * @ordinal: TPM command ordinal.
- *
- * The function returns an index to the chip duration table
- * (enum tpm_duration), that describes the maximum amount of
- * time the chip could take to return the result for a particular ordinal.
- *
- * The values of the MEDIUM, and LONG durations are taken
- * from the PC Client Profile (PTP) specification (750, 2000 msec)
- *
- * LONG_LONG is for commands that generates keys which empirically takes
- * a longer time on some systems.
- *
- * Return:
- * * TPM_MEDIUM
- * * TPM_LONG
- * * TPM_LONG_LONG
- * * TPM_UNDEFINED
+/*
+ * Contains the maximum durations in milliseconds for TPM2 commands.
*/
-static u8 tpm2_ordinal_duration_index(u32 ordinal)
-{
- switch (ordinal) {
- /* Startup */
- case TPM2_CC_STARTUP: /* 144 */
- return TPM_MEDIUM;
-
- case TPM2_CC_SELF_TEST: /* 143 */
- return TPM_LONG;
-
- case TPM2_CC_GET_RANDOM: /* 17B */
- return TPM_LONG;
-
- case TPM2_CC_SEQUENCE_UPDATE: /* 15C */
- return TPM_MEDIUM;
- case TPM2_CC_SEQUENCE_COMPLETE: /* 13E */
- return TPM_MEDIUM;
- case TPM2_CC_EVENT_SEQUENCE_COMPLETE: /* 185 */
- return TPM_MEDIUM;
- case TPM2_CC_HASH_SEQUENCE_START: /* 186 */
- return TPM_MEDIUM;
-
- case TPM2_CC_VERIFY_SIGNATURE: /* 177 */
- return TPM_LONG_LONG;
-
- case TPM2_CC_PCR_EXTEND: /* 182 */
- return TPM_MEDIUM;
-
- case TPM2_CC_HIERARCHY_CONTROL: /* 121 */
- return TPM_LONG;
- case TPM2_CC_HIERARCHY_CHANGE_AUTH: /* 129 */
- return TPM_LONG;
-
- case TPM2_CC_GET_CAPABILITY: /* 17A */
- return TPM_MEDIUM;
-
- case TPM2_CC_NV_READ: /* 14E */
- return TPM_LONG;
-
- case TPM2_CC_CREATE_PRIMARY: /* 131 */
- return TPM_LONG_LONG;
- case TPM2_CC_CREATE: /* 153 */
- return TPM_LONG_LONG;
- case TPM2_CC_CREATE_LOADED: /* 191 */
- return TPM_LONG_LONG;
-
- default:
- return TPM_UNDEFINED;
- }
-}
+static const struct {
+ unsigned long ordinal;
+ unsigned long duration;
+} tpm2_ordinal_duration_map[] = {
+ {TPM2_CC_STARTUP, 750},
+ {TPM2_CC_SELF_TEST, 3000},
+ {TPM2_CC_GET_RANDOM, 2000},
+ {TPM2_CC_SEQUENCE_UPDATE, 750},
+ {TPM2_CC_SEQUENCE_COMPLETE, 750},
+ {TPM2_CC_EVENT_SEQUENCE_COMPLETE, 750},
+ {TPM2_CC_HASH_SEQUENCE_START, 750},
+ {TPM2_CC_VERIFY_SIGNATURE, 30000},
+ {TPM2_CC_PCR_EXTEND, 750},
+ {TPM2_CC_HIERARCHY_CONTROL, 2000},
+ {TPM2_CC_HIERARCHY_CHANGE_AUTH, 2000},
+ {TPM2_CC_GET_CAPABILITY, 750},
+ {TPM2_CC_NV_READ, 2000},
+ {TPM2_CC_CREATE_PRIMARY, 30000},
+ {TPM2_CC_CREATE, 30000},
+ {TPM2_CC_CREATE_LOADED, 30000},
+};
/**
- * tpm2_calc_ordinal_duration() - calculate the maximum command duration
- * @chip: TPM chip to use.
+ * tpm2_calc_ordinal_duration() - Calculate the maximum command duration
* @ordinal: TPM command ordinal.
*
- * The function returns the maximum amount of time the chip could take
- * to return the result for a particular ordinal in jiffies.
- *
- * Return: A maximal duration time for an ordinal in jiffies.
+ * Returns the maximum amount of time the chip is expected by kernel to
+ * take in jiffies.
*/
-unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
+unsigned long tpm2_calc_ordinal_duration(u32 ordinal)
{
- unsigned int index;
+ int i;
- index = tpm2_ordinal_duration_index(ordinal);
+ for (i = 0; i < ARRAY_SIZE(tpm2_ordinal_duration_map); i++)
+ if (ordinal == tpm2_ordinal_duration_map[i].ordinal)
+ return tpm2_ordinal_duration_map[i].duration;
- if (index != TPM_UNDEFINED)
- return chip->duration[index];
- else
- return msecs_to_jiffies(TPM2_DURATION_DEFAULT);
+ return TPM2_DURATION_DEFAULT;
}
-
struct tpm2_pcr_read_out {
__be32 update_cnt;
__be32 pcr_selects_cnt;
--
2.39.5
^ permalink raw reply related
* Re: [PATCH] tpm: Disable TPM2_TCG_HMAC by default
From: Jonathan McDowell @ 2025-09-18 18:56 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: linux-integrity, stable, Chris Fenner, Peter Huewe,
Jason Gunthorpe, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, James Bottomley, open list,
open list:KEYS/KEYRINGS, open list:SECURITY SUBSYSTEM
In-Reply-To: <20250825203223.629515-1-jarkko@kernel.org>
On Mon, Aug 25, 2025 at 11:32:23PM +0300, Jarkko Sakkinen wrote:
>After reading all the feedback, right now disabling the TPM2_TCG_HMAC
>is the right call.
>
>Other views discussed:
>
>A. Having a kernel command-line parameter or refining the feature
> otherwise. This goes to the area of improvements. E.g., one
> example is my own idea where the null key specific code would be
> replaced with a persistent handle parameter (which can be
> *unambigously* defined as part of attestation process when
> done correctly).
>
>B. Removing the code. I don't buy this because that is same as saying
> that HMAC encryption cannot work at all (if really nitpicking) in
> any form. Also I disagree on the view that the feature could not
> be refined to something more reasoable.
>
>Also, both A and B are worst options in terms of backporting.
>
>Thus, this is the best possible choice.
I think this is reasonable; it's adding runtime overhead and not adding
enough benefit to be the default upstream.
Reviewed-By: Jonathan McDowell <noodles@earth.li>
>Cc: stable@vger.kernel.or # v6.10+
>Fixes: d2add27cf2b8 ("tpm: Add NULL primary creation")
>Suggested-by: Chris Fenner <cfenn@google.com>
>Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
>---
>PS. I did not post this last week because that would have been most
>likely the most counter-productive action to taken. It's better
>sometimes to take a bit of time to think (which can be seen that
>I've given also more reasonable weight to my own eaerlier
>proposals).
>
>I also accept further changes, if there is e.g., inconsistency
>with TCG_TPM_HMAC setting or similar (obviously).
>---
> drivers/char/tpm/Kconfig | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
>diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig
>index dddd702b2454..3e4684f6b4af 100644
>--- a/drivers/char/tpm/Kconfig
>+++ b/drivers/char/tpm/Kconfig
>@@ -29,7 +29,7 @@ if TCG_TPM
>
> config TCG_TPM2_HMAC
> bool "Use HMAC and encrypted transactions on the TPM bus"
>- default X86_64
>+ default n
> select CRYPTO_ECDH
> select CRYPTO_LIB_AESCFB
> select CRYPTO_LIB_SHA256
>--
>2.39.5
J.
--
] https://www.earth.li/~noodles/ [] Is this real - that's the first [
] PGP/GPG Key @ the.earth.li [] thing I think every morning. [
] via keyserver, web or email. [] [
] RSA: 4096/0x94FA372B2DA8B985 [] [
^ permalink raw reply
* あたま専門のもみほぐし店 収益性等/概要資料
From: ヘッドミント @ 2025-09-18 11:41 UTC (permalink / raw)
To: linux-security-module
お世話になります。
コンパクトにスタートできて手堅く収益をあげることのできる
フランチャイズビジネスの事業概要資料をご案内申し上げます。
小資本/小スペース/少人数の
コンパクト・フランチャイズ
ドライヘッドスパ専門店
“ヘッドミント”
・収益モデル
・開業に必要な資金
・ロイヤリティ
・スケジュール etc
↓ FC事業概要資料 ↓
https://dryheadspa-hm.biz/fc/
ドライヘッドスパとは ――― 水を使わないヘッドスパです。
足つぼや耳つぼなど、専門のもみほぐし店がありますが
ご紹介するサロンは「 頭に特化したもみほぐし店 」です。
ドライヘッドスパというジャンルの認知度は、
現時点ではそれほど高くありません。
にも関わらず、私どもがフランチャイズ展開する“ヘッドミント”の
店舗には月間450人以上の新規客が来店し、満席が続いています。
これから先、認知度が高まることで
爆発的に伸びるポテンシャルを秘めています。
フランチャイズによる事業を展開していますので、
新たな収益づくりをお考えの方は、まずは概要資料をご覧ください。
ドライヘッドスパ専門店
ヘッドミント
↓ FC事業概要資料 ↓
https://dryheadspa-hm.biz/fc/
よろしくお願いします。
------------------------------------------
株式会社じむや
愛知県名古屋市中区大須3-26-41堀田ビル
TEL:052-263-4688
------------------------------------------
本情報がご不要な方にはご迷惑をおかけし申し訳ございません。
メールマガジンの解除は、下記URLにて承っております。
https://dryheadspa-hm.biz/mail/
お手数お掛けしますがよろしくお願いします。
^ permalink raw reply
* Re: [PATCH v3 00/35] Compiler-Based Capability- and Locking-Analysis
From: Nathan Chancellor @ 2025-09-18 17:45 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Marco Elver, Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon,
David S. Miller, Luc Van Oostenryck, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Bill Wendling, Dmitry Vyukov, Eric Dumazet, Frederic Weisbecker,
Greg Kroah-Hartman, Herbert Xu, Ian Rogers, Jann Horn,
Joel Fernandes, Jonathan Corbet, Josh Triplett, Justin Stitt,
Kees Cook, Kentaro Takeda, Lukas Bulwahn, Mark Rutland,
Mathieu Desnoyers, Miguel Ojeda, Neeraj Upadhyay,
Nick Desaulniers, Steven Rostedt, Tetsuo Handa, Thomas Gleixner,
Thomas Graf, Uladzislau Rezki, Waiman Long, kasan-dev,
linux-crypto, linux-doc, linux-kbuild, linux-kernel, linux-mm,
linux-security-module, linux-sparse, llvm, rcu
In-Reply-To: <20250918141511.GA30263@lst.de>
On Thu, Sep 18, 2025 at 04:15:11PM +0200, Christoph Hellwig wrote:
> On Thu, Sep 18, 2025 at 03:59:11PM +0200, Marco Elver wrote:
> > A Clang version that supports `-Wthread-safety-pointer` and the new
> > alias-analysis of capability pointers is required (from this version
> > onwards):
> >
> > https://github.com/llvm/llvm-project/commit/b4c98fcbe1504841203e610c351a3227f36c92a4 [3]
>
> There's no chance to make say x86 pre-built binaries for that available?
I can use my existing kernel.org LLVM [1] build infrastructure to
generate prebuilt x86 binaries. Just give me a bit to build and upload
them. You may not be the only developer or maintainer who may want to
play with this.
[1]: https://kernel.org/pub/tools/llvm/
Cheers,
Nathan
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox