* Re: [PATCH v5] ima_fs: Avoid creating measurement lists for unsupported hash algos
From: Mimi Zohar @ 2026-02-25 20:07 UTC (permalink / raw)
To: dima, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Paul Moore,
James Morris, Serge E. Hallyn, Silvia Sisinni, Enrico Bravi
Cc: Jonathan McDowell, linux-integrity, linux-security-module,
linux-kernel, stable, Dmitry Safonov
In-Reply-To: <20260223-ima-oob-v5-1-91cc1064e767@arista.com>
On Mon, 2026-02-23 at 14:56 +0000, Dmitry Safonov via B4 Relay wrote:
> From: Dmitry Safonov <dima@arista.com>
>
> ima_tpm_chip->allocated_banks[i].crypto_id is initialized to
> HASH_ALGO__LAST if the TPM algorithm is not supported. However there
> are places relying on the algorithm to be valid because it is accessed
> by hash_algo_name[].
>
> On 6.12.40 I observe the following read out-of-bounds in hash_algo_name:
> ==================================================================
> BUG: KASAN: global-out-of-bounds in create_securityfs_measurement_lists+0x396/0x440
> Read of size 8 at addr ffffffff83e18138 by task swapper/0/1
>
> CPU: 4 UID: 0 PID: 1 Comm: swapper/0 Not tainted 6.12.40 #3
> Call Trace:
> <TASK>
> dump_stack_lvl+0x61/0x90
> print_report+0xc4/0x580
> ? kasan_addr_to_slab+0x26/0x80
> ? create_securityfs_measurement_lists+0x396/0x440
> kasan_report+0xc2/0x100
> ? create_securityfs_measurement_lists+0x396/0x440
> create_securityfs_measurement_lists+0x396/0x440
> ima_fs_init+0xa3/0x300
> ima_init+0x7d/0xd0
> init_ima+0x28/0x100
> do_one_initcall+0xa6/0x3e0
> kernel_init_freeable+0x455/0x740
> kernel_init+0x24/0x1d0
> ret_from_fork+0x38/0x80
> ret_from_fork_asm+0x11/0x20
> </TASK>
>
> The buggy address belongs to the variable:
> hash_algo_name+0xb8/0x420
>
> Memory state around the buggy address:
> ffffffff83e18000: 00 01 f9 f9 f9 f9 f9 f9 00 01 f9 f9 f9 f9 f9 f9
> ffffffff83e18080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> >ffffffff83e18100: 00 00 00 00 00 00 00 f9 f9 f9 f9 f9 00 05 f9 f9
> ^
> ffffffff83e18180: f9 f9 f9 f9 00 00 00 00 00 00 00 04 f9 f9 f9 f9
> ffffffff83e18200: 00 00 00 00 00 00 00 00 04 f9 f9 f9 f9 f9 f9 f9
> ==================================================================
>
> Seems like the TPM chip supports sha3_256, which isn't yet in
> tpm_algorithms:
> tpm tpm0: TPM with unsupported bank algorithm 0x0027
>
> Thus solve the problem by creating a file name with "_tpm_alg_<ID>"
> postfix if the crypto algorithm isn't initialized.
>
> This is how it looks on the test machine (patch ported to v6.12 release):
> # ls -1 /sys/kernel/security/ima/
> ascii_runtime_measurements
> ascii_runtime_measurements_tpm_alg_27
> ascii_runtime_measurements_sha1
> ascii_runtime_measurements_sha256
> binary_runtime_measurements
> binary_runtime_measurements_tpm_alg_27
> binary_runtime_measurements_sha1
> binary_runtime_measurements_sha256
> policy
> runtime_measurements_count
> violations
When reposting this patch, on top of Roberto's patch, please include the name of
the TCG document, or a link to it, defining the algorithm id number here in the
patch description.
thanks,
Mimi
^ permalink raw reply
* Re: [PATCH] ima: Add digest_size field to ima_algo_desc structure and use to show meas.
From: Mimi Zohar @ 2026-02-25 19:47 UTC (permalink / raw)
To: Roberto Sassu, dmitry.kasatkin, eric.snowberg, paul, jmorris,
serge
Cc: linux-integrity, linux-security-module, linux-kernel,
devnull+dima.arista.com, Roberto Sassu
In-Reply-To: <55905382b88f00de0d7a82aff0605b6d0d9a1e8c.camel@linux.ibm.com>
On Wed, 2026-02-25 at 13:37 -0500, Mimi Zohar wrote:
> On Wed, 2026-02-25 at 13:53 +0100, Roberto Sassu wrote:
> > From: Roberto Sassu <roberto.sassu@huawei.com>
> >
> > Add the digest_size field to the ima_algo_desc structure to determine the
> > digest size from the correct source.
> >
> > If the hash algorithm is among allocated PCR banks, take the value from the
> > TPM bank info; if the hash algorithm is SHA1, use the predefined value; if
> > the hash algorithm is the default one but not among the PCR banks, take the
> > digest size from the crypto subsystem (the default hash algorithm is
> > checked when parsing the ima_hash= command line option).
> >
> > Finally, use the new information to correctly show the template digest in
> > ima_measurements_show() and ima_ascii_measurements_show().
> >
> > Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
>
> Thanks, Roberto. The patch looks fine, other than the patch title. Could it be
> renamed as "ima: define and use a digest_size field in the ima_algo_desc
> structure"?
The original design relied on the kernel's hash algorithm size. This patch
changes it to use the TPM's hash algorithm size, without comparing it to the
kernel's definition.
At minimum, this change in design should be noted above in the patch
description.
Mimi
^ permalink raw reply
* [RFC PATCH 3/3] LSM: Reserve use of secmarks
From: Casey Schaufler @ 2026-02-25 19:21 UTC (permalink / raw)
To: casey, paul, linux-security-module
Cc: jmorris, serge, keescook, john.johansen, penguin-kernel,
stephen.smalley.work, selinux
In-Reply-To: <20260225192143.14448-1-casey@schaufler-ca.com>
Use of "exclusive" LSM hooks are limited to the first LSM registering
them. These hooks include those use to process network secmarks.
The hooks required to process secmarks are flagged with LSM_FLAG_EXCLUSIVE
in their definitions. This includes secid_to_secctx and secctx_to_secid,
which are used by netfilter.
The various LSMs that use secmarks are updated to detect whether
they are providing exclusive hooks, and to eschew the use of secmarks
if they are not.
SELinux has a peculiar behavior in that it may decide that it
must have network controls, but only after policy is loaded.
This patch includes a warning should policy capability alwaysnetwork
be set when another LSM holds the exclusive hooks. It has been
suggested that SELinux would consider this a fatal condition, in
which case a panic might be a favored, if draconian, option.
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
include/linux/lsm_hook_defs.h | 12 +++++------
include/linux/security.h | 1 +
security/apparmor/lsm.c | 24 ++++++++++++++++------
security/security.c | 15 ++++++++++++++
security/selinux/hooks.c | 35 ++++++++++++++++++++++++--------
security/selinux/ss/services.c | 3 +++
security/smack/smack_lsm.c | 6 ++++--
security/smack/smack_netfilter.c | 6 ++++++
8 files changed, 80 insertions(+), 22 deletions(-)
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index acda3a02da97..e6d4d9c80ac6 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -309,12 +309,12 @@ LSM_HOOK(int, -EINVAL, 0, getprocattr, struct task_struct *p, const char *name,
LSM_HOOK(int, -EINVAL, 0, setprocattr, const char *name, void *value,
size_t size)
LSM_HOOK(int, 0, 0, ismaclabel, const char *name)
-LSM_HOOK(int, -EOPNOTSUPP, 0, secid_to_secctx, u32 secid,
+LSM_HOOK(int, -EOPNOTSUPP, LSM_FLAG_EXCLUSIVE, secid_to_secctx, u32 secid,
struct lsm_context *cp)
LSM_HOOK(int, -EOPNOTSUPP, 0, lsmprop_to_secctx, struct lsm_prop *prop,
struct lsm_context *cp)
-LSM_HOOK(int, 0, 0, secctx_to_secid, const char *secdata, u32 seclen,
- u32 *secid)
+LSM_HOOK(int, 0, LSM_FLAG_EXCLUSIVE, secctx_to_secid, const char *secdata,
+ u32 seclen, u32 *secid)
LSM_HOOK(void, LSM_RET_VOID, 0, release_secctx, struct lsm_context *cp)
LSM_HOOK(void, LSM_RET_VOID, 0, inode_invalidate_secctx, struct inode *inode)
LSM_HOOK(int, 0, 0, inode_notifysecctx, struct inode *inode, void *ctx,
@@ -379,9 +379,9 @@ LSM_HOOK(void, LSM_RET_VOID, 0, inet_csk_clone, struct sock *newsk,
const struct request_sock *req)
LSM_HOOK(void, LSM_RET_VOID, 0, inet_conn_established, struct sock *sk,
struct sk_buff *skb)
-LSM_HOOK(int, 0, 0, secmark_relabel_packet, u32 secid)
-LSM_HOOK(void, LSM_RET_VOID, 0, secmark_refcount_inc, void)
-LSM_HOOK(void, LSM_RET_VOID, 0, secmark_refcount_dec, void)
+LSM_HOOK(int, 0, LSM_FLAG_EXCLUSIVE, secmark_relabel_packet, u32 secid)
+LSM_HOOK(void, LSM_RET_VOID, LSM_FLAG_EXCLUSIVE, secmark_refcount_inc, void)
+LSM_HOOK(void, LSM_RET_VOID, LSM_FLAG_EXCLUSIVE, secmark_refcount_dec, void)
LSM_HOOK(void, LSM_RET_VOID, 0, req_classify_flow,
const struct request_sock *req, struct flowi_common *flic)
LSM_HOOK(int, 0, 0, tun_dev_alloc_security, void *security)
diff --git a/include/linux/security.h b/include/linux/security.h
index e3c137a1b30a..638436b9b748 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -326,6 +326,7 @@ int unregister_blocking_lsm_notifier(struct notifier_block *nb);
extern int security_init(void);
extern int early_security_init(void);
extern u64 lsm_name_to_attr(const char *name);
+extern u32 lsm_secmark_from_skb(struct sk_buff *skb, const u64 lsmid);
/* Security operations */
int security_binder_set_context_mgr(const struct cred *mgr);
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index a87cd60ed206..2ce0d9a73973 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -1511,9 +1511,11 @@ static int apparmor_socket_shutdown(struct socket *sock, int how)
static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
struct aa_sk_ctx *ctx = aa_sock(sk);
+ u32 secmark;
int error;
- if (!skb->secmark)
+ secmark = lsm_secmark_from_skb(skb, LSM_ID_APPARMOR);
+ if (!secmark)
return 0;
/*
@@ -1525,7 +1527,7 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
rcu_read_lock();
error = apparmor_secmark_check(rcu_dereference(ctx->label), OP_RECVMSG,
- AA_MAY_RECEIVE, skb->secmark, sk);
+ AA_MAY_RECEIVE, secmark, sk);
rcu_read_unlock();
return error;
@@ -1640,14 +1642,16 @@ static int apparmor_inet_conn_request(const struct sock *sk, struct sk_buff *skb
struct request_sock *req)
{
struct aa_sk_ctx *ctx = aa_sock(sk);
+ u32 secmark;
int error;
- if (!skb->secmark)
+ secmark = lsm_secmark_from_skb(skb, LSM_ID_APPARMOR);
+ if (!secmark)
return 0;
rcu_read_lock();
error = apparmor_secmark_check(rcu_dereference(ctx->label), OP_CONNECT,
- AA_MAY_CONNECT, skb->secmark, sk);
+ AA_MAY_CONNECT, secmark, sk);
rcu_read_unlock();
return error;
@@ -2359,9 +2363,11 @@ static unsigned int apparmor_ip_postroute(void *priv,
{
struct aa_sk_ctx *ctx;
struct sock *sk;
+ u32 secmark;
int error;
- if (!skb->secmark)
+ secmark = lsm_secmark_from_skb(skb, LSM_ID_APPARMOR);
+ if (!secmark)
return NF_ACCEPT;
sk = skb_to_full_sk(skb);
@@ -2371,7 +2377,7 @@ static unsigned int apparmor_ip_postroute(void *priv,
ctx = aa_sock(sk);
rcu_read_lock();
error = apparmor_secmark_check(rcu_dereference(ctx->label), OP_SENDMSG,
- AA_MAY_SEND, skb->secmark, sk);
+ AA_MAY_SEND, secmark, sk);
rcu_read_unlock();
if (!error)
return NF_ACCEPT;
@@ -2399,12 +2405,18 @@ static const struct nf_hook_ops apparmor_nf_ops[] = {
static int __net_init apparmor_nf_register(struct net *net)
{
+ if (lsm_exclusive_hooks != LSM_ID_APPARMOR)
+ return 0;
+
return nf_register_net_hooks(net, apparmor_nf_ops,
ARRAY_SIZE(apparmor_nf_ops));
}
static void __net_exit apparmor_nf_unregister(struct net *net)
{
+ if (lsm_exclusive_hooks != LSM_ID_APPARMOR)
+ return;
+
nf_unregister_net_hooks(net, apparmor_nf_ops,
ARRAY_SIZE(apparmor_nf_ops));
}
diff --git a/security/security.c b/security/security.c
index 25e7cfc96f20..754b16004677 100644
--- a/security/security.c
+++ b/security/security.c
@@ -4509,6 +4509,21 @@ void security_inet_conn_established(struct sock *sk,
}
EXPORT_SYMBOL(security_inet_conn_established);
+/**
+ * lsm_secmark_from_skb - secid for the specified LSM from the packet
+ * @skb: the packet
+ * @lsm: which LSM is asking
+ *
+ * If the specified LSM has use of the secmark, return its value.
+ * Otherwise, return the invalid secid 0.
+ */
+u32 lsm_secmark_from_skb(struct sk_buff *skb, const u64 lsmid)
+{
+ if (lsmid == lsm_exclusive_hooks)
+ return skb->secmark;
+ return 0;
+}
+
/**
* security_secmark_relabel_packet() - Check if setting a secmark is allowed
* @secid: new secmark value
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index d053ce562370..1dff2121a834 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -5269,6 +5269,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
int err = 0;
struct sk_security_struct *sksec = selinux_sock(sk);
u32 sk_sid = sksec->sid;
+ u32 secmark;
struct common_audit_data ad;
struct lsm_network_audit net;
char *addrp;
@@ -5279,7 +5280,8 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
return err;
if (selinux_secmark_enabled()) {
- err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
+ secmark = lsm_secmark_from_skb(skb, LSM_ID_SELINUX);
+ err = avc_has_perm(sk_sid, secmark, SECCLASS_PACKET,
PACKET__RECV, &ad);
if (err)
return err;
@@ -5299,6 +5301,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
struct sk_security_struct *sksec = selinux_sock(sk);
u16 family = sk->sk_family;
u32 sk_sid = sksec->sid;
+ u32 secmark;
struct common_audit_data ad;
struct lsm_network_audit net;
char *addrp;
@@ -5348,7 +5351,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
}
if (secmark_active) {
- err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET,
+ secmark = lsm_secmark_from_skb(skb, LSM_ID_SELINUX);
+ err = avc_has_perm(sk_sid, secmark, SECCLASS_PACKET,
PACKET__RECV, &ad);
if (err)
return err;
@@ -5850,6 +5854,7 @@ static unsigned int selinux_ip_forward(void *priv, struct sk_buff *skb,
int ifindex;
u16 family;
char *addrp;
+ u32 secmark;
u32 peer_sid;
struct common_audit_data ad;
struct lsm_network_audit net;
@@ -5883,10 +5888,12 @@ static unsigned int selinux_ip_forward(void *priv, struct sk_buff *skb,
}
}
- if (secmark_active)
- if (avc_has_perm(peer_sid, skb->secmark,
+ if (secmark_active) {
+ secmark = lsm_secmark_from_skb(skb, LSM_ID_SELINUX);
+ if (avc_has_perm(peer_sid, secmark,
SECCLASS_PACKET, PACKET__FORWARD_IN, &ad))
return NF_DROP;
+ }
if (netlbl_enabled())
/* we do this in the FORWARD path and not the POST_ROUTING
@@ -5950,6 +5957,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
struct common_audit_data ad;
struct lsm_network_audit net;
u8 proto = 0;
+ u32 secmark;
sk = skb_to_full_sk(skb);
if (sk == NULL)
@@ -5960,10 +5968,12 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
if (selinux_parse_skb(skb, &ad, NULL, 0, &proto))
return NF_DROP;
- if (selinux_secmark_enabled())
- if (avc_has_perm(sksec->sid, skb->secmark,
+ if (selinux_secmark_enabled()) {
+ secmark = lsm_secmark_from_skb(skb, LSM_ID_SELINUX);
+ if (avc_has_perm(sksec->sid, secmark,
SECCLASS_PACKET, PACKET__SEND, &ad))
return NF_DROP_ERR(-ECONNREFUSED);
+ }
if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto))
return NF_DROP_ERR(-ECONNREFUSED);
@@ -5978,6 +5988,7 @@ static unsigned int selinux_ip_postroute(void *priv,
u16 family;
u32 secmark_perm;
u32 peer_sid;
+ u32 secmark;
int ifindex;
struct sock *sk;
struct common_audit_data ad;
@@ -6082,10 +6093,12 @@ static unsigned int selinux_ip_postroute(void *priv,
if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL))
return NF_DROP;
- if (secmark_active)
- if (avc_has_perm(peer_sid, skb->secmark,
+ if (secmark_active) {
+ secmark = lsm_secmark_from_skb(skb, LSM_ID_SELINUX);
+ if (avc_has_perm(peer_sid, secmark,
SECCLASS_PACKET, secmark_perm, &ad))
return NF_DROP_ERR(-ECONNREFUSED);
+ }
if (peerlbl_active) {
u32 if_sid;
@@ -7718,12 +7731,18 @@ static const struct nf_hook_ops selinux_nf_ops[] = {
static int __net_init selinux_nf_register(struct net *net)
{
+ if (lsm_exclusive_hooks != LSM_ID_SELINUX)
+ return 0;
+
return nf_register_net_hooks(net, selinux_nf_ops,
ARRAY_SIZE(selinux_nf_ops));
}
static void __net_exit selinux_nf_unregister(struct net *net)
{
+ if (lsm_exclusive_hooks != LSM_ID_SELINUX)
+ return;
+
nf_unregister_net_hooks(net, selinux_nf_ops,
ARRAY_SIZE(selinux_nf_ops));
}
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 13fc712d5923..269ad09f8dca 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -2189,6 +2189,9 @@ static void security_load_policycaps(struct selinux_policy *policy)
pr_info("SELinux: unknown policy capability %u\n",
i);
}
+ if (selinux_state.policycap[POLICYDB_CAP_ALWAYSNETWORK] &&
+ lsm_exclusive_hooks != LSM_ID_SELINUX)
+ pr_warn("SELinux: policy capability alwaysnetwork is set, but secmark is used by another LSM.\n");
}
static int security_preserve_bools(struct selinux_policy *oldpolicy,
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index a0bd4919a9d9..7a98dcc4c67d 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -4195,10 +4195,12 @@ static int smk_skb_to_addr_ipv6(struct sk_buff *skb, struct sockaddr_in6 *sip)
#ifdef CONFIG_NETWORK_SECMARK
static struct smack_known *smack_from_skb(struct sk_buff *skb)
{
- if (skb == NULL || skb->secmark == 0)
+ u32 secmark = lsm_secmark_from_skb(skb, LSM_ID_SMACK);
+
+ if (skb == NULL || secmark == 0)
return NULL;
- return smack_from_secid(skb->secmark);
+ return smack_from_secid(secmark);
}
#else
static inline struct smack_known *smack_from_skb(struct sk_buff *skb)
diff --git a/security/smack/smack_netfilter.c b/security/smack/smack_netfilter.c
index 17ba578b1308..47426973843b 100644
--- a/security/smack/smack_netfilter.c
+++ b/security/smack/smack_netfilter.c
@@ -54,12 +54,18 @@ static const struct nf_hook_ops smack_nf_ops[] = {
static int __net_init smack_nf_register(struct net *net)
{
+ if (lsm_exclusive_hooks != LSM_ID_SMACK)
+ return 0;
+
return nf_register_net_hooks(net, smack_nf_ops,
ARRAY_SIZE(smack_nf_ops));
}
static void __net_exit smack_nf_unregister(struct net *net)
{
+ if (lsm_exclusive_hooks != LSM_ID_SMACK)
+ return;
+
nf_unregister_net_hooks(net, smack_nf_ops, ARRAY_SIZE(smack_nf_ops));
}
--
2.52.0
^ permalink raw reply related
* [RFC PATCH 2/3] LSM: Enforce exclusive hooks
From: Casey Schaufler @ 2026-02-25 19:21 UTC (permalink / raw)
To: casey, paul, linux-security-module
Cc: jmorris, serge, keescook, john.johansen, penguin-kernel,
stephen.smalley.work, selinux
In-Reply-To: <20260225192143.14448-1-casey@schaufler-ca.com>
If an LSM hook is marked as exclusive via LSM_FLAG_EXCLUSIVE
in lsm_hook_defs.h it will not be added to the set of hooks to
be executed if an different LSM has already registered an
exclusive hook.
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
include/linux/security.h | 2 ++
security/lsm_init.c | 66 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 68 insertions(+)
diff --git a/include/linux/security.h b/include/linux/security.h
index 83a646d72f6f..e3c137a1b30a 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -2404,4 +2404,6 @@ static inline void security_initramfs_populated(void)
}
#endif /* CONFIG_SECURITY */
+extern u64 lsm_exclusive_hooks;
+
#endif /* ! __LINUX_SECURITY_H */
diff --git a/security/lsm_init.c b/security/lsm_init.c
index 05bd52e6b1f2..dc3c84387a7e 100644
--- a/security/lsm_init.c
+++ b/security/lsm_init.c
@@ -356,6 +356,70 @@ static int __init lsm_static_call_init(struct security_hook_list *hl)
return -ENOSPC;
}
+/*
+ * Hooks that are restricted to use by a single security module.
+ *
+ * Secmark hooks have not been converted from secids to lsm_props
+ * due to space limitations in packet headers.
+ *
+ * Conversions from a secid to a secctx are restricted to the
+ * single security module. All cases where there may be multiple
+ * modules providing the input data have been converted to use
+ * a lsm_prop instead of a secid.
+ */
+struct lsm_exclusive {
+ struct lsm_static_call *name;
+ char *namestr;
+ u32 flags;
+};
+
+static __initdata struct lsm_exclusive lsm_exclusive_set[] = {
+#define LSM_HOOK(RET, DEFAULT, FLAGS, NAME, ...) \
+ { .name = static_calls_table.NAME, .flags = FLAGS, .namestr = "" #NAME "" , },
+#include <linux/lsm_hook_defs.h>
+#undef LSM_HOOK
+};
+u64 lsm_exclusive_hooks;
+EXPORT_SYMBOL(lsm_exclusive_hooks);
+
+/**
+ * lsm_exclusive_hook_denial - Check if exclusive hook is in use
+ * @hook: the hook to check
+ *
+ * Check if the hook in question is restricted to a single using LSM,
+ * and if the LSM providing single LSM hooks is defined.
+ *
+ * Returns true if the hook is exclusive and they are already provided,
+ * false otherwise.
+ */
+static bool __init lsm_exclusive_hook_denial(struct security_hook_list *hook)
+{
+ int i;
+
+ if (lsm_exclusive_hooks == hook->lsmid->id)
+ return false;
+
+ for (i = 0; i < ARRAY_SIZE(lsm_exclusive_set); i++) {
+ if (!(lsm_exclusive_set[i].flags & LSM_FLAG_EXCLUSIVE))
+ continue;
+ if (hook->scalls == lsm_exclusive_set[i].name) {
+ if (lsm_exclusive_hooks) {
+ if (lsm_debug)
+ lsm_pr("%s denied for %s.\n",
+ lsm_exclusive_set[i].namestr,
+ hook->lsmid->name);
+ return true;
+ }
+ if (lsm_debug)
+ lsm_pr("Exclusive hooks limited to %s.\n",
+ hook->lsmid->name);
+ lsm_exclusive_hooks = hook->lsmid->id;
+ break;
+ }
+ }
+ return false;
+}
+
/**
* security_add_hooks - Add a LSM's hooks to the LSM framework's hook lists
* @hooks: LSM hooks to add
@@ -371,6 +435,8 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
for (i = 0; i < count; i++) {
hooks[i].lsmid = lsmid;
+ if (lsm_exclusive_hook_denial(&hooks[i]))
+ continue;
if (lsm_static_call_init(&hooks[i]))
panic("exhausted LSM callback slots with LSM %s\n",
lsmid->name);
--
2.52.0
^ permalink raw reply related
* [RFC PATCH 1/3] LSM: add a flags field to the LSM hook definitions
From: Casey Schaufler @ 2026-02-25 19:21 UTC (permalink / raw)
To: casey, paul, linux-security-module
Cc: jmorris, serge, keescook, john.johansen, penguin-kernel,
stephen.smalley.work, selinux
In-Reply-To: <20260225192143.14448-1-casey@schaufler-ca.com>
Add a field for flags to the definition of LSM hooks. This allows
for hooks to be identified at system initialization for special
processing.
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
include/linux/bpf_lsm.h | 2 +-
include/linux/lsm_hook_defs.h | 614 ++++++++++++++++++----------------
include/linux/lsm_hooks.h | 4 +-
kernel/bpf/bpf_lsm.c | 10 +-
security/bpf/hooks.c | 2 +-
security/security.c | 6 +-
6 files changed, 331 insertions(+), 307 deletions(-)
diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h
index 643809cc78c3..d71ba8c87e79 100644
--- a/include/linux/bpf_lsm.h
+++ b/include/linux/bpf_lsm.h
@@ -14,7 +14,7 @@
#ifdef CONFIG_BPF_LSM
-#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
+#define LSM_HOOK(RET, DEFAULT, FLAGS, NAME, ...) \
RET bpf_lsm_##NAME(__VA_ARGS__);
#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 8c42b4bde09c..acda3a02da97 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -18,451 +18,475 @@
* The macro LSM_HOOK is used to define the data structures required by
* the LSM framework using the pattern:
*
- * LSM_HOOK(<return_type>, <default_value>, <hook_name>, args...)
+ * LSM_HOOK(<return_type>, <default_value>, <flags>, <single>,
+ * <hook_name>, args...)
*
* struct security_hook_heads {
- * #define LSM_HOOK(RET, DEFAULT, NAME, ...) struct hlist_head NAME;
+ * #define LSM_HOOK(RET, DEFAULT, FLAGS, NAME, ...) struct hlist_head NAME;
* #include <linux/lsm_hook_defs.h>
* #undef LSM_HOOK
* };
*/
-LSM_HOOK(int, 0, binder_set_context_mgr, const struct cred *mgr)
-LSM_HOOK(int, 0, binder_transaction, const struct cred *from,
+LSM_HOOK(int, 0, 0, binder_set_context_mgr, const struct cred *mgr)
+LSM_HOOK(int, 0, 0, binder_transaction, const struct cred *from,
const struct cred *to)
-LSM_HOOK(int, 0, binder_transfer_binder, const struct cred *from,
+LSM_HOOK(int, 0, 0, binder_transfer_binder, const struct cred *from,
const struct cred *to)
-LSM_HOOK(int, 0, binder_transfer_file, const struct cred *from,
+LSM_HOOK(int, 0, 0, binder_transfer_file, const struct cred *from,
const struct cred *to, const struct file *file)
-LSM_HOOK(int, 0, ptrace_access_check, struct task_struct *child,
+LSM_HOOK(int, 0, 0, ptrace_access_check, struct task_struct *child,
unsigned int mode)
-LSM_HOOK(int, 0, ptrace_traceme, struct task_struct *parent)
-LSM_HOOK(int, 0, capget, const struct task_struct *target, kernel_cap_t *effective,
- kernel_cap_t *inheritable, kernel_cap_t *permitted)
-LSM_HOOK(int, 0, capset, struct cred *new, const struct cred *old,
+LSM_HOOK(int, 0, 0, ptrace_traceme, struct task_struct *parent)
+LSM_HOOK(int, 0, 0, capget, const struct task_struct *target,
+ kernel_cap_t *effective, kernel_cap_t *inheritable,
+ kernel_cap_t *permitted)
+LSM_HOOK(int, 0, 0, capset, struct cred *new, const struct cred *old,
const kernel_cap_t *effective, const kernel_cap_t *inheritable,
const kernel_cap_t *permitted)
-LSM_HOOK(int, 0, capable, const struct cred *cred, struct user_namespace *ns,
- int cap, unsigned int opts)
-LSM_HOOK(int, 0, quotactl, int cmds, int type, int id, const struct super_block *sb)
-LSM_HOOK(int, 0, quota_on, struct dentry *dentry)
-LSM_HOOK(int, 0, syslog, int type)
-LSM_HOOK(int, 0, settime, const struct timespec64 *ts,
+LSM_HOOK(int, 0, 0, capable, const struct cred *cred,
+ struct user_namespace *ns, int cap, unsigned int opts)
+LSM_HOOK(int, 0, 0, quotactl, int cmds, int type, int id,
+ const struct super_block *sb)
+LSM_HOOK(int, 0, 0, quota_on, struct dentry *dentry)
+LSM_HOOK(int, 0, 0, syslog, int type)
+LSM_HOOK(int, 0, 0, settime, const struct timespec64 *ts,
const struct timezone *tz)
-LSM_HOOK(int, 0, vm_enough_memory, struct mm_struct *mm, long pages)
-LSM_HOOK(int, 0, bprm_creds_for_exec, struct linux_binprm *bprm)
-LSM_HOOK(int, 0, bprm_creds_from_file, struct linux_binprm *bprm, const struct file *file)
-LSM_HOOK(int, 0, bprm_check_security, struct linux_binprm *bprm)
-LSM_HOOK(void, LSM_RET_VOID, bprm_committing_creds, const struct linux_binprm *bprm)
-LSM_HOOK(void, LSM_RET_VOID, bprm_committed_creds, const struct linux_binprm *bprm)
-LSM_HOOK(int, 0, fs_context_submount, struct fs_context *fc, struct super_block *reference)
-LSM_HOOK(int, 0, fs_context_dup, struct fs_context *fc,
+LSM_HOOK(int, 0, 0, vm_enough_memory, struct mm_struct *mm, long pages)
+LSM_HOOK(int, 0, 0, bprm_creds_for_exec, struct linux_binprm *bprm)
+LSM_HOOK(int, 0, 0, bprm_creds_from_file, struct linux_binprm *bprm,
+ const struct file *file)
+LSM_HOOK(int, 0, 0, bprm_check_security, struct linux_binprm *bprm)
+LSM_HOOK(void, LSM_RET_VOID, 0, bprm_committing_creds,
+ const struct linux_binprm *bprm)
+LSM_HOOK(void, LSM_RET_VOID, 0, bprm_committed_creds,
+ const struct linux_binprm *bprm)
+LSM_HOOK(int, 0, 0, fs_context_submount, struct fs_context *fc,
+ struct super_block *reference)
+LSM_HOOK(int, 0, 0, fs_context_dup, struct fs_context *fc,
struct fs_context *src_sc)
-LSM_HOOK(int, -ENOPARAM, fs_context_parse_param, struct fs_context *fc,
+LSM_HOOK(int, -ENOPARAM, 0, fs_context_parse_param, struct fs_context *fc,
struct fs_parameter *param)
-LSM_HOOK(int, 0, sb_alloc_security, struct super_block *sb)
-LSM_HOOK(void, LSM_RET_VOID, sb_delete, struct super_block *sb)
-LSM_HOOK(void, LSM_RET_VOID, sb_free_security, struct super_block *sb)
-LSM_HOOK(void, LSM_RET_VOID, sb_free_mnt_opts, void *mnt_opts)
-LSM_HOOK(int, 0, sb_eat_lsm_opts, char *orig, void **mnt_opts)
-LSM_HOOK(int, 0, sb_mnt_opts_compat, struct super_block *sb, void *mnt_opts)
-LSM_HOOK(int, 0, sb_remount, struct super_block *sb, void *mnt_opts)
-LSM_HOOK(int, 0, sb_kern_mount, const struct super_block *sb)
-LSM_HOOK(int, 0, sb_show_options, struct seq_file *m, struct super_block *sb)
-LSM_HOOK(int, 0, sb_statfs, struct dentry *dentry)
-LSM_HOOK(int, 0, sb_mount, const char *dev_name, const struct path *path,
+LSM_HOOK(int, 0, 0, sb_alloc_security, struct super_block *sb)
+LSM_HOOK(void, LSM_RET_VOID, 0, sb_delete, struct super_block *sb)
+LSM_HOOK(void, LSM_RET_VOID, 0, sb_free_security, struct super_block *sb)
+LSM_HOOK(void, LSM_RET_VOID, 0, sb_free_mnt_opts, void *mnt_opts)
+LSM_HOOK(int, 0, 0, sb_eat_lsm_opts, char *orig, void **mnt_opts)
+LSM_HOOK(int, 0, 0, sb_mnt_opts_compat, struct super_block *sb, void *mnt_opts)
+LSM_HOOK(int, 0, 0, sb_remount, struct super_block *sb, void *mnt_opts)
+LSM_HOOK(int, 0, 0, sb_kern_mount, const struct super_block *sb)
+LSM_HOOK(int, 0, 0, sb_show_options, struct seq_file *m, struct super_block *sb)
+LSM_HOOK(int, 0, 0, sb_statfs, struct dentry *dentry)
+LSM_HOOK(int, 0, 0, sb_mount, const char *dev_name, const struct path *path,
const char *type, unsigned long flags, void *data)
-LSM_HOOK(int, 0, sb_umount, struct vfsmount *mnt, int flags)
-LSM_HOOK(int, 0, sb_pivotroot, const struct path *old_path,
+LSM_HOOK(int, 0, 0, sb_umount, struct vfsmount *mnt, int flags)
+LSM_HOOK(int, 0, 0, sb_pivotroot, const struct path *old_path,
const struct path *new_path)
-LSM_HOOK(int, 0, sb_set_mnt_opts, struct super_block *sb, void *mnt_opts,
+LSM_HOOK(int, 0, 0, sb_set_mnt_opts, struct super_block *sb, void *mnt_opts,
unsigned long kern_flags, unsigned long *set_kern_flags)
-LSM_HOOK(int, 0, sb_clone_mnt_opts, const struct super_block *oldsb,
+LSM_HOOK(int, 0, 0, sb_clone_mnt_opts, const struct super_block *oldsb,
struct super_block *newsb, unsigned long kern_flags,
unsigned long *set_kern_flags)
-LSM_HOOK(int, 0, move_mount, const struct path *from_path,
+LSM_HOOK(int, 0, 0, move_mount, const struct path *from_path,
const struct path *to_path)
-LSM_HOOK(int, -EOPNOTSUPP, dentry_init_security, struct dentry *dentry,
+LSM_HOOK(int, -EOPNOTSUPP, 0, dentry_init_security, struct dentry *dentry,
int mode, const struct qstr *name, const char **xattr_name,
struct lsm_context *cp)
-LSM_HOOK(int, 0, dentry_create_files_as, struct dentry *dentry, int mode,
+LSM_HOOK(int, 0, 0, dentry_create_files_as, struct dentry *dentry, int mode,
const struct qstr *name, const struct cred *old, struct cred *new)
#ifdef CONFIG_SECURITY_PATH
-LSM_HOOK(int, 0, path_unlink, const struct path *dir, struct dentry *dentry)
-LSM_HOOK(int, 0, path_mkdir, const struct path *dir, struct dentry *dentry,
+LSM_HOOK(int, 0, 0, path_unlink, const struct path *dir, struct dentry *dentry)
+LSM_HOOK(int, 0, 0, path_mkdir, const struct path *dir, struct dentry *dentry,
umode_t mode)
-LSM_HOOK(int, 0, path_rmdir, const struct path *dir, struct dentry *dentry)
-LSM_HOOK(int, 0, path_mknod, const struct path *dir, struct dentry *dentry,
+LSM_HOOK(int, 0, 0, path_rmdir, const struct path *dir, struct dentry *dentry)
+LSM_HOOK(int, 0, 0, path_mknod, const struct path *dir, struct dentry *dentry,
umode_t mode, unsigned int dev)
-LSM_HOOK(void, LSM_RET_VOID, path_post_mknod, struct mnt_idmap *idmap,
+LSM_HOOK(void, LSM_RET_VOID, 0, path_post_mknod, struct mnt_idmap *idmap,
struct dentry *dentry)
-LSM_HOOK(int, 0, path_truncate, const struct path *path)
-LSM_HOOK(int, 0, path_symlink, const struct path *dir, struct dentry *dentry,
- const char *old_name)
-LSM_HOOK(int, 0, path_link, struct dentry *old_dentry,
+LSM_HOOK(int, 0, 0, path_truncate, const struct path *path)
+LSM_HOOK(int, 0, 0, path_symlink, const struct path *dir,
+ struct dentry *dentry, const char *old_name)
+LSM_HOOK(int, 0, 0, path_link, struct dentry *old_dentry,
const struct path *new_dir, struct dentry *new_dentry)
-LSM_HOOK(int, 0, path_rename, const struct path *old_dir,
+LSM_HOOK(int, 0, 0, path_rename, const struct path *old_dir,
struct dentry *old_dentry, const struct path *new_dir,
struct dentry *new_dentry, unsigned int flags)
-LSM_HOOK(int, 0, path_chmod, const struct path *path, umode_t mode)
-LSM_HOOK(int, 0, path_chown, const struct path *path, kuid_t uid, kgid_t gid)
-LSM_HOOK(int, 0, path_chroot, const struct path *path)
+LSM_HOOK(int, 0, 0, path_chmod, const struct path *path, umode_t mode)
+LSM_HOOK(int, 0, 0, path_chown, const struct path *path, kuid_t uid, kgid_t gid)
+LSM_HOOK(int, 0, 0, path_chroot, const struct path *path)
#endif /* CONFIG_SECURITY_PATH */
/* Needed for inode based security check */
-LSM_HOOK(int, 0, path_notify, const struct path *path, u64 mask,
+LSM_HOOK(int, 0, 0, path_notify, const struct path *path, u64 mask,
unsigned int obj_type)
-LSM_HOOK(int, 0, inode_alloc_security, struct inode *inode)
-LSM_HOOK(void, LSM_RET_VOID, inode_free_security, struct inode *inode)
-LSM_HOOK(void, LSM_RET_VOID, inode_free_security_rcu, void *inode_security)
-LSM_HOOK(int, -EOPNOTSUPP, inode_init_security, struct inode *inode,
+LSM_HOOK(int, 0, 0, inode_alloc_security, struct inode *inode)
+LSM_HOOK(void, LSM_RET_VOID, 0, inode_free_security, struct inode *inode)
+LSM_HOOK(void, LSM_RET_VOID, 0, inode_free_security_rcu, void *inode_security)
+LSM_HOOK(int, -EOPNOTSUPP, 0, inode_init_security, struct inode *inode,
struct inode *dir, const struct qstr *qstr, struct xattr *xattrs,
int *xattr_count)
-LSM_HOOK(int, 0, inode_init_security_anon, struct inode *inode,
+LSM_HOOK(int, 0, 0, inode_init_security_anon, struct inode *inode,
const struct qstr *name, const struct inode *context_inode)
-LSM_HOOK(int, 0, inode_create, struct inode *dir, struct dentry *dentry,
+LSM_HOOK(int, 0, 0, inode_create, struct inode *dir, struct dentry *dentry,
umode_t mode)
-LSM_HOOK(void, LSM_RET_VOID, inode_post_create_tmpfile, struct mnt_idmap *idmap,
- struct inode *inode)
-LSM_HOOK(int, 0, inode_link, struct dentry *old_dentry, struct inode *dir,
+LSM_HOOK(void, LSM_RET_VOID, 0, inode_post_create_tmpfile,
+ struct mnt_idmap *idmap, struct inode *inode)
+LSM_HOOK(int, 0, 0, inode_link, struct dentry *old_dentry, struct inode *dir,
struct dentry *new_dentry)
-LSM_HOOK(int, 0, inode_unlink, struct inode *dir, struct dentry *dentry)
-LSM_HOOK(int, 0, inode_symlink, struct inode *dir, struct dentry *dentry,
+LSM_HOOK(int, 0, 0, inode_unlink, struct inode *dir, struct dentry *dentry)
+LSM_HOOK(int, 0, 0, inode_symlink, struct inode *dir, struct dentry *dentry,
const char *old_name)
-LSM_HOOK(int, 0, inode_mkdir, struct inode *dir, struct dentry *dentry,
+LSM_HOOK(int, 0, 0, inode_mkdir, struct inode *dir, struct dentry *dentry,
umode_t mode)
-LSM_HOOK(int, 0, inode_rmdir, struct inode *dir, struct dentry *dentry)
-LSM_HOOK(int, 0, inode_mknod, struct inode *dir, struct dentry *dentry,
+LSM_HOOK(int, 0, 0, inode_rmdir, struct inode *dir, struct dentry *dentry)
+LSM_HOOK(int, 0, 0, inode_mknod, struct inode *dir, struct dentry *dentry,
umode_t mode, dev_t dev)
-LSM_HOOK(int, 0, inode_rename, struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry)
-LSM_HOOK(int, 0, inode_readlink, struct dentry *dentry)
-LSM_HOOK(int, 0, inode_follow_link, struct dentry *dentry, struct inode *inode,
- bool rcu)
-LSM_HOOK(int, 0, inode_permission, struct inode *inode, int mask)
-LSM_HOOK(int, 0, inode_setattr, struct mnt_idmap *idmap, struct dentry *dentry,
- struct iattr *attr)
-LSM_HOOK(void, LSM_RET_VOID, inode_post_setattr, struct mnt_idmap *idmap,
+LSM_HOOK(int, 0, 0, inode_rename, struct inode *old_dir,
+ struct dentry *old_dentry, struct inode *new_dir,
+ struct dentry *new_dentry)
+LSM_HOOK(int, 0, 0, inode_readlink, struct dentry *dentry)
+LSM_HOOK(int, 0, 0, inode_follow_link, struct dentry *dentry,
+ struct inode *inode, bool rcu)
+LSM_HOOK(int, 0, 0, inode_permission, struct inode *inode, int mask)
+LSM_HOOK(int, 0, 0, inode_setattr, struct mnt_idmap *idmap, struct dentry *dentry, struct iattr *attr)
+LSM_HOOK(void, LSM_RET_VOID, 0, inode_post_setattr, struct mnt_idmap *idmap,
struct dentry *dentry, int ia_valid)
-LSM_HOOK(int, 0, inode_getattr, const struct path *path)
-LSM_HOOK(int, 0, inode_xattr_skipcap, const char *name)
-LSM_HOOK(int, 0, inode_setxattr, struct mnt_idmap *idmap,
+LSM_HOOK(int, 0, 0, inode_getattr, const struct path *path)
+LSM_HOOK(int, 0, 0, inode_xattr_skipcap, const char *name)
+LSM_HOOK(int, 0, 0, inode_setxattr, struct mnt_idmap *idmap,
struct dentry *dentry, const char *name, const void *value,
size_t size, int flags)
-LSM_HOOK(void, LSM_RET_VOID, inode_post_setxattr, struct dentry *dentry,
+LSM_HOOK(void, LSM_RET_VOID, 0, inode_post_setxattr, struct dentry *dentry,
const char *name, const void *value, size_t size, int flags)
-LSM_HOOK(int, 0, inode_getxattr, struct dentry *dentry, const char *name)
-LSM_HOOK(int, 0, inode_listxattr, struct dentry *dentry)
-LSM_HOOK(int, 0, inode_removexattr, struct mnt_idmap *idmap,
+LSM_HOOK(int, 0, 0, inode_getxattr, struct dentry *dentry, const char *name)
+LSM_HOOK(int, 0, 0, inode_listxattr, struct dentry *dentry)
+LSM_HOOK(int, 0, 0, inode_removexattr, struct mnt_idmap *idmap,
struct dentry *dentry, const char *name)
-LSM_HOOK(void, LSM_RET_VOID, inode_post_removexattr, struct dentry *dentry,
+LSM_HOOK(void, LSM_RET_VOID, 0, inode_post_removexattr, struct dentry *dentry,
const char *name)
-LSM_HOOK(int, 0, inode_file_setattr, struct dentry *dentry, struct file_kattr *fa)
-LSM_HOOK(int, 0, inode_file_getattr, struct dentry *dentry, struct file_kattr *fa)
-LSM_HOOK(int, 0, inode_set_acl, struct mnt_idmap *idmap,
+LSM_HOOK(int, 0, 0, inode_file_setattr, struct dentry *dentry,
+ struct file_kattr *fa)
+LSM_HOOK(int, 0, 0, inode_file_getattr, struct dentry *dentry,
+ struct file_kattr *fa)
+LSM_HOOK(int, 0, 0, inode_set_acl, struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name, struct posix_acl *kacl)
-LSM_HOOK(void, LSM_RET_VOID, inode_post_set_acl, struct dentry *dentry,
+LSM_HOOK(void, LSM_RET_VOID, 0, inode_post_set_acl, struct dentry *dentry,
const char *acl_name, struct posix_acl *kacl)
-LSM_HOOK(int, 0, inode_get_acl, struct mnt_idmap *idmap,
+LSM_HOOK(int, 0, 0, inode_get_acl, struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name)
-LSM_HOOK(int, 0, inode_remove_acl, struct mnt_idmap *idmap,
+LSM_HOOK(int, 0, 0, inode_remove_acl, struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name)
-LSM_HOOK(void, LSM_RET_VOID, inode_post_remove_acl, struct mnt_idmap *idmap,
+LSM_HOOK(void, LSM_RET_VOID, 0, inode_post_remove_acl, struct mnt_idmap *idmap,
struct dentry *dentry, const char *acl_name)
-LSM_HOOK(int, 0, inode_need_killpriv, struct dentry *dentry)
-LSM_HOOK(int, 0, inode_killpriv, struct mnt_idmap *idmap,
+LSM_HOOK(int, 0, 0, inode_need_killpriv, struct dentry *dentry)
+LSM_HOOK(int, 0, 0, inode_killpriv, struct mnt_idmap *idmap,
struct dentry *dentry)
-LSM_HOOK(int, -EOPNOTSUPP, inode_getsecurity, struct mnt_idmap *idmap,
+LSM_HOOK(int, -EOPNOTSUPP, 0, inode_getsecurity, struct mnt_idmap *idmap,
struct inode *inode, const char *name, void **buffer, bool alloc)
-LSM_HOOK(int, -EOPNOTSUPP, inode_setsecurity, struct inode *inode,
+LSM_HOOK(int, -EOPNOTSUPP, 0, inode_setsecurity, struct inode *inode,
const char *name, const void *value, size_t size, int flags)
-LSM_HOOK(int, 0, inode_listsecurity, struct inode *inode, char *buffer,
+LSM_HOOK(int, 0, 0, inode_listsecurity, struct inode *inode, char *buffer,
size_t buffer_size)
-LSM_HOOK(void, LSM_RET_VOID, inode_getlsmprop, struct inode *inode,
+LSM_HOOK(void, LSM_RET_VOID, 0, inode_getlsmprop, struct inode *inode,
struct lsm_prop *prop)
-LSM_HOOK(int, 0, inode_copy_up, struct dentry *src, struct cred **new)
-LSM_HOOK(int, -EOPNOTSUPP, inode_copy_up_xattr, struct dentry *src,
+LSM_HOOK(int, 0, 0, inode_copy_up, struct dentry *src, struct cred **new)
+LSM_HOOK(int, -EOPNOTSUPP, 0, inode_copy_up_xattr, struct dentry *src,
const char *name)
-LSM_HOOK(int, 0, inode_setintegrity, const struct inode *inode,
+LSM_HOOK(int, 0, 0, inode_setintegrity, const struct inode *inode,
enum lsm_integrity_type type, const void *value, size_t size)
-LSM_HOOK(int, 0, kernfs_init_security, struct kernfs_node *kn_dir,
+LSM_HOOK(int, 0, 0, kernfs_init_security, struct kernfs_node *kn_dir,
struct kernfs_node *kn)
-LSM_HOOK(int, 0, file_permission, struct file *file, int mask)
-LSM_HOOK(int, 0, file_alloc_security, struct file *file)
-LSM_HOOK(void, LSM_RET_VOID, file_release, struct file *file)
-LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file)
-LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd,
+LSM_HOOK(int, 0, 0, file_permission, struct file *file, int mask)
+LSM_HOOK(int, 0, 0, file_alloc_security, struct file *file)
+LSM_HOOK(void, LSM_RET_VOID, 0, file_release, struct file *file)
+LSM_HOOK(void, LSM_RET_VOID, 0, file_free_security, struct file *file)
+LSM_HOOK(int, 0, 0, file_ioctl, struct file *file, unsigned int cmd,
unsigned long arg)
-LSM_HOOK(int, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
+LSM_HOOK(int, 0, 0, file_ioctl_compat, struct file *file, unsigned int cmd,
unsigned long arg)
-LSM_HOOK(int, 0, mmap_addr, unsigned long addr)
-LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot,
+LSM_HOOK(int, 0, 0, mmap_addr, unsigned long addr)
+LSM_HOOK(int, 0, 0, mmap_file, struct file *file, unsigned long reqprot,
unsigned long prot, unsigned long flags)
-LSM_HOOK(int, 0, file_mprotect, struct vm_area_struct *vma,
+LSM_HOOK(int, 0, 0, file_mprotect, struct vm_area_struct *vma,
unsigned long reqprot, unsigned long prot)
-LSM_HOOK(int, 0, file_lock, struct file *file, unsigned int cmd)
-LSM_HOOK(int, 0, file_fcntl, struct file *file, unsigned int cmd,
+LSM_HOOK(int, 0, 0, file_lock, struct file *file, unsigned int cmd)
+LSM_HOOK(int, 0, 0, file_fcntl, struct file *file, unsigned int cmd,
unsigned long arg)
-LSM_HOOK(void, LSM_RET_VOID, file_set_fowner, struct file *file)
-LSM_HOOK(int, 0, file_send_sigiotask, struct task_struct *tsk,
+LSM_HOOK(void, LSM_RET_VOID, 0, file_set_fowner, struct file *file)
+LSM_HOOK(int, 0, 0, file_send_sigiotask, struct task_struct *tsk,
struct fown_struct *fown, int sig)
-LSM_HOOK(int, 0, file_receive, struct file *file)
-LSM_HOOK(int, 0, file_open, struct file *file)
-LSM_HOOK(int, 0, file_post_open, struct file *file, int mask)
-LSM_HOOK(int, 0, file_truncate, struct file *file)
-LSM_HOOK(int, 0, task_alloc, struct task_struct *task,
- u64 clone_flags)
-LSM_HOOK(void, LSM_RET_VOID, task_free, struct task_struct *task)
-LSM_HOOK(int, 0, cred_alloc_blank, struct cred *cred, gfp_t gfp)
-LSM_HOOK(void, LSM_RET_VOID, cred_free, struct cred *cred)
-LSM_HOOK(int, 0, cred_prepare, struct cred *new, const struct cred *old,
+LSM_HOOK(int, 0, 0, file_receive, struct file *file)
+LSM_HOOK(int, 0, 0, file_open, struct file *file)
+LSM_HOOK(int, 0, 0, file_post_open, struct file *file, int mask)
+LSM_HOOK(int, 0, 0, file_truncate, struct file *file)
+LSM_HOOK(int, 0, 0, task_alloc, struct task_struct *task, u64 clone_flags)
+LSM_HOOK(void, LSM_RET_VOID, 0, task_free, struct task_struct *task)
+LSM_HOOK(int, 0, 0, cred_alloc_blank, struct cred *cred, gfp_t gfp)
+LSM_HOOK(void, LSM_RET_VOID, 0, cred_free, struct cred *cred)
+LSM_HOOK(int, 0, 0, cred_prepare, struct cred *new, const struct cred *old,
gfp_t gfp)
-LSM_HOOK(void, LSM_RET_VOID, cred_transfer, struct cred *new,
+LSM_HOOK(void, LSM_RET_VOID, 0, cred_transfer, struct cred *new,
const struct cred *old)
-LSM_HOOK(void, LSM_RET_VOID, cred_getsecid, const struct cred *c, u32 *secid)
-LSM_HOOK(void, LSM_RET_VOID, cred_getlsmprop, const struct cred *c,
+LSM_HOOK(void, LSM_RET_VOID, 0, cred_getsecid, const struct cred *c, u32 *secid)
+LSM_HOOK(void, LSM_RET_VOID, 0, cred_getlsmprop, const struct cred *c,
struct lsm_prop *prop)
-LSM_HOOK(int, 0, kernel_act_as, struct cred *new, u32 secid)
-LSM_HOOK(int, 0, kernel_create_files_as, struct cred *new, struct inode *inode)
-LSM_HOOK(int, 0, kernel_module_request, char *kmod_name)
-LSM_HOOK(int, 0, kernel_load_data, enum kernel_load_data_id id, bool contents)
-LSM_HOOK(int, 0, kernel_post_load_data, char *buf, loff_t size,
+LSM_HOOK(int, 0, 0, kernel_act_as, struct cred *new, u32 secid)
+LSM_HOOK(int, 0, 0, kernel_create_files_as, struct cred *new,
+ struct inode *inode)
+LSM_HOOK(int, 0, 0, kernel_module_request, char *kmod_name)
+LSM_HOOK(int, 0, 0, kernel_load_data, enum kernel_load_data_id id,
+ bool contents)
+LSM_HOOK(int, 0, 0, kernel_post_load_data, char *buf, loff_t size,
enum kernel_load_data_id id, char *description)
-LSM_HOOK(int, 0, kernel_read_file, struct file *file,
+LSM_HOOK(int, 0, 0, kernel_read_file, struct file *file,
enum kernel_read_file_id id, bool contents)
-LSM_HOOK(int, 0, kernel_post_read_file, struct file *file, char *buf,
+LSM_HOOK(int, 0, 0, kernel_post_read_file, struct file *file, char *buf,
loff_t size, enum kernel_read_file_id id)
-LSM_HOOK(int, 0, task_fix_setuid, struct cred *new, const struct cred *old,
+LSM_HOOK(int, 0, 0, task_fix_setuid, struct cred *new, const struct cred *old,
int flags)
-LSM_HOOK(int, 0, task_fix_setgid, struct cred *new, const struct cred * old,
+LSM_HOOK(int, 0, 0, task_fix_setgid, struct cred *new, const struct cred *old,
int flags)
-LSM_HOOK(int, 0, task_fix_setgroups, struct cred *new, const struct cred * old)
-LSM_HOOK(int, 0, task_setpgid, struct task_struct *p, pid_t pgid)
-LSM_HOOK(int, 0, task_getpgid, struct task_struct *p)
-LSM_HOOK(int, 0, task_getsid, struct task_struct *p)
-LSM_HOOK(void, LSM_RET_VOID, current_getlsmprop_subj, struct lsm_prop *prop)
-LSM_HOOK(void, LSM_RET_VOID, task_getlsmprop_obj,
- struct task_struct *p, struct lsm_prop *prop)
-LSM_HOOK(int, 0, task_setnice, struct task_struct *p, int nice)
-LSM_HOOK(int, 0, task_setioprio, struct task_struct *p, int ioprio)
-LSM_HOOK(int, 0, task_getioprio, struct task_struct *p)
-LSM_HOOK(int, 0, task_prlimit, const struct cred *cred,
+LSM_HOOK(int, 0, 0, task_fix_setgroups, struct cred *new,
+ const struct cred *old)
+LSM_HOOK(int, 0, 0, task_setpgid, struct task_struct *p, pid_t pgid)
+LSM_HOOK(int, 0, 0, task_getpgid, struct task_struct *p)
+LSM_HOOK(int, 0, 0, task_getsid, struct task_struct *p)
+LSM_HOOK(void, LSM_RET_VOID, 0, current_getlsmprop_subj, struct lsm_prop *prop)
+LSM_HOOK(void, LSM_RET_VOID, 0, task_getlsmprop_obj, struct task_struct *p,
+ struct lsm_prop *prop)
+LSM_HOOK(int, 0, 0, task_setnice, struct task_struct *p, int nice)
+LSM_HOOK(int, 0, 0, task_setioprio, struct task_struct *p, int ioprio)
+LSM_HOOK(int, 0, 0, task_getioprio, struct task_struct *p)
+LSM_HOOK(int, 0, 0, task_prlimit, const struct cred *cred,
const struct cred *tcred, unsigned int flags)
-LSM_HOOK(int, 0, task_setrlimit, struct task_struct *p, unsigned int resource,
- struct rlimit *new_rlim)
-LSM_HOOK(int, 0, task_setscheduler, struct task_struct *p)
-LSM_HOOK(int, 0, task_getscheduler, struct task_struct *p)
-LSM_HOOK(int, 0, task_movememory, struct task_struct *p)
-LSM_HOOK(int, 0, task_kill, struct task_struct *p, struct kernel_siginfo *info,
- int sig, const struct cred *cred)
-LSM_HOOK(int, -ENOSYS, task_prctl, int option, unsigned long arg2,
+LSM_HOOK(int, 0, 0, task_setrlimit, struct task_struct *p,
+ unsigned int resource, struct rlimit *new_rlim)
+LSM_HOOK(int, 0, 0, task_setscheduler, struct task_struct *p)
+LSM_HOOK(int, 0, 0, task_getscheduler, struct task_struct *p)
+LSM_HOOK(int, 0, 0, task_movememory, struct task_struct *p)
+LSM_HOOK(int, 0, 0, task_kill, struct task_struct *p,
+ struct kernel_siginfo *info, int sig, const struct cred *cred)
+LSM_HOOK(int, -ENOSYS, 0, task_prctl, int option, unsigned long arg2,
unsigned long arg3, unsigned long arg4, unsigned long arg5)
-LSM_HOOK(void, LSM_RET_VOID, task_to_inode, struct task_struct *p,
+LSM_HOOK(void, LSM_RET_VOID, 0, task_to_inode, struct task_struct *p,
struct inode *inode)
-LSM_HOOK(int, 0, userns_create, const struct cred *cred)
-LSM_HOOK(int, 0, ipc_permission, struct kern_ipc_perm *ipcp, short flag)
-LSM_HOOK(void, LSM_RET_VOID, ipc_getlsmprop, struct kern_ipc_perm *ipcp,
+LSM_HOOK(int, 0, 0, userns_create, const struct cred *cred)
+LSM_HOOK(int, 0, 0, ipc_permission, struct kern_ipc_perm *ipcp, short flag)
+LSM_HOOK(void, LSM_RET_VOID, 0, ipc_getlsmprop, struct kern_ipc_perm *ipcp,
struct lsm_prop *prop)
-LSM_HOOK(int, 0, msg_msg_alloc_security, struct msg_msg *msg)
-LSM_HOOK(void, LSM_RET_VOID, msg_msg_free_security, struct msg_msg *msg)
-LSM_HOOK(int, 0, msg_queue_alloc_security, struct kern_ipc_perm *perm)
-LSM_HOOK(void, LSM_RET_VOID, msg_queue_free_security,
+LSM_HOOK(int, 0, 0, msg_msg_alloc_security, struct msg_msg *msg)
+LSM_HOOK(void, LSM_RET_VOID, 0, msg_msg_free_security, struct msg_msg *msg)
+LSM_HOOK(int, 0, 0, msg_queue_alloc_security, struct kern_ipc_perm *perm)
+LSM_HOOK(void, LSM_RET_VOID, 0, msg_queue_free_security,
struct kern_ipc_perm *perm)
-LSM_HOOK(int, 0, msg_queue_associate, struct kern_ipc_perm *perm, int msqflg)
-LSM_HOOK(int, 0, msg_queue_msgctl, struct kern_ipc_perm *perm, int cmd)
-LSM_HOOK(int, 0, msg_queue_msgsnd, struct kern_ipc_perm *perm,
+LSM_HOOK(int, 0, 0, msg_queue_associate, struct kern_ipc_perm *perm, int msqflg)
+LSM_HOOK(int, 0, 0, msg_queue_msgctl, struct kern_ipc_perm *perm, int cmd)
+LSM_HOOK(int, 0, 0, msg_queue_msgsnd, struct kern_ipc_perm *perm,
struct msg_msg *msg, int msqflg)
-LSM_HOOK(int, 0, msg_queue_msgrcv, struct kern_ipc_perm *perm,
+LSM_HOOK(int, 0, 0, msg_queue_msgrcv, struct kern_ipc_perm *perm,
struct msg_msg *msg, struct task_struct *target, long type, int mode)
-LSM_HOOK(int, 0, shm_alloc_security, struct kern_ipc_perm *perm)
-LSM_HOOK(void, LSM_RET_VOID, shm_free_security, struct kern_ipc_perm *perm)
-LSM_HOOK(int, 0, shm_associate, struct kern_ipc_perm *perm, int shmflg)
-LSM_HOOK(int, 0, shm_shmctl, struct kern_ipc_perm *perm, int cmd)
-LSM_HOOK(int, 0, shm_shmat, struct kern_ipc_perm *perm, char __user *shmaddr,
- int shmflg)
-LSM_HOOK(int, 0, sem_alloc_security, struct kern_ipc_perm *perm)
-LSM_HOOK(void, LSM_RET_VOID, sem_free_security, struct kern_ipc_perm *perm)
-LSM_HOOK(int, 0, sem_associate, struct kern_ipc_perm *perm, int semflg)
-LSM_HOOK(int, 0, sem_semctl, struct kern_ipc_perm *perm, int cmd)
-LSM_HOOK(int, 0, sem_semop, struct kern_ipc_perm *perm, struct sembuf *sops,
+LSM_HOOK(int, 0, 0, shm_alloc_security, struct kern_ipc_perm *perm)
+LSM_HOOK(void, LSM_RET_VOID, 0, shm_free_security, struct kern_ipc_perm *perm)
+LSM_HOOK(int, 0, 0, shm_associate, struct kern_ipc_perm *perm, int shmflg)
+LSM_HOOK(int, 0, 0, shm_shmctl, struct kern_ipc_perm *perm, int cmd)
+LSM_HOOK(int, 0, 0, shm_shmat, struct kern_ipc_perm *perm,
+ char __user *shmaddr, int shmflg)
+LSM_HOOK(int, 0, 0, sem_alloc_security, struct kern_ipc_perm *perm)
+LSM_HOOK(void, LSM_RET_VOID, 0, sem_free_security, struct kern_ipc_perm *perm)
+LSM_HOOK(int, 0, 0, sem_associate, struct kern_ipc_perm *perm, int semflg)
+LSM_HOOK(int, 0, 0, sem_semctl, struct kern_ipc_perm *perm, int cmd)
+LSM_HOOK(int, 0, 0, sem_semop, struct kern_ipc_perm *perm, struct sembuf *sops,
unsigned nsops, int alter)
-LSM_HOOK(int, 0, netlink_send, struct sock *sk, struct sk_buff *skb)
-LSM_HOOK(void, LSM_RET_VOID, d_instantiate, struct dentry *dentry,
+LSM_HOOK(int, 0, 0, netlink_send, struct sock *sk, struct sk_buff *skb)
+LSM_HOOK(void, LSM_RET_VOID, 0, d_instantiate, struct dentry *dentry,
struct inode *inode)
-LSM_HOOK(int, -EOPNOTSUPP, getselfattr, unsigned int attr,
+LSM_HOOK(int, -EOPNOTSUPP, 0, getselfattr, unsigned int attr,
struct lsm_ctx __user *ctx, u32 *size, u32 flags)
-LSM_HOOK(int, -EOPNOTSUPP, setselfattr, unsigned int attr,
+LSM_HOOK(int, -EOPNOTSUPP, 0, setselfattr, unsigned int attr,
struct lsm_ctx *ctx, u32 size, u32 flags)
-LSM_HOOK(int, -EINVAL, getprocattr, struct task_struct *p, const char *name,
+LSM_HOOK(int, -EINVAL, 0, getprocattr, struct task_struct *p, const char *name,
char **value)
-LSM_HOOK(int, -EINVAL, setprocattr, const char *name, void *value, size_t size)
-LSM_HOOK(int, 0, ismaclabel, const char *name)
-LSM_HOOK(int, -EOPNOTSUPP, secid_to_secctx, u32 secid, struct lsm_context *cp)
-LSM_HOOK(int, -EOPNOTSUPP, lsmprop_to_secctx, struct lsm_prop *prop,
+LSM_HOOK(int, -EINVAL, 0, setprocattr, const char *name, void *value,
+ size_t size)
+LSM_HOOK(int, 0, 0, ismaclabel, const char *name)
+LSM_HOOK(int, -EOPNOTSUPP, 0, secid_to_secctx, u32 secid,
+ struct lsm_context *cp)
+LSM_HOOK(int, -EOPNOTSUPP, 0, lsmprop_to_secctx, struct lsm_prop *prop,
struct lsm_context *cp)
-LSM_HOOK(int, 0, secctx_to_secid, const char *secdata, u32 seclen, u32 *secid)
-LSM_HOOK(void, LSM_RET_VOID, release_secctx, struct lsm_context *cp)
-LSM_HOOK(void, LSM_RET_VOID, inode_invalidate_secctx, struct inode *inode)
-LSM_HOOK(int, 0, inode_notifysecctx, struct inode *inode, void *ctx, u32 ctxlen)
-LSM_HOOK(int, 0, inode_setsecctx, struct dentry *dentry, void *ctx, u32 ctxlen)
-LSM_HOOK(int, -EOPNOTSUPP, inode_getsecctx, struct inode *inode,
+LSM_HOOK(int, 0, 0, secctx_to_secid, const char *secdata, u32 seclen,
+ u32 *secid)
+LSM_HOOK(void, LSM_RET_VOID, 0, release_secctx, struct lsm_context *cp)
+LSM_HOOK(void, LSM_RET_VOID, 0, inode_invalidate_secctx, struct inode *inode)
+LSM_HOOK(int, 0, 0, inode_notifysecctx, struct inode *inode, void *ctx,
+ u32 ctxlen)
+LSM_HOOK(int, 0, 0, inode_setsecctx, struct dentry *dentry, void *ctx,
+ u32 ctxlen)
+LSM_HOOK(int, -EOPNOTSUPP, 0, inode_getsecctx, struct inode *inode,
struct lsm_context *cp)
#if defined(CONFIG_SECURITY) && defined(CONFIG_WATCH_QUEUE)
-LSM_HOOK(int, 0, post_notification, const struct cred *w_cred,
+LSM_HOOK(int, 0, 0, post_notification, const struct cred *w_cred,
const struct cred *cred, struct watch_notification *n)
#endif /* CONFIG_SECURITY && CONFIG_WATCH_QUEUE */
#if defined(CONFIG_SECURITY) && defined(CONFIG_KEY_NOTIFICATIONS)
-LSM_HOOK(int, 0, watch_key, struct key *key)
+LSM_HOOK(int, 0, 0, watch_key, struct key *key)
#endif /* CONFIG_SECURITY && CONFIG_KEY_NOTIFICATIONS */
#ifdef CONFIG_SECURITY_NETWORK
-LSM_HOOK(int, 0, unix_stream_connect, struct sock *sock, struct sock *other,
+LSM_HOOK(int, 0, 0, unix_stream_connect, struct sock *sock, struct sock *other,
struct sock *newsk)
-LSM_HOOK(int, 0, unix_may_send, struct socket *sock, struct socket *other)
-LSM_HOOK(int, 0, socket_create, int family, int type, int protocol, int kern)
-LSM_HOOK(int, 0, socket_post_create, struct socket *sock, int family, int type,
- int protocol, int kern)
-LSM_HOOK(int, 0, socket_socketpair, struct socket *socka, struct socket *sockb)
-LSM_HOOK(int, 0, socket_bind, struct socket *sock, struct sockaddr *address,
- int addrlen)
-LSM_HOOK(int, 0, socket_connect, struct socket *sock, struct sockaddr *address,
+LSM_HOOK(int, 0, 0, unix_may_send, struct socket *sock, struct socket *other)
+LSM_HOOK(int, 0, 0, socket_create, int family, int type, int protocol,
+ int kern)
+LSM_HOOK(int, 0, 0, socket_post_create, struct socket *sock, int family,
+ int type, int protocol, int kern)
+LSM_HOOK(int, 0, 0, socket_socketpair, struct socket *socka,
+ struct socket *sockb)
+LSM_HOOK(int, 0, 0, socket_bind, struct socket *sock, struct sockaddr *address,
int addrlen)
-LSM_HOOK(int, 0, socket_listen, struct socket *sock, int backlog)
-LSM_HOOK(int, 0, socket_accept, struct socket *sock, struct socket *newsock)
-LSM_HOOK(int, 0, socket_sendmsg, struct socket *sock, struct msghdr *msg,
+LSM_HOOK(int, 0, 0, socket_connect, struct socket *sock,
+ struct sockaddr *address, int addrlen)
+LSM_HOOK(int, 0, 0, socket_listen, struct socket *sock, int backlog)
+LSM_HOOK(int, 0, 0, socket_accept, struct socket *sock, struct socket *newsock)
+LSM_HOOK(int, 0, 0, socket_sendmsg, struct socket *sock, struct msghdr *msg,
int size)
-LSM_HOOK(int, 0, socket_recvmsg, struct socket *sock, struct msghdr *msg,
+LSM_HOOK(int, 0, 0, socket_recvmsg, struct socket *sock, struct msghdr *msg,
int size, int flags)
-LSM_HOOK(int, 0, socket_getsockname, struct socket *sock)
-LSM_HOOK(int, 0, socket_getpeername, struct socket *sock)
-LSM_HOOK(int, 0, socket_getsockopt, struct socket *sock, int level, int optname)
-LSM_HOOK(int, 0, socket_setsockopt, struct socket *sock, int level, int optname)
-LSM_HOOK(int, 0, socket_shutdown, struct socket *sock, int how)
-LSM_HOOK(int, 0, socket_sock_rcv_skb, struct sock *sk, struct sk_buff *skb)
-LSM_HOOK(int, -ENOPROTOOPT, socket_getpeersec_stream, struct socket *sock,
+LSM_HOOK(int, 0, 0, socket_getsockname, struct socket *sock)
+LSM_HOOK(int, 0, 0, socket_getpeername, struct socket *sock)
+LSM_HOOK(int, 0, 0, socket_getsockopt, struct socket *sock, int level,
+ int optname)
+LSM_HOOK(int, 0, 0, socket_setsockopt, struct socket *sock, int level,
+ int optname)
+LSM_HOOK(int, 0, 0, socket_shutdown, struct socket *sock, int how)
+LSM_HOOK(int, 0, 0, socket_sock_rcv_skb, struct sock *sk, struct sk_buff *skb)
+LSM_HOOK(int, -ENOPROTOOPT, 0, socket_getpeersec_stream, struct socket *sock,
sockptr_t optval, sockptr_t optlen, unsigned int len)
-LSM_HOOK(int, -ENOPROTOOPT, socket_getpeersec_dgram, struct socket *sock,
+LSM_HOOK(int, -ENOPROTOOPT, 0, socket_getpeersec_dgram, struct socket *sock,
struct sk_buff *skb, u32 *secid)
-LSM_HOOK(int, 0, sk_alloc_security, struct sock *sk, int family, gfp_t priority)
-LSM_HOOK(void, LSM_RET_VOID, sk_free_security, struct sock *sk)
-LSM_HOOK(void, LSM_RET_VOID, sk_clone_security, const struct sock *sk,
+LSM_HOOK(int, 0, 0, sk_alloc_security, struct sock *sk, int family,
+ gfp_t priority)
+LSM_HOOK(void, LSM_RET_VOID, 0, sk_free_security, struct sock *sk)
+LSM_HOOK(void, LSM_RET_VOID, 0, sk_clone_security, const struct sock *sk,
struct sock *newsk)
-LSM_HOOK(void, LSM_RET_VOID, sk_getsecid, const struct sock *sk, u32 *secid)
-LSM_HOOK(void, LSM_RET_VOID, sock_graft, struct sock *sk, struct socket *parent)
-LSM_HOOK(int, 0, inet_conn_request, const struct sock *sk, struct sk_buff *skb,
- struct request_sock *req)
-LSM_HOOK(void, LSM_RET_VOID, inet_csk_clone, struct sock *newsk,
+LSM_HOOK(void, LSM_RET_VOID, 0, sk_getsecid, const struct sock *sk, u32 *secid)
+LSM_HOOK(void, LSM_RET_VOID, 0, sock_graft, struct sock *sk,
+ struct socket *parent)
+LSM_HOOK(int, 0, 0, inet_conn_request, const struct sock *sk,
+ struct sk_buff *skb, struct request_sock *req)
+LSM_HOOK(void, LSM_RET_VOID, 0, inet_csk_clone, struct sock *newsk,
const struct request_sock *req)
-LSM_HOOK(void, LSM_RET_VOID, inet_conn_established, struct sock *sk,
+LSM_HOOK(void, LSM_RET_VOID, 0, inet_conn_established, struct sock *sk,
struct sk_buff *skb)
-LSM_HOOK(int, 0, secmark_relabel_packet, u32 secid)
-LSM_HOOK(void, LSM_RET_VOID, secmark_refcount_inc, void)
-LSM_HOOK(void, LSM_RET_VOID, secmark_refcount_dec, void)
-LSM_HOOK(void, LSM_RET_VOID, req_classify_flow, const struct request_sock *req,
- struct flowi_common *flic)
-LSM_HOOK(int, 0, tun_dev_alloc_security, void *security)
-LSM_HOOK(int, 0, tun_dev_create, void)
-LSM_HOOK(int, 0, tun_dev_attach_queue, void *security)
-LSM_HOOK(int, 0, tun_dev_attach, struct sock *sk, void *security)
-LSM_HOOK(int, 0, tun_dev_open, void *security)
-LSM_HOOK(int, 0, sctp_assoc_request, struct sctp_association *asoc,
+LSM_HOOK(int, 0, 0, secmark_relabel_packet, u32 secid)
+LSM_HOOK(void, LSM_RET_VOID, 0, secmark_refcount_inc, void)
+LSM_HOOK(void, LSM_RET_VOID, 0, secmark_refcount_dec, void)
+LSM_HOOK(void, LSM_RET_VOID, 0, req_classify_flow,
+ const struct request_sock *req, struct flowi_common *flic)
+LSM_HOOK(int, 0, 0, tun_dev_alloc_security, void *security)
+LSM_HOOK(int, 0, 0, tun_dev_create, void)
+LSM_HOOK(int, 0, 0, tun_dev_attach_queue, void *security)
+LSM_HOOK(int, 0, 0, tun_dev_attach, struct sock *sk, void *security)
+LSM_HOOK(int, 0, 0, tun_dev_open, void *security)
+LSM_HOOK(int, 0, 0, sctp_assoc_request, struct sctp_association *asoc,
struct sk_buff *skb)
-LSM_HOOK(int, 0, sctp_bind_connect, struct sock *sk, int optname,
+LSM_HOOK(int, 0, 0, sctp_bind_connect, struct sock *sk, int optname,
struct sockaddr *address, int addrlen)
-LSM_HOOK(void, LSM_RET_VOID, sctp_sk_clone, struct sctp_association *asoc,
+LSM_HOOK(void, LSM_RET_VOID, 0, sctp_sk_clone, struct sctp_association *asoc,
struct sock *sk, struct sock *newsk)
-LSM_HOOK(int, 0, sctp_assoc_established, struct sctp_association *asoc,
+LSM_HOOK(int, 0, 0, sctp_assoc_established, struct sctp_association *asoc,
struct sk_buff *skb)
-LSM_HOOK(int, 0, mptcp_add_subflow, struct sock *sk, struct sock *ssk)
+LSM_HOOK(int, 0, 0, mptcp_add_subflow, struct sock *sk, struct sock *ssk)
#endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_INFINIBAND
-LSM_HOOK(int, 0, ib_pkey_access, void *sec, u64 subnet_prefix, u16 pkey)
-LSM_HOOK(int, 0, ib_endport_manage_subnet, void *sec, const char *dev_name,
+LSM_HOOK(int, 0, 0, ib_pkey_access, void *sec, u64 subnet_prefix, u16 pkey)
+LSM_HOOK(int, 0, 0, ib_endport_manage_subnet, void *sec, const char *dev_name,
u8 port_num)
-LSM_HOOK(int, 0, ib_alloc_security, void *sec)
+LSM_HOOK(int, 0, 0, ib_alloc_security, void *sec)
#endif /* CONFIG_SECURITY_INFINIBAND */
#ifdef CONFIG_SECURITY_NETWORK_XFRM
-LSM_HOOK(int, 0, xfrm_policy_alloc_security, struct xfrm_sec_ctx **ctxp,
+LSM_HOOK(int, 0, 0, xfrm_policy_alloc_security, struct xfrm_sec_ctx **ctxp,
struct xfrm_user_sec_ctx *sec_ctx, gfp_t gfp)
-LSM_HOOK(int, 0, xfrm_policy_clone_security, struct xfrm_sec_ctx *old_ctx,
+LSM_HOOK(int, 0, 0, xfrm_policy_clone_security, struct xfrm_sec_ctx *old_ctx,
struct xfrm_sec_ctx **new_ctx)
-LSM_HOOK(void, LSM_RET_VOID, xfrm_policy_free_security,
+LSM_HOOK(void, LSM_RET_VOID, 0, xfrm_policy_free_security,
struct xfrm_sec_ctx *ctx)
-LSM_HOOK(int, 0, xfrm_policy_delete_security, struct xfrm_sec_ctx *ctx)
-LSM_HOOK(int, 0, xfrm_state_alloc, struct xfrm_state *x,
+LSM_HOOK(int, 0, 0, xfrm_policy_delete_security, struct xfrm_sec_ctx *ctx)
+LSM_HOOK(int, 0, 0, xfrm_state_alloc, struct xfrm_state *x,
struct xfrm_user_sec_ctx *sec_ctx)
-LSM_HOOK(int, 0, xfrm_state_alloc_acquire, struct xfrm_state *x,
- struct xfrm_sec_ctx *polsec, u32 secid)
-LSM_HOOK(void, LSM_RET_VOID, xfrm_state_free_security, struct xfrm_state *x)
-LSM_HOOK(int, 0, xfrm_state_delete_security, struct xfrm_state *x)
-LSM_HOOK(int, 0, xfrm_policy_lookup, struct xfrm_sec_ctx *ctx, u32 fl_secid)
-LSM_HOOK(int, 1, xfrm_state_pol_flow_match, struct xfrm_state *x,
+LSM_HOOK(int, 0, 0, xfrm_state_alloc_acquire,
+ struct xfrm_state *x, struct xfrm_sec_ctx *polsec, u32 secid)
+LSM_HOOK(void, LSM_RET_VOID, 0, xfrm_state_free_security, struct xfrm_state *x)
+LSM_HOOK(int, 0, 0, xfrm_state_delete_security, struct xfrm_state *x)
+LSM_HOOK(int, 0, 0, xfrm_policy_lookup, struct xfrm_sec_ctx *ctx, u32 fl_secid)
+LSM_HOOK(int, 1, 0, xfrm_state_pol_flow_match, struct xfrm_state *x,
struct xfrm_policy *xp, const struct flowi_common *flic)
-LSM_HOOK(int, 0, xfrm_decode_session, struct sk_buff *skb, u32 *secid,
+LSM_HOOK(int, 0, 0, xfrm_decode_session, struct sk_buff *skb, u32 *secid,
int ckall)
#endif /* CONFIG_SECURITY_NETWORK_XFRM */
/* key management security hooks */
#ifdef CONFIG_KEYS
-LSM_HOOK(int, 0, key_alloc, struct key *key, const struct cred *cred,
+LSM_HOOK(int, 0, 0, key_alloc, struct key *key, const struct cred *cred,
unsigned long flags)
-LSM_HOOK(int, 0, key_permission, key_ref_t key_ref, const struct cred *cred,
- enum key_need_perm need_perm)
-LSM_HOOK(int, 0, key_getsecurity, struct key *key, char **buffer)
-LSM_HOOK(void, LSM_RET_VOID, key_post_create_or_update, struct key *keyring,
+LSM_HOOK(int, 0, 0, key_permission, key_ref_t key_ref,
+ const struct cred *cred, enum key_need_perm need_perm)
+LSM_HOOK(int, 0, 0, key_getsecurity, struct key *key, char **buffer)
+LSM_HOOK(void, LSM_RET_VOID, 0, key_post_create_or_update, struct key *keyring,
struct key *key, const void *payload, size_t payload_len,
unsigned long flags, bool create)
#endif /* CONFIG_KEYS */
#ifdef CONFIG_AUDIT
-LSM_HOOK(int, 0, audit_rule_init, u32 field, u32 op, char *rulestr,
+LSM_HOOK(int, 0, 0, audit_rule_init, u32 field, u32 op, char *rulestr,
void **lsmrule, gfp_t gfp)
-LSM_HOOK(int, 0, audit_rule_known, struct audit_krule *krule)
-LSM_HOOK(int, 0, audit_rule_match, struct lsm_prop *prop, u32 field, u32 op,
+LSM_HOOK(int, 0, 0, audit_rule_known, struct audit_krule *krule)
+LSM_HOOK(int, 0, 0, audit_rule_match, struct lsm_prop *prop, u32 field, u32 op,
void *lsmrule)
-LSM_HOOK(void, LSM_RET_VOID, audit_rule_free, void *lsmrule)
+LSM_HOOK(void, LSM_RET_VOID, 0, audit_rule_free, void *lsmrule)
#endif /* CONFIG_AUDIT */
#ifdef CONFIG_BPF_SYSCALL
-LSM_HOOK(int, 0, bpf, int cmd, union bpf_attr *attr, unsigned int size, bool kernel)
-LSM_HOOK(int, 0, bpf_map, struct bpf_map *map, fmode_t fmode)
-LSM_HOOK(int, 0, bpf_prog, struct bpf_prog *prog)
-LSM_HOOK(int, 0, bpf_map_create, struct bpf_map *map, union bpf_attr *attr,
- struct bpf_token *token, bool kernel)
-LSM_HOOK(void, LSM_RET_VOID, bpf_map_free, struct bpf_map *map)
-LSM_HOOK(int, 0, bpf_prog_load, struct bpf_prog *prog, union bpf_attr *attr,
+LSM_HOOK(int, 0, 0, bpf, int cmd, union bpf_attr *attr, unsigned int size,
+ bool kernel)
+LSM_HOOK(int, 0, 0, bpf_map, struct bpf_map *map, fmode_t fmode)
+LSM_HOOK(int, 0, 0, bpf_prog, struct bpf_prog *prog)
+LSM_HOOK(int, 0, 0, bpf_map_create, struct bpf_map *map, union bpf_attr *attr,
struct bpf_token *token, bool kernel)
-LSM_HOOK(void, LSM_RET_VOID, bpf_prog_free, struct bpf_prog *prog)
-LSM_HOOK(int, 0, bpf_token_create, struct bpf_token *token, union bpf_attr *attr,
- const struct path *path)
-LSM_HOOK(void, LSM_RET_VOID, bpf_token_free, struct bpf_token *token)
-LSM_HOOK(int, 0, bpf_token_cmd, const struct bpf_token *token, enum bpf_cmd cmd)
-LSM_HOOK(int, 0, bpf_token_capable, const struct bpf_token *token, int cap)
+LSM_HOOK(void, LSM_RET_VOID, 0, bpf_map_free, struct bpf_map *map)
+LSM_HOOK(int, 0, 0, bpf_prog_load, struct bpf_prog *prog,
+ union bpf_attr *attr, struct bpf_token *token, bool kernel)
+LSM_HOOK(void, LSM_RET_VOID, 0, bpf_prog_free, struct bpf_prog *prog)
+LSM_HOOK(int, 0, 0, bpf_token_create, struct bpf_token *token,
+ union bpf_attr *attr, const struct path *path)
+LSM_HOOK(void, LSM_RET_VOID, 0, bpf_token_free, struct bpf_token *token)
+LSM_HOOK(int, 0, 0, bpf_token_cmd, const struct bpf_token *token,
+ enum bpf_cmd cmd)
+LSM_HOOK(int, 0, 0, bpf_token_capable, const struct bpf_token *token, int cap)
#endif /* CONFIG_BPF_SYSCALL */
-LSM_HOOK(int, 0, locked_down, enum lockdown_reason what)
+LSM_HOOK(int, 0, 0, locked_down, enum lockdown_reason what)
#ifdef CONFIG_PERF_EVENTS
-LSM_HOOK(int, 0, perf_event_open, int type)
-LSM_HOOK(int, 0, perf_event_alloc, struct perf_event *event)
-LSM_HOOK(int, 0, perf_event_read, struct perf_event *event)
-LSM_HOOK(int, 0, perf_event_write, struct perf_event *event)
+LSM_HOOK(int, 0, 0, perf_event_open, int type)
+LSM_HOOK(int, 0, 0, perf_event_alloc, struct perf_event *event)
+LSM_HOOK(int, 0, 0, perf_event_read, struct perf_event *event)
+LSM_HOOK(int, 0, 0, perf_event_write, struct perf_event *event)
#endif /* CONFIG_PERF_EVENTS */
#ifdef CONFIG_IO_URING
-LSM_HOOK(int, 0, uring_override_creds, const struct cred *new)
-LSM_HOOK(int, 0, uring_sqpoll, void)
-LSM_HOOK(int, 0, uring_cmd, struct io_uring_cmd *ioucmd)
-LSM_HOOK(int, 0, uring_allowed, void)
+LSM_HOOK(int, 0, 0, uring_override_creds, const struct cred *new)
+LSM_HOOK(int, 0, 0, uring_sqpoll, void)
+LSM_HOOK(int, 0, 0, uring_cmd, struct io_uring_cmd *ioucmd)
+LSM_HOOK(int, 0, 0, uring_allowed, void)
#endif /* CONFIG_IO_URING */
-LSM_HOOK(void, LSM_RET_VOID, initramfs_populated, void)
+LSM_HOOK(void, LSM_RET_VOID, 0, initramfs_populated, void)
-LSM_HOOK(int, 0, bdev_alloc_security, struct block_device *bdev)
-LSM_HOOK(void, LSM_RET_VOID, bdev_free_security, struct block_device *bdev)
-LSM_HOOK(int, 0, bdev_setintegrity, struct block_device *bdev,
+LSM_HOOK(int, 0, 0, bdev_alloc_security, struct block_device *bdev)
+LSM_HOOK(void, LSM_RET_VOID, 0, bdev_free_security, struct block_device *bdev)
+LSM_HOOK(int, 0, 0, bdev_setintegrity, struct block_device *bdev,
enum lsm_integrity_type type, const void *value, size_t size)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index b92008641242..4411b47f9a8d 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -36,7 +36,7 @@
#include <linux/lsm_count.h>
union security_list_options {
- #define LSM_HOOK(RET, DEFAULT, NAME, ...) RET (*NAME)(__VA_ARGS__);
+ #define LSM_HOOK(RET, DEFAULT, FLAGS, NAME, ...) RET (*NAME)(__VA_ARGS__);
#include "lsm_hook_defs.h"
#undef LSM_HOOK
void *lsm_func_addr;
@@ -65,7 +65,7 @@ struct lsm_static_call {
* dynamic to adapt the number of static calls to the number of callbacks.
*/
struct lsm_static_calls_table {
- #define LSM_HOOK(RET, DEFAULT, NAME, ...) \
+ #define LSM_HOOK(RET, DEFAULT, FLAGS, NAME, ...) \
struct lsm_static_call NAME[MAX_LSM_COUNT];
#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK
diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c
index 7cb6e8d4282c..53d5c0b69b2c 100644
--- a/kernel/bpf/bpf_lsm.c
+++ b/kernel/bpf/bpf_lsm.c
@@ -20,16 +20,16 @@
/* For every LSM hook that allows attachment of BPF programs, declare a nop
* function where a BPF program can be attached.
*/
-#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
-noinline RET bpf_lsm_##NAME(__VA_ARGS__) \
-{ \
- return DEFAULT; \
+#define LSM_HOOK(RET, DEFAULT, FLAGS, NAME, ...) \
+noinline RET bpf_lsm_##NAME(__VA_ARGS__) \
+{ \
+ return DEFAULT; \
}
#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK
-#define LSM_HOOK(RET, DEFAULT, NAME, ...) BTF_ID(func, bpf_lsm_##NAME)
+#define LSM_HOOK(RET, DEFAULT, FLAGS, NAME, ...) BTF_ID(func, bpf_lsm_##NAME)
BTF_SET_START(bpf_lsm_hooks)
#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK
diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c
index 40efde233f3a..8c4b0ed2b22e 100644
--- a/security/bpf/hooks.c
+++ b/security/bpf/hooks.c
@@ -8,7 +8,7 @@
#include <uapi/linux/lsm.h>
static struct security_hook_list bpf_lsm_hooks[] __ro_after_init = {
- #define LSM_HOOK(RET, DEFAULT, NAME, ...) \
+ #define LSM_HOOK(RET, DEFAULT, FLAGS, NAME, ...) \
LSM_HOOK_INIT(NAME, bpf_lsm_##NAME),
#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK
diff --git a/security/security.c b/security/security.c
index 31a688650601..25e7cfc96f20 100644
--- a/security/security.c
+++ b/security/security.c
@@ -117,7 +117,7 @@ do { \
*((RET(*)(__VA_ARGS__))NULL)); \
DEFINE_STATIC_KEY_FALSE(SECURITY_HOOK_ACTIVE_KEY(NAME, NUM));
-#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
+#define LSM_HOOK(RET, DEFAULT, FLAGS, NAME, ...) \
LSM_DEFINE_UNROLL(DEFINE_LSM_STATIC_CALL, NAME, RET, __VA_ARGS__)
#include <linux/lsm_hook_defs.h>
#undef LSM_HOOK
@@ -142,7 +142,7 @@ struct lsm_static_calls_table
.trampoline = LSM_HOOK_TRAMP(NAME, NUM), \
.active = &SECURITY_HOOK_ACTIVE_KEY(NAME, NUM), \
},
-#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
+#define LSM_HOOK(RET, DEFAULT, FLAGS, NAME, ...) \
.NAME = { \
LSM_DEFINE_UNROLL(INIT_LSM_STATIC_CALL, NAME) \
},
@@ -422,7 +422,7 @@ int lsm_fill_user_ctx(struct lsm_ctx __user *uctx, u32 *uctx_len,
#define DECLARE_LSM_RET_DEFAULT_void(DEFAULT, NAME)
#define DECLARE_LSM_RET_DEFAULT_int(DEFAULT, NAME) \
static const int __maybe_unused LSM_RET_DEFAULT(NAME) = (DEFAULT);
-#define LSM_HOOK(RET, DEFAULT, NAME, ...) \
+#define LSM_HOOK(RET, DEFAULT, FLAGS, NAME, ...) \
DECLARE_LSM_RET_DEFAULT_##RET(DEFAULT, NAME)
#include <linux/lsm_hook_defs.h>
--
2.52.0
^ permalink raw reply related
* [RFC PATCH 0/3] LSM: Hook registration exculsivity
From: Casey Schaufler @ 2026-02-25 19:21 UTC (permalink / raw)
To: casey, paul, linux-security-module
Cc: jmorris, serge, keescook, john.johansen, penguin-kernel,
stephen.smalley.work, selinux
In-Reply-To: <20260225192143.14448-1-casey.ref@schaufler-ca.com>
This patch set represents a reasonably radical change to the LSM
stacking model. The notion of an "exclusive" LSM begins to give
way to exclusive LSM hooks. Instead of disallowing more than one
exclusive LSM, individual hooks are marked as exclusive and only
one is allowed to be registered. Subsequent LSMs that attempt to
register these hooks are denied them. This can have consequences.
The hooks relative to secmarks have been used here to demonstrate
that the scheme can work, and that it isn't always as clean as
one might like.
Please comment fully. Quite a bit of tinkering went into devising
this approach, which is intended to address a number of concerns.
Add a flags field to the LSM hook definition.
The first LSM that requests a hook with the LSM_FLAG_EXCLUSIVE flag
will be the only LSM that can register any hook thus marked.
Attempts by other LSMs to register such hooks are ignored.
Direct access to skb->secmark within LSMs are wrapped with a
helper function lsm_secmark_from_skb(). This function checks to see
if the secmark related LSM hooks, which are marked LSM_FLAG_EXCLUSIVE,
are registered by the calling LSM. If they are, the secmark value is
returned. Otherwise, the invalid secmark value 0 is returned.
Future implementations of lsm_secmark_from_skb() could use some
form of secmark encoding that would allow more than one LSM to
use secmarks at the same time.
The LSMs that currently support use of secmarks are taught how to
identify if they are allowed use of the secmark. Each sets secmark values
differently. At initialization the LSMs have the opportunity to
take steps to ensure correct behavior if they don't have secmark access.
https://github.com/cschaufler/lsm-stacking#secmark-6.19-rc8-v1
Casey Schaufler (3):
LSM: add a flags field to the LSM hook definitions
LSM: Enforce exclusive hooks
LSM: Reserve use of secmarks
include/linux/bpf_lsm.h | 2 +-
include/linux/lsm_hook_defs.h | 614 ++++++++++++++++---------------
include/linux/lsm_hooks.h | 4 +-
include/linux/security.h | 3 +
kernel/bpf/bpf_lsm.c | 10 +-
security/apparmor/lsm.c | 24 +-
security/bpf/hooks.c | 2 +-
security/lsm_init.c | 66 ++++
security/security.c | 21 +-
security/selinux/hooks.c | 35 +-
security/selinux/ss/services.c | 3 +
security/smack/smack_lsm.c | 6 +-
security/smack/smack_netfilter.c | 6 +
13 files changed, 473 insertions(+), 323 deletions(-)
--
2.52.0
^ permalink raw reply
* Re: [PATCH v2] landlock: Expand restrict flags example for ABI version 8
From: Günther Noack @ 2026-02-25 18:53 UTC (permalink / raw)
To: Panagiotis "Ivory" Vasilopoulos
Cc: Mickaël Salaün, Günther Noack, Jonathan Corbet,
Shuah Khan, linux-security-module, linux-doc, linux-kernel
In-Reply-To: <20260221-landlock-docs-add-tsync-example-v2-1-60990986bba5@n0toose.net>
On Sat, Feb 21, 2026 at 11:12:25PM +0100, Panagiotis "Ivory" Vasilopoulos wrote:
> Add LANDLOCK_RESTRICT_SELF_TSYNC to the backwards compatibility example
> for restrict flags. This introduces completeness, similar to that of
> the ruleset attributes example.
>
> Additionally, I modified the two comments of the example to make them
> more consistent with the ruleset attributes example's.
>
> Signed-off-by: Panagiotis 'Ivory' Vasilopoulos <git@n0toose.net>
> ---
> Changes in v2:
> - Fix formatting error.
> - Link to v1: https://lore.kernel.org/r/20260221-landlock-docs-add-tsync-example-v1-1-f89383809eb4@n0toose.net
> ---
> Documentation/userspace-api/landlock.rst | 12 +++++++++---
> 1 file changed, 9 insertions(+), 3 deletions(-)
>
> diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
> index 13134bccdd39d78ddce3daf454f32dda162ce91b..0affe1c953d61a4b32aca700cd262c49cee6304a 100644
> --- a/Documentation/userspace-api/landlock.rst
> +++ b/Documentation/userspace-api/landlock.rst
> @@ -197,12 +197,18 @@ similar backwards compatibility check is needed for the restrict flags
>
> .. code-block:: c
>
> - __u32 restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON;
> - if (abi < 7) {
> - /* Clear logging flags unsupported before ABI 7. */
> + __u32 restrict_flags =
> + LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON |
> + LANDLOCK_RESTRICT_SELF_TSYNC;
> + switch (abi) {
> + case 1 ... 6:
> + /* Clear logging flags unsupported for ABI < 7 */
> restrict_flags &= ~(LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF |
> LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON |
> LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF);
> + case 7:
> + /* Removes multithread flag unsupported for ABI < 8 */
> + restrict_flags &= ~LANDLOCK_RESTRICT_SELF_TSYNC;
> }
Thanks Panagiotis, this makes sense. We overlooked this.
I'm slightly worried that people will copy-paste this example blindly
if it does not have a big warning in it. Unlike the other "backwards
compatibility" example code that we have, this one actually changes
how the enforcement works. (The other flags change logging, but audit
logging makes no difference to the process that sandboxes itself.)
Could you please add wording to the comment to state more explicitly
that below ABI v8, the enforced Landlock policy only applies to the
current thread?
Thanks,
–Günther
^ permalink raw reply
* Re: [PATCH] ima: Add digest_size field to ima_algo_desc structure and use to show meas.
From: Mimi Zohar @ 2026-02-25 18:37 UTC (permalink / raw)
To: Roberto Sassu, dmitry.kasatkin, eric.snowberg, paul, jmorris,
serge
Cc: linux-integrity, linux-security-module, linux-kernel,
devnull+dima.arista.com, Roberto Sassu
In-Reply-To: <20260225125301.87996-1-roberto.sassu@huaweicloud.com>
On Wed, 2026-02-25 at 13:53 +0100, Roberto Sassu wrote:
> From: Roberto Sassu <roberto.sassu@huawei.com>
>
> Add the digest_size field to the ima_algo_desc structure to determine the
> digest size from the correct source.
>
> If the hash algorithm is among allocated PCR banks, take the value from the
> TPM bank info; if the hash algorithm is SHA1, use the predefined value; if
> the hash algorithm is the default one but not among the PCR banks, take the
> digest size from the crypto subsystem (the default hash algorithm is
> checked when parsing the ima_hash= command line option).
>
> Finally, use the new information to correctly show the template digest in
> ima_measurements_show() and ima_ascii_measurements_show().
>
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
Thanks, Roberto. The patch looks fine, other than the patch title. Could it be
renamed as "ima: define and use a digest_size field in the ima_algo_desc
structure"?
thanks,
Mimi
^ permalink raw reply
* Re: [PATCH v5] ima_fs: Avoid creating measurement lists for unsupported hash algos
From: Dmitry Safonov @ 2026-02-25 18:00 UTC (permalink / raw)
To: Roberto Sassu
Cc: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
Paul Moore, James Morris, Serge E. Hallyn, Silvia Sisinni,
Enrico Bravi, Jonathan McDowell, linux-integrity,
linux-security-module, linux-kernel, stable, Dmitry Safonov
In-Reply-To: <5579780966d26d2fd0e3756d404d2156bd55a06b.camel@huaweicloud.com>
On Wed, Feb 25, 2026 at 1:20 PM Roberto Sassu
<roberto.sassu@huaweicloud.com> wrote:
>
> On Mon, 2026-02-23 at 14:56 +0000, Dmitry Safonov via B4 Relay wrote:
[..]
> > @@ -252,7 +245,8 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
> > seq_printf(m, "%2d ", e->pcr);
> >
> > /* 2nd: template hash */
> > - ima_print_digest(m, e->digests[algo_idx].digest, hash_digest_size[algo]);
> > + ima_print_digest(m, e->digests[algo_idx].digest,
> > + ima_tpm_chip->allocated_banks[algo_idx].digest_size);
>
> Sorry, I realized that this does not work if SHA1 or the default hash
> algorithm are not among allocated PCR banks.
>
> I just sent a patch to correctly determine the digest size:
>
> https://marc.info/?l=linux-integrity&m=177202677128752&w=2
>
> and applied yours on top of that (if it is fine for you):
>
> https://github.com/linux-integrity/linux/commit/6efbd2b38b102ecbadc350228cc30fd67666a089
>
Thanks!
Dmitry
^ permalink raw reply
* Re: [PATCH v5] ima_fs: Avoid creating measurement lists for unsupported hash algos
From: Roberto Sassu @ 2026-02-25 13:19 UTC (permalink / raw)
To: dima, Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
Paul Moore, James Morris, Serge E. Hallyn, Silvia Sisinni,
Enrico Bravi
Cc: Jonathan McDowell, linux-integrity, linux-security-module,
linux-kernel, stable, Dmitry Safonov
In-Reply-To: <20260223-ima-oob-v5-1-91cc1064e767@arista.com>
On Mon, 2026-02-23 at 14:56 +0000, Dmitry Safonov via B4 Relay wrote:
> From: Dmitry Safonov <dima@arista.com>
>
> ima_tpm_chip->allocated_banks[i].crypto_id is initialized to
> HASH_ALGO__LAST if the TPM algorithm is not supported. However there
> are places relying on the algorithm to be valid because it is accessed
> by hash_algo_name[].
>
> On 6.12.40 I observe the following read out-of-bounds in hash_algo_name:
> ==================================================================
> BUG: KASAN: global-out-of-bounds in create_securityfs_measurement_lists+0x396/0x440
> Read of size 8 at addr ffffffff83e18138 by task swapper/0/1
>
> CPU: 4 UID: 0 PID: 1 Comm: swapper/0 Not tainted 6.12.40 #3
> Call Trace:
> <TASK>
> dump_stack_lvl+0x61/0x90
> print_report+0xc4/0x580
> ? kasan_addr_to_slab+0x26/0x80
> ? create_securityfs_measurement_lists+0x396/0x440
> kasan_report+0xc2/0x100
> ? create_securityfs_measurement_lists+0x396/0x440
> create_securityfs_measurement_lists+0x396/0x440
> ima_fs_init+0xa3/0x300
> ima_init+0x7d/0xd0
> init_ima+0x28/0x100
> do_one_initcall+0xa6/0x3e0
> kernel_init_freeable+0x455/0x740
> kernel_init+0x24/0x1d0
> ret_from_fork+0x38/0x80
> ret_from_fork_asm+0x11/0x20
> </TASK>
>
> The buggy address belongs to the variable:
> hash_algo_name+0xb8/0x420
>
> Memory state around the buggy address:
> ffffffff83e18000: 00 01 f9 f9 f9 f9 f9 f9 00 01 f9 f9 f9 f9 f9 f9
> ffffffff83e18080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> >ffffffff83e18100: 00 00 00 00 00 00 00 f9 f9 f9 f9 f9 00 05 f9 f9
> ^
> ffffffff83e18180: f9 f9 f9 f9 00 00 00 00 00 00 00 04 f9 f9 f9 f9
> ffffffff83e18200: 00 00 00 00 00 00 00 00 04 f9 f9 f9 f9 f9 f9 f9
> ==================================================================
>
> Seems like the TPM chip supports sha3_256, which isn't yet in
> tpm_algorithms:
> tpm tpm0: TPM with unsupported bank algorithm 0x0027
>
> Thus solve the problem by creating a file name with "_tpm_alg_<ID>"
> postfix if the crypto algorithm isn't initialized.
>
> This is how it looks on the test machine (patch ported to v6.12 release):
> # ls -1 /sys/kernel/security/ima/
> ascii_runtime_measurements
> ascii_runtime_measurements_tpm_alg_27
> ascii_runtime_measurements_sha1
> ascii_runtime_measurements_sha256
> binary_runtime_measurements
> binary_runtime_measurements_tpm_alg_27
> binary_runtime_measurements_sha1
> binary_runtime_measurements_sha256
> policy
> runtime_measurements_count
> violations
>
> Fixes: 9fa8e7625008 ("ima: add crypto agility support for template-hash algorithm")
> Signed-off-by: Dmitry Safonov <dima@arista.com>
> Cc: Enrico Bravi <enrico.bravi@polito.it>
> Cc: Silvia Sisinni <silvia.sisinni@polito.it>
> Cc: Roberto Sassu <roberto.sassu@huawei.com>
> Cc: Mimi Zohar <zohar@linux.ibm.com>
> ---
> Changes in v5:
> - Use lower-case for sysfs file name (as suggested-by Jonathan and Roberto)
> - Don't use email quotes for patch description (Roberto)
> - Re-word the patch description (suggested-by Roberto)
> - Link to v4: https://lore.kernel.org/r/20260127-ima-oob-v4-1-bf0cd7f9b4d4@arista.com
>
> Changes in v4:
> - Use ima_tpm_chip->allocated_banks[algo_idx].digest_size instead of hash_digest_size[algo]
> (Roberto Sassu)
> - Link to v3: https://lore.kernel.org/r/20260127-ima-oob-v3-1-1dd09f4c2a6a@arista.com
> Testing note: I test it on v6.12.40 kernel backport, which slightly differs as
> lookup_template_data_hash_algo() was yet present.
>
> Changes in v3:
> - Now fix the spelling *for real* (sorry, messed it up in v2)
> - Link to v2: https://lore.kernel.org/r/20260127-ima-oob-v2-1-f38a18c850cf@arista.com
>
> Changes in v2:
> - Instead of skipping unknown algorithms, add files under their TPM_ALG_ID (Roberto Sassu)
> - Fix spelling (Roberto Sassu)
> - Copy @stable on the fix
> - Link to v1: https://lore.kernel.org/r/20260127-ima-oob-v1-1-2d42f3418e57@arista.com
> ---
> security/integrity/ima/ima_fs.c | 34 ++++++++++++++++++----------------
> 1 file changed, 18 insertions(+), 16 deletions(-)
>
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 012a58959ff0..3d9996ed486d 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -132,16 +132,12 @@ int ima_measurements_show(struct seq_file *m, void *v)
> char *template_name;
> u32 pcr, namelen, template_data_len; /* temporary fields */
> bool is_ima_template = false;
> - enum hash_algo algo;
> int i, algo_idx;
>
> algo_idx = ima_sha1_idx;
> - algo = HASH_ALGO_SHA1;
>
> - if (m->file != NULL) {
> + if (m->file != NULL)
> algo_idx = (unsigned long)file_inode(m->file)->i_private;
> - algo = ima_algo_array[algo_idx].algo;
> - }
>
> /* get entry */
> e = qe->entry;
> @@ -160,7 +156,8 @@ int ima_measurements_show(struct seq_file *m, void *v)
> ima_putc(m, &pcr, sizeof(e->pcr));
>
> /* 2nd: template digest */
> - ima_putc(m, e->digests[algo_idx].digest, hash_digest_size[algo]);
> + ima_putc(m, e->digests[algo_idx].digest,
> + ima_tpm_chip->allocated_banks[algo_idx].digest_size);
>
> /* 3rd: template name size */
> namelen = !ima_canonical_fmt ? strlen(template_name) :
> @@ -229,16 +226,12 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
> struct ima_queue_entry *qe = v;
> struct ima_template_entry *e;
> char *template_name;
> - enum hash_algo algo;
> int i, algo_idx;
>
> algo_idx = ima_sha1_idx;
> - algo = HASH_ALGO_SHA1;
>
> - if (m->file != NULL) {
> + if (m->file != NULL)
> algo_idx = (unsigned long)file_inode(m->file)->i_private;
> - algo = ima_algo_array[algo_idx].algo;
> - }
>
> /* get entry */
> e = qe->entry;
> @@ -252,7 +245,8 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
> seq_printf(m, "%2d ", e->pcr);
>
> /* 2nd: template hash */
> - ima_print_digest(m, e->digests[algo_idx].digest, hash_digest_size[algo]);
> + ima_print_digest(m, e->digests[algo_idx].digest,
> + ima_tpm_chip->allocated_banks[algo_idx].digest_size);
Sorry, I realized that this does not work if SHA1 or the default hash
algorithm are not among allocated PCR banks.
I just sent a patch to correctly determine the digest size:
https://marc.info/?l=linux-integrity&m=177202677128752&w=2
and applied yours on top of that (if it is fine for you):
https://github.com/linux-integrity/linux/commit/6efbd2b38b102ecbadc350228cc30fd67666a089
Thanks
Roberto
>
> /* 3th: template name */
> seq_printf(m, " %s", template_name);
> @@ -404,16 +398,24 @@ static int __init create_securityfs_measurement_lists(void)
> char file_name[NAME_MAX + 1];
> struct dentry *dentry;
>
> - sprintf(file_name, "ascii_runtime_measurements_%s",
> - hash_algo_name[algo]);
> + if (algo == HASH_ALGO__LAST)
> + sprintf(file_name, "ascii_runtime_measurements_tpm_alg_%x",
> + ima_tpm_chip->allocated_banks[i].alg_id);
> + else
> + sprintf(file_name, "ascii_runtime_measurements_%s",
> + hash_algo_name[algo]);
> dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP,
> ima_dir, (void *)(uintptr_t)i,
> &ima_ascii_measurements_ops);
> if (IS_ERR(dentry))
> return PTR_ERR(dentry);
>
> - sprintf(file_name, "binary_runtime_measurements_%s",
> - hash_algo_name[algo]);
> + if (algo == HASH_ALGO__LAST)
> + sprintf(file_name, "binary_runtime_measurements_tpm_alg_%x",
> + ima_tpm_chip->allocated_banks[i].alg_id);
> + else
> + sprintf(file_name, "binary_runtime_measurements_%s",
> + hash_algo_name[algo]);
> dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP,
> ima_dir, (void *)(uintptr_t)i,
> &ima_measurements_ops);
>
> ---
> base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f
> change-id: 20260127-ima-oob-9fa83a634d7b
>
> Best regards,
^ permalink raw reply
* [PATCH] ima: Add digest_size field to ima_algo_desc structure and use to show meas.
From: Roberto Sassu @ 2026-02-25 12:53 UTC (permalink / raw)
To: zohar, dmitry.kasatkin, eric.snowberg, paul, jmorris, serge
Cc: linux-integrity, linux-security-module, linux-kernel,
devnull+dima.arista.com, Roberto Sassu
From: Roberto Sassu <roberto.sassu@huawei.com>
Add the digest_size field to the ima_algo_desc structure to determine the
digest size from the correct source.
If the hash algorithm is among allocated PCR banks, take the value from the
TPM bank info; if the hash algorithm is SHA1, use the predefined value; if
the hash algorithm is the default one but not among the PCR banks, take the
digest size from the crypto subsystem (the default hash algorithm is
checked when parsing the ima_hash= command line option).
Finally, use the new information to correctly show the template digest in
ima_measurements_show() and ima_ascii_measurements_show().
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
security/integrity/ima/ima.h | 1 +
security/integrity/ima/ima_crypto.c | 6 ++++++
security/integrity/ima/ima_fs.c | 18 ++++++------------
3 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 89ebe98ffc5e..c38a9eb945b6 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -53,6 +53,7 @@ extern atomic_t ima_setxattr_allowed_hash_algorithms;
struct ima_algo_desc {
struct crypto_shash *tfm;
enum hash_algo algo;
+ unsigned int digest_size;
};
/* set during initialization */
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
index 8ae7821a65c2..c2a859710d20 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -109,6 +109,7 @@ static struct crypto_shash *ima_alloc_tfm(enum hash_algo algo)
int __init ima_init_crypto(void)
{
+ unsigned int digest_size;
enum hash_algo algo;
long rc;
int i;
@@ -147,7 +148,9 @@ int __init ima_init_crypto(void)
for (i = 0; i < NR_BANKS(ima_tpm_chip); i++) {
algo = ima_tpm_chip->allocated_banks[i].crypto_id;
+ digest_size = ima_tpm_chip->allocated_banks[i].digest_size;
ima_algo_array[i].algo = algo;
+ ima_algo_array[i].digest_size = digest_size;
/* unknown TPM algorithm */
if (algo == HASH_ALGO__LAST)
@@ -183,12 +186,15 @@ int __init ima_init_crypto(void)
}
ima_algo_array[ima_sha1_idx].algo = HASH_ALGO_SHA1;
+ ima_algo_array[ima_sha1_idx].digest_size = SHA1_DIGEST_SIZE;
}
if (ima_hash_algo_idx >= NR_BANKS(ima_tpm_chip) &&
ima_hash_algo_idx != ima_sha1_idx) {
+ digest_size = hash_digest_size[ima_hash_algo];
ima_algo_array[ima_hash_algo_idx].tfm = ima_shash_tfm;
ima_algo_array[ima_hash_algo_idx].algo = ima_hash_algo;
+ ima_algo_array[ima_hash_algo_idx].digest_size = digest_size;
}
return 0;
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 012a58959ff0..23d3a14b8ce3 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -132,16 +132,12 @@ int ima_measurements_show(struct seq_file *m, void *v)
char *template_name;
u32 pcr, namelen, template_data_len; /* temporary fields */
bool is_ima_template = false;
- enum hash_algo algo;
int i, algo_idx;
algo_idx = ima_sha1_idx;
- algo = HASH_ALGO_SHA1;
- if (m->file != NULL) {
+ if (m->file != NULL)
algo_idx = (unsigned long)file_inode(m->file)->i_private;
- algo = ima_algo_array[algo_idx].algo;
- }
/* get entry */
e = qe->entry;
@@ -160,7 +156,8 @@ int ima_measurements_show(struct seq_file *m, void *v)
ima_putc(m, &pcr, sizeof(e->pcr));
/* 2nd: template digest */
- ima_putc(m, e->digests[algo_idx].digest, hash_digest_size[algo]);
+ ima_putc(m, e->digests[algo_idx].digest,
+ ima_algo_array[algo_idx].digest_size);
/* 3rd: template name size */
namelen = !ima_canonical_fmt ? strlen(template_name) :
@@ -229,16 +226,12 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
struct ima_queue_entry *qe = v;
struct ima_template_entry *e;
char *template_name;
- enum hash_algo algo;
int i, algo_idx;
algo_idx = ima_sha1_idx;
- algo = HASH_ALGO_SHA1;
- if (m->file != NULL) {
+ if (m->file != NULL)
algo_idx = (unsigned long)file_inode(m->file)->i_private;
- algo = ima_algo_array[algo_idx].algo;
- }
/* get entry */
e = qe->entry;
@@ -252,7 +245,8 @@ static int ima_ascii_measurements_show(struct seq_file *m, void *v)
seq_printf(m, "%2d ", e->pcr);
/* 2nd: template hash */
- ima_print_digest(m, e->digests[algo_idx].digest, hash_digest_size[algo]);
+ ima_print_digest(m, e->digests[algo_idx].digest,
+ ima_algo_array[algo_idx].digest_size);
/* 3th: template name */
seq_printf(m, " %s", template_name);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v2 2/2] landlock: Clean up interrupted thread logic in TSYNC
From: Günther Noack @ 2026-02-25 12:09 UTC (permalink / raw)
To: Yihan Ding
Cc: Mickaël Salaün, Günther Noack, Paul Moore,
Jann Horn, linux-security-module, linux-kernel,
syzbot+7ea2f5e9dfd468201817
In-Reply-To: <20260225024734.3024732-3-dingyihan@uniontech.com>
On Wed, Feb 25, 2026 at 10:47:34AM +0800, Yihan Ding wrote:
> In landlock_restrict_sibling_threads(), when the calling thread is
> interrupted while waiting for sibling threads to prepare, it executes
> a recovery path.
>
> Previously, this path included a wait_for_completion() call on
> all_prepared to prevent a Use-After-Free of the local shared_ctx.
> However, this wait is redundant. Exiting the main do-while loop
> already leads to a bottom cleanup section that unconditionally waits
> for all_finished. Therefore, replacing the wait with a simple break
> is safe, prevents UAF, and correctly unblocks the remaining task_works.
>
> Clean up the error path by breaking the loop and updating the
> surrounding comments to accurately reflect the state machine.
>
> Suggested-by: Günther Noack <gnoack3000@gmail.com>
> Signed-off-by: Yihan Ding <dingyihan@uniontech.com>
> ---
> Changes in v2:
> - Replaced wait_for_completion(&shared_ctx.all_prepared) with a break
> statement based on the realization that the bottom wait for 'all_finished'
> already guards against UAF.
> - Updated comments for clarity.
> ---
> security/landlock/tsync.c | 18 +++++++++++-------
> 1 file changed, 11 insertions(+), 7 deletions(-)
>
> diff --git a/security/landlock/tsync.c b/security/landlock/tsync.c
> index 420fcfc2fe9a..9731ec7f329a 100644
> --- a/security/landlock/tsync.c
> +++ b/security/landlock/tsync.c
> @@ -534,24 +534,28 @@ int landlock_restrict_sibling_threads(const struct cred *old_cred,
> -ERESTARTNOINTR);
>
> /*
> - * Cancel task works for tasks that did not start running yet,
> - * and decrement all_prepared and num_unfinished accordingly.
> + * Opportunistic improvement: try to cancel task works
> + * for tasks that did not start running yet. We do not
> + * have a guarantee that it cancels any of the enqueued
> + * task works (because task_work_run() might already have
> + * dequeued them).
> */
> cancel_tsync_works(&works, &shared_ctx);
>
> /*
> - * The remaining task works have started running, so waiting for
> - * their completion will finish.
> + * Break the loop with error. The cleanup code after the loop
> + * unblocks the remaining task_works.
> */
> - wait_for_completion(&shared_ctx.all_prepared);
> + break;
> }
> }
> } while (found_more_threads &&
> !atomic_read(&shared_ctx.preparation_error));
>
> /*
> - * We now have all sibling threads blocking and in "prepared" state in the
> - * task work. Ask all threads to commit.
> + * We now have either (a) all sibling threads blocking and in
> + * "prepared" state in the task work, or (b) the preparation error is
> + * set. Ask all threads to commit (or abort).
> */
> complete_all(&shared_ctx.ready_to_commit);
>
> --
> 2.51.0
>
>
Thank you!
Reviewed-by: Günther Noack <gnoack@google.com>
^ permalink raw reply
* Re: [PATCH v2 1/2] landlock: Serialize TSYNC thread restriction
From: Günther Noack @ 2026-02-25 12:07 UTC (permalink / raw)
To: Yihan Ding
Cc: Mickaël Salaün, Günther Noack, Paul Moore,
Jann Horn, linux-security-module, linux-kernel,
syzbot+7ea2f5e9dfd468201817
In-Reply-To: <20260225024734.3024732-2-dingyihan@uniontech.com>
On Wed, Feb 25, 2026 at 10:47:33AM +0800, Yihan Ding wrote:
> syzbot found a deadlock in landlock_restrict_sibling_threads().
> When multiple threads concurrently call landlock_restrict_self() with
> sibling thread restriction enabled, they can deadlock by mutually
> queueing task_works on each other and then blocking in kernel space
> (waiting for the other to finish).
>
> Fix this by serializing the TSYNC operations within the same process
> using the exec_update_lock. This prevents concurrent invocations
> from deadlocking. We use down_write_killable() to ensure the thread
> remains responsive to fatal signals while waiting for the lock.
>
> Fixes: 42fc7e6543f6 ("landlock: Multithreading support for landlock_restrict_self()")
> Reported-by: syzbot+7ea2f5e9dfd468201817@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=7ea2f5e9dfd468201817
> Suggested-by: Günther Noack <gnoack3000@gmail.com>
> Signed-off-by: Yihan Ding <dingyihan@uniontech.com>
> ---
> security/landlock/tsync.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/security/landlock/tsync.c b/security/landlock/tsync.c
> index de01aa899751..420fcfc2fe9a 100644
> --- a/security/landlock/tsync.c
> +++ b/security/landlock/tsync.c
> @@ -447,6 +447,13 @@ int landlock_restrict_sibling_threads(const struct cred *old_cred,
> shared_ctx.new_cred = new_cred;
> shared_ctx.set_no_new_privs = task_no_new_privs(current);
>
> + /*
> + * Serialize concurrent TSYNC operations to prevent deadlocks
> + * when multiple threads call landlock_restrict_self() simultaneously.
> + */
> + if (down_write_killable(¤t->signal->exec_update_lock))
> + return -EINTR;
> +
> /*
> * We schedule a pseudo-signal task_work for each of the calling task's
> * sibling threads. In the task work, each thread:
> @@ -556,6 +563,7 @@ int landlock_restrict_sibling_threads(const struct cred *old_cred,
> wait_for_completion(&shared_ctx.all_finished);
>
> tsync_works_release(&works);
> + up_write(¤t->signal->exec_update_lock);
>
> return atomic_read(&shared_ctx.preparation_error);
> }
> --
> 2.51.0
>
Thank you!
Reviewed-by: Günther Noack <gnoack@google.com>
^ permalink raw reply
* [PATCH v2 2/2] landlock: Clean up interrupted thread logic in TSYNC
From: Yihan Ding @ 2026-02-25 2:47 UTC (permalink / raw)
To: Mickaël Salaün, Günther Noack
Cc: Paul Moore, Jann Horn, linux-security-module, linux-kernel,
syzbot+7ea2f5e9dfd468201817, Yihan Ding
In-Reply-To: <20260225024734.3024732-1-dingyihan@uniontech.com>
In landlock_restrict_sibling_threads(), when the calling thread is
interrupted while waiting for sibling threads to prepare, it executes
a recovery path.
Previously, this path included a wait_for_completion() call on
all_prepared to prevent a Use-After-Free of the local shared_ctx.
However, this wait is redundant. Exiting the main do-while loop
already leads to a bottom cleanup section that unconditionally waits
for all_finished. Therefore, replacing the wait with a simple break
is safe, prevents UAF, and correctly unblocks the remaining task_works.
Clean up the error path by breaking the loop and updating the
surrounding comments to accurately reflect the state machine.
Suggested-by: Günther Noack <gnoack3000@gmail.com>
Signed-off-by: Yihan Ding <dingyihan@uniontech.com>
---
Changes in v2:
- Replaced wait_for_completion(&shared_ctx.all_prepared) with a break
statement based on the realization that the bottom wait for 'all_finished'
already guards against UAF.
- Updated comments for clarity.
---
security/landlock/tsync.c | 18 +++++++++++-------
1 file changed, 11 insertions(+), 7 deletions(-)
diff --git a/security/landlock/tsync.c b/security/landlock/tsync.c
index 420fcfc2fe9a..9731ec7f329a 100644
--- a/security/landlock/tsync.c
+++ b/security/landlock/tsync.c
@@ -534,24 +534,28 @@ int landlock_restrict_sibling_threads(const struct cred *old_cred,
-ERESTARTNOINTR);
/*
- * Cancel task works for tasks that did not start running yet,
- * and decrement all_prepared and num_unfinished accordingly.
+ * Opportunistic improvement: try to cancel task works
+ * for tasks that did not start running yet. We do not
+ * have a guarantee that it cancels any of the enqueued
+ * task works (because task_work_run() might already have
+ * dequeued them).
*/
cancel_tsync_works(&works, &shared_ctx);
/*
- * The remaining task works have started running, so waiting for
- * their completion will finish.
+ * Break the loop with error. The cleanup code after the loop
+ * unblocks the remaining task_works.
*/
- wait_for_completion(&shared_ctx.all_prepared);
+ break;
}
}
} while (found_more_threads &&
!atomic_read(&shared_ctx.preparation_error));
/*
- * We now have all sibling threads blocking and in "prepared" state in the
- * task work. Ask all threads to commit.
+ * We now have either (a) all sibling threads blocking and in
+ * "prepared" state in the task work, or (b) the preparation error is
+ * set. Ask all threads to commit (or abort).
*/
complete_all(&shared_ctx.ready_to_commit);
--
2.51.0
^ permalink raw reply related
* [PATCH v2 1/2] landlock: Serialize TSYNC thread restriction
From: Yihan Ding @ 2026-02-25 2:47 UTC (permalink / raw)
To: Mickaël Salaün, Günther Noack
Cc: Paul Moore, Jann Horn, linux-security-module, linux-kernel,
syzbot+7ea2f5e9dfd468201817, Yihan Ding
In-Reply-To: <20260225024734.3024732-1-dingyihan@uniontech.com>
syzbot found a deadlock in landlock_restrict_sibling_threads().
When multiple threads concurrently call landlock_restrict_self() with
sibling thread restriction enabled, they can deadlock by mutually
queueing task_works on each other and then blocking in kernel space
(waiting for the other to finish).
Fix this by serializing the TSYNC operations within the same process
using the exec_update_lock. This prevents concurrent invocations
from deadlocking. We use down_write_killable() to ensure the thread
remains responsive to fatal signals while waiting for the lock.
Fixes: 42fc7e6543f6 ("landlock: Multithreading support for landlock_restrict_self()")
Reported-by: syzbot+7ea2f5e9dfd468201817@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=7ea2f5e9dfd468201817
Suggested-by: Günther Noack <gnoack3000@gmail.com>
Signed-off-by: Yihan Ding <dingyihan@uniontech.com>
---
security/landlock/tsync.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/security/landlock/tsync.c b/security/landlock/tsync.c
index de01aa899751..420fcfc2fe9a 100644
--- a/security/landlock/tsync.c
+++ b/security/landlock/tsync.c
@@ -447,6 +447,13 @@ int landlock_restrict_sibling_threads(const struct cred *old_cred,
shared_ctx.new_cred = new_cred;
shared_ctx.set_no_new_privs = task_no_new_privs(current);
+ /*
+ * Serialize concurrent TSYNC operations to prevent deadlocks
+ * when multiple threads call landlock_restrict_self() simultaneously.
+ */
+ if (down_write_killable(¤t->signal->exec_update_lock))
+ return -EINTR;
+
/*
* We schedule a pseudo-signal task_work for each of the calling task's
* sibling threads. In the task work, each thread:
@@ -556,6 +563,7 @@ int landlock_restrict_sibling_threads(const struct cred *old_cred,
wait_for_completion(&shared_ctx.all_finished);
tsync_works_release(&works);
+ up_write(¤t->signal->exec_update_lock);
return atomic_read(&shared_ctx.preparation_error);
}
--
2.51.0
^ permalink raw reply related
* [PATCH v2 0/2] landlock: Fix TSYNC deadlock and clean up error path
From: Yihan Ding @ 2026-02-25 2:47 UTC (permalink / raw)
To: Mickaël Salaün, Günther Noack
Cc: Paul Moore, Jann Horn, linux-security-module, linux-kernel,
syzbot+7ea2f5e9dfd468201817, Yihan Ding
Hello,
This patch series fixes a deadlock in the Landlock TSYNC multithreading
support, originally reported by syzbot, and cleans up the associated
interrupt recovery path.
The deadlock occurs when multiple threads concurrently call
landlock_restrict_self() with sibling thread restriction enabled,
causing them to mutually queue task_works on each other and block
indefinitely.
* Patch 1 fixes the root cause by serializing the TSYNC operations
within the same process using the exec_update_lock.
* Patch 2 cleans up the interrupt recovery path by replacing an
unnecessary wait_for_completion() with a straightforward loop break,
avoiding Use-After-Free while unblocking remaining task_works.
Changes in v2:
- Split the changes into a 2-patch series for clearer logical separation.
- Patch 1: Adopted down_write_killable() instead of down_write() to
ensure responsiveness to fatal signals (suggested by Günther Noack).
- Patch 2: Removed wait_for_completion(&shared_ctx.all_prepared) and
replaced it with a `break`. The function's bottom wait for
'all_finished' already provides the necessary UAF protection
(suggested by Günther Noack).
Link to v1: https://lore.kernel.org/all/20260224062729.2908692-1-dingyihan@uniontech.com/
Yihan Ding (2):
landlock: Serialize TSYNC thread restriction
landlock: Clean up interrupted thread logic in TSYNC
security/landlock/tsync.c | 26 +++++++++++++++++++-------
1 file changed, 19 insertions(+), 7 deletions(-)
--
2.51.0
^ permalink raw reply
* Re: [PATCH 1/3] integrity: Make arch_ima_get_secureboot integrity-wide
From: Mimi Zohar @ 2026-02-25 0:03 UTC (permalink / raw)
To: Ard Biesheuvel
Cc: Coiby Xu, Dave Hansen, linux-integrity, Heiko Carstens,
Roberto Sassu, Catalin Marinas, Will Deacon, Madhavan Srinivasan,
Michael Ellerman, Nicholas Piggin, Christophe Leroy (CS GROUP),
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, maintainer:X86 ARCHITECTURE (32-BIT AND 64-BIT),
H. Peter Anvin, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
Paul Moore, James Morris, Serge E. Hallyn, Jarkko Sakkinen,
moderated list:ARM64 PORT (AARCH64 ARCHITECTURE), open list,
open list:LINUX FOR POWERPC (32-BIT AND 64-BIT),
open list:S390 ARCHITECTURE,
open list:EXTENSIBLE FIRMWARE INTERFACE (EFI),
open list:SECURITY SUBSYSTEM, open list:KEYS/KEYRINGS_INTEGRITY
In-Reply-To: <CAMj1kXFBMSEdRL8FotASbQO3dcfNG0bpp9Vnm5JPn-yjyDr=GA@mail.gmail.com>
On Wed, 2026-01-21 at 17:25 +0100, Ard Biesheuvel wrote:
> On Wed, 21 Jan 2026 at 16:41, Mimi Zohar <zohar@linux.ibm.com> wrote:
> >
> > On Mon, 2026-01-19 at 12:04 +0800, Coiby Xu wrote:
> >
> > > diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
> > > index 976e75f9b9ba..5dce572192d6 100644
> > > --- a/security/integrity/ima/Kconfig
> > > +++ b/security/integrity/ima/Kconfig
> > > @@ -311,6 +311,7 @@ config IMA_QUEUE_EARLY_BOOT_KEYS
> > > config IMA_SECURE_AND_OR_TRUSTED_BOOT
> > > bool
> > > depends on IMA_ARCH_POLICY
> > > + depends on INTEGRITY_SECURE_BOOT
> > >
> > >
> > > Another idea is make a tree-wide arch_get_secureboot i.e. to move
> > > current arch_ima_get_secureboot code to arch-specific secure boot
> > > implementation. By this way, there will no need for a new Kconfig option
> > > INTEGRITY_SECURE_BOOT. But I'm not sure if there is any unforeseen
> > > concern.
> >
> > Originally basing IMA policy on the secure boot mode was an exception. As long
> > as making it public isn't an issue any longer, this sounds to me. Ard, Dave, do
> > you have any issues with replacing arch_ima_get_secureboot() with
> > arch_get_secureboot()?
>
> I don't see an issue with that. If there is a legitimate need to
> determine this even if IMA is not enabled, then this makes sense.
Ard, Dave -
FYI, Coiby posted v3 of this patch set[1], which is queued in the next-
integrity-testing branch[2].
[1]
https://lore.kernel.org/linux-integrity/20260213012851.2532722-1-coxu@redhat.com/
[2] https://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git/
Mimi
^ permalink raw reply
* [PATCH v3 15/15] VFS: unexport lock_rename(), lock_rename_child(), unlock_rename()
From: NeilBrown @ 2026-02-24 22:17 UTC (permalink / raw)
To: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, Paul Moore, James Morris, Serge E. Hallyn,
Stephen Smalley, Darrick J. Wong
Cc: linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
In-Reply-To: <20260224222542.3458677-1-neilb@ownmail.net>
From: NeilBrown <neil@brown.name>
These three function are now only used in namei.c, so they don't need to
be exported.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: NeilBrown <neil@brown.name>
---
Documentation/filesystems/porting.rst | 7 +++++++
fs/namei.c | 9 +++------
include/linux/namei.h | 3 ---
3 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/Documentation/filesystems/porting.rst b/Documentation/filesystems/porting.rst
index 1dd31ab417a2..d02aa57e4477 100644
--- a/Documentation/filesystems/porting.rst
+++ b/Documentation/filesystems/porting.rst
@@ -1368,3 +1368,10 @@ lifetime, consider using inode_set_cached_link() instead.
lookup_one_qstr_excl() is no longer exported - use start_creating() or
similar.
+---
+
+** mandatory**
+
+lock_rename(), lock_rename_child(), unlock_rename() are no
+longer available. Use start_renaming() or similar.
+
diff --git a/fs/namei.c b/fs/namei.c
index a5daa62399d7..77189335bbcc 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -3775,7 +3775,7 @@ static struct dentry *lock_two_directories(struct dentry *p1, struct dentry *p2)
/*
* p1 and p2 should be directories on the same fs.
*/
-struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
+static struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
{
if (p1 == p2) {
inode_lock_nested(p1->d_inode, I_MUTEX_PARENT);
@@ -3785,12 +3785,11 @@ struct dentry *lock_rename(struct dentry *p1, struct dentry *p2)
mutex_lock(&p1->d_sb->s_vfs_rename_mutex);
return lock_two_directories(p1, p2);
}
-EXPORT_SYMBOL(lock_rename);
/*
* c1 and p2 should be on the same fs.
*/
-struct dentry *lock_rename_child(struct dentry *c1, struct dentry *p2)
+static struct dentry *lock_rename_child(struct dentry *c1, struct dentry *p2)
{
if (READ_ONCE(c1->d_parent) == p2) {
/*
@@ -3827,9 +3826,8 @@ struct dentry *lock_rename_child(struct dentry *c1, struct dentry *p2)
mutex_unlock(&c1->d_sb->s_vfs_rename_mutex);
return NULL;
}
-EXPORT_SYMBOL(lock_rename_child);
-void unlock_rename(struct dentry *p1, struct dentry *p2)
+static void unlock_rename(struct dentry *p1, struct dentry *p2)
{
inode_unlock(p1->d_inode);
if (p1 != p2) {
@@ -3837,7 +3835,6 @@ void unlock_rename(struct dentry *p1, struct dentry *p2)
mutex_unlock(&p1->d_sb->s_vfs_rename_mutex);
}
}
-EXPORT_SYMBOL(unlock_rename);
/**
* __start_renaming - lookup and lock names for rename
diff --git a/include/linux/namei.h b/include/linux/namei.h
index c7a7288cdd25..2ad6dd9987b9 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -165,9 +165,6 @@ extern int follow_down_one(struct path *);
extern int follow_down(struct path *path, unsigned int flags);
extern int follow_up(struct path *);
-extern struct dentry *lock_rename(struct dentry *, struct dentry *);
-extern struct dentry *lock_rename_child(struct dentry *, struct dentry *);
-extern void unlock_rename(struct dentry *, struct dentry *);
int start_renaming(struct renamedata *rd, int lookup_flags,
struct qstr *old_last, struct qstr *new_last);
int start_renaming_dentry(struct renamedata *rd, int lookup_flags,
--
2.50.0.107.gf914562f5916.dirty
^ permalink raw reply related
* [PATCH v3 14/15] ovl: remove ovl_lock_rename_workdir()
From: NeilBrown @ 2026-02-24 22:16 UTC (permalink / raw)
To: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, Paul Moore, James Morris, Serge E. Hallyn,
Stephen Smalley, Darrick J. Wong
Cc: linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
In-Reply-To: <20260224222542.3458677-1-neilb@ownmail.net>
From: NeilBrown <neil@brown.name>
This function is unused since
Commit 833d2b3a072f ("Add start_renaming_two_dentries()")
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: NeilBrown <neil@brown.name>
---
fs/overlayfs/overlayfs.h | 2 --
fs/overlayfs/util.c | 25 -------------------------
2 files changed, 27 deletions(-)
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index 714a1cec3709..6fb99c583c31 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -569,8 +569,6 @@ bool ovl_is_inuse(struct dentry *dentry);
bool ovl_need_index(struct dentry *dentry);
int ovl_nlink_start(struct dentry *dentry);
void ovl_nlink_end(struct dentry *dentry);
-int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *work,
- struct dentry *upperdir, struct dentry *upper);
int ovl_check_metacopy_xattr(struct ovl_fs *ofs, const struct path *path,
struct ovl_metacopy *data);
int ovl_set_metacopy_xattr(struct ovl_fs *ofs, struct dentry *d,
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index 3f1b763a8bb4..aa2a32793c2f 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -1213,31 +1213,6 @@ void ovl_nlink_end(struct dentry *dentry)
ovl_inode_unlock(inode);
}
-int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *work,
- struct dentry *upperdir, struct dentry *upper)
-{
- struct dentry *trap;
-
- /* Workdir should not be subdir of upperdir and vice versa */
- trap = lock_rename(workdir, upperdir);
- if (IS_ERR(trap))
- goto err;
- if (trap)
- goto err_unlock;
- if (work && (work->d_parent != workdir || d_unhashed(work)))
- goto err_unlock;
- if (upper && (upper->d_parent != upperdir || d_unhashed(upper)))
- goto err_unlock;
-
- return 0;
-
-err_unlock:
- unlock_rename(workdir, upperdir);
-err:
- pr_err("failed to lock workdir+upperdir\n");
- return -EIO;
-}
-
/*
* err < 0, 0 if no metacopy xattr, metacopy data size if xattr found.
* an empty xattr returns OVL_METACOPY_MIN_SIZE to distinguish from no xattr value.
--
2.50.0.107.gf914562f5916.dirty
^ permalink raw reply related
* [PATCH v3 13/15] ovl: use is_subdir() for testing if one thing is a subdir of another
From: NeilBrown @ 2026-02-24 22:16 UTC (permalink / raw)
To: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, Paul Moore, James Morris, Serge E. Hallyn,
Stephen Smalley, Darrick J. Wong
Cc: linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
In-Reply-To: <20260224222542.3458677-1-neilb@ownmail.net>
From: NeilBrown <neil@brown.name>
Rather than using lock_rename(), use the more obvious is_subdir() for
ensuring that neither upper nor workdir contain the other.
Also be explicit in the comment that the two directories cannot be the
same.
As this is a point-it-time sanity check and does not provide any
on-going guarantees, the removal of locking does not introduce any
interesting races.
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: NeilBrown <neil@brown.name>
---
fs/overlayfs/super.c | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 109643930b9f..58adefb1c5b8 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -451,18 +451,13 @@ static int ovl_lower_dir(const char *name, const struct path *path,
return 0;
}
-/* Workdir should not be subdir of upperdir and vice versa */
+/*
+ * Workdir should not be subdir of upperdir and vice versa, and
+ * they should not be the same.
+ */
static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir)
{
- bool ok = false;
-
- if (workdir != upperdir) {
- struct dentry *trap = lock_rename(workdir, upperdir);
- if (!IS_ERR(trap))
- unlock_rename(workdir, upperdir);
- ok = (trap == NULL);
- }
- return ok;
+ return !is_subdir(workdir, upperdir) && !is_subdir(upperdir, workdir);
}
static int ovl_setup_trap(struct super_block *sb, struct dentry *dir,
--
2.50.0.107.gf914562f5916.dirty
^ permalink raw reply related
* [PATCH v3 12/15] ovl: change ovl_create_real() to get a new lock when re-opening created file.
From: NeilBrown @ 2026-02-24 22:16 UTC (permalink / raw)
To: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, Paul Moore, James Morris, Serge E. Hallyn,
Stephen Smalley, Darrick J. Wong
Cc: linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
In-Reply-To: <20260224222542.3458677-1-neilb@ownmail.net>
From: NeilBrown <neil@brown.name>
When ovl_create_real() is used to create a file on the upper filesystem
it needs to return the resulting dentry - positive and hashed.
It is usually the case the that dentry passed to the create function
(e.g. vfs_create()) will be suitable but this is not guaranteed. The
filesystem may unhash that dentry forcing a repeat lookup next time the
name is wanted.
So ovl_create_real() must be (and is) aware of this and prepared to
perform that lookup to get a hash positive dentry.
This is currently done under that same directory lock that provided
exclusion for the create. Proposed changes to locking will make this
not possible - as the name, rather than the directory, will be locked.
The new APIs provided for lookup and locking do not and cannot support
this pattern.
The lock isn't needed. ovl_create_real() can drop the lock and then get
a new lock for the lookup - then check that the lookup returned the
correct inode. In a well-behaved configuration where the upper
filesystem is not being modified by a third party, this will always work
reliably, and if there are separate modification it will fail cleanly.
So change ovl_create_real() to drop the lock and call
ovl_start_creating_upper() to find the correct dentry. Note that
start_creating doesn't fail if the name already exists.
The lookup previously used the name from newdentry which was guaranteed
to be stable because the parent directory was locked. As we now drop
the lock we lose that guarantee. As newdentry is unhashed it is
unlikely for the name to change, but safest not to depend on that. So
the expected name is now passed in to ovl_create_real() and that is
used.
This removes the only remaining use of ovl_lookup_upper, so it is
removed.
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: NeilBrown <neil@brown.name>
---
fs/overlayfs/dir.c | 37 +++++++++++++++++++++++++------------
fs/overlayfs/overlayfs.h | 8 +-------
fs/overlayfs/super.c | 1 +
3 files changed, 27 insertions(+), 19 deletions(-)
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index c4feb89ad1e3..8c0a3d876fef 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -159,7 +159,8 @@ int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct dentry *dir,
}
struct dentry *ovl_create_real(struct ovl_fs *ofs, struct dentry *parent,
- struct dentry *newdentry, struct ovl_cattr *attr)
+ struct dentry *newdentry, struct qstr *qname,
+ struct ovl_cattr *attr)
{
struct inode *dir = parent->d_inode;
int err;
@@ -221,19 +222,30 @@ struct dentry *ovl_create_real(struct ovl_fs *ofs, struct dentry *parent,
struct dentry *d;
/*
* Some filesystems (i.e. casefolded) may return an unhashed
- * negative dentry from the ovl_lookup_upper() call before
+ * negative dentry from the ovl_start_creating_upper() call before
* ovl_create_real().
* In that case, lookup again after making the newdentry
* positive, so ovl_create_upper() always returns a hashed
- * positive dentry.
+ * positive dentry. We lookup using qname which should be
+ * the same name as newentry, but is certain not to change.
+ * As we have to drop the lock before the lookup a race
+ * could result in a lookup failure. In that case we return
+ * an error.
*/
- d = ovl_lookup_upper(ofs, newdentry->d_name.name, parent,
- newdentry->d_name.len);
- dput(newdentry);
- if (IS_ERR_OR_NULL(d))
+ end_creating_keep(newdentry);
+ d = ovl_start_creating_upper(ofs, parent, qname);
+
+ if (IS_ERR_OR_NULL(d)) {
err = d ? PTR_ERR(d) : -ENOENT;
- else
+ } else if (d->d_inode != newdentry->d_inode) {
+ err = -EIO;
+ } else {
+ dput(newdentry);
return d;
+ }
+ end_creating(d);
+ dput(newdentry);
+ return ERR_PTR(err);
}
out:
if (err) {
@@ -252,7 +264,7 @@ struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir,
ret = ovl_start_creating_temp(ofs, workdir, name);
if (IS_ERR(ret))
return ret;
- ret = ovl_create_real(ofs, workdir, ret, attr);
+ ret = ovl_create_real(ofs, workdir, ret, &QSTR(name), attr);
return end_creating_keep(ret);
}
@@ -352,14 +364,15 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
struct dentry *newdentry;
+ struct qstr qname = QSTR_LEN(dentry->d_name.name,
+ dentry->d_name.len);
int err;
newdentry = ovl_start_creating_upper(ofs, upperdir,
- &QSTR_LEN(dentry->d_name.name,
- dentry->d_name.len));
+ &qname);
if (IS_ERR(newdentry))
return PTR_ERR(newdentry);
- newdentry = ovl_create_real(ofs, upperdir, newdentry, attr);
+ newdentry = ovl_create_real(ofs, upperdir, newdentry, &qname, attr);
if (IS_ERR(newdentry))
return PTR_ERR(newdentry);
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index cad2055ebf18..714a1cec3709 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -406,13 +406,6 @@ static inline struct file *ovl_do_tmpfile(struct ovl_fs *ofs,
return file;
}
-static inline struct dentry *ovl_lookup_upper(struct ovl_fs *ofs,
- const char *name,
- struct dentry *base, int len)
-{
- return lookup_one(ovl_upper_mnt_idmap(ofs), &QSTR_LEN(name, len), base);
-}
-
static inline struct dentry *ovl_lookup_upper_unlocked(struct ovl_fs *ofs,
const char *name,
struct dentry *base,
@@ -888,6 +881,7 @@ struct ovl_cattr {
struct dentry *ovl_create_real(struct ovl_fs *ofs,
struct dentry *parent, struct dentry *newdentry,
+ struct qstr *qname,
struct ovl_cattr *attr);
int ovl_cleanup(struct ovl_fs *ofs, struct dentry *workdir, struct dentry *dentry);
#define OVL_TEMPNAME_SIZE 20
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index d4c12feec039..109643930b9f 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -634,6 +634,7 @@ static struct dentry *ovl_lookup_or_create(struct ovl_fs *ofs,
if (!IS_ERR(child)) {
if (!child->d_inode)
child = ovl_create_real(ofs, parent, child,
+ &QSTR(name),
OVL_CATTR(mode));
end_creating_keep(child);
}
--
2.50.0.107.gf914562f5916.dirty
^ permalink raw reply related
* [PATCH v3 11/15] ovl: pass name buffer to ovl_start_creating_temp()
From: NeilBrown @ 2026-02-24 22:16 UTC (permalink / raw)
To: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, Paul Moore, James Morris, Serge E. Hallyn,
Stephen Smalley, Darrick J. Wong
Cc: linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
In-Reply-To: <20260224222542.3458677-1-neilb@ownmail.net>
From: NeilBrown <neil@brown.name>
Now ovl_start_creating_temp() is passed a buffer in which to store the
temp name. This will be useful in a future patch were ovl_create_real()
will need access to that name.
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: NeilBrown <neil@brown.name>
---
fs/overlayfs/dir.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index ff3dbd1ca61f..c4feb89ad1e3 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -66,10 +66,9 @@ void ovl_tempname(char name[OVL_TEMPNAME_SIZE])
}
static struct dentry *ovl_start_creating_temp(struct ovl_fs *ofs,
- struct dentry *workdir)
+ struct dentry *workdir,
+ char name[OVL_TEMPNAME_SIZE])
{
- char name[OVL_TEMPNAME_SIZE];
-
ovl_tempname(name);
return start_creating(ovl_upper_mnt_idmap(ofs), workdir,
&QSTR(name));
@@ -81,11 +80,12 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs)
struct dentry *whiteout, *link;
struct dentry *workdir = ofs->workdir;
struct inode *wdir = workdir->d_inode;
+ char name[OVL_TEMPNAME_SIZE];
guard(mutex)(&ofs->whiteout_lock);
if (!ofs->whiteout) {
- whiteout = ovl_start_creating_temp(ofs, workdir);
+ whiteout = ovl_start_creating_temp(ofs, workdir, name);
if (IS_ERR(whiteout))
return whiteout;
err = ovl_do_whiteout(ofs, wdir, whiteout);
@@ -97,7 +97,7 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs)
}
if (!ofs->no_shared_whiteout) {
- link = ovl_start_creating_temp(ofs, workdir);
+ link = ovl_start_creating_temp(ofs, workdir, name);
if (IS_ERR(link))
return link;
err = ovl_do_link(ofs, ofs->whiteout, wdir, link);
@@ -247,7 +247,9 @@ struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir,
struct ovl_cattr *attr)
{
struct dentry *ret;
- ret = ovl_start_creating_temp(ofs, workdir);
+ char name[OVL_TEMPNAME_SIZE];
+
+ ret = ovl_start_creating_temp(ofs, workdir, name);
if (IS_ERR(ret))
return ret;
ret = ovl_create_real(ofs, workdir, ret, attr);
--
2.50.0.107.gf914562f5916.dirty
^ permalink raw reply related
* [PATCH v3 10/15] cachefiles: change cachefiles_bury_object to use start_renaming_dentry()
From: NeilBrown @ 2026-02-24 22:16 UTC (permalink / raw)
To: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, Paul Moore, James Morris, Serge E. Hallyn,
Stephen Smalley, Darrick J. Wong
Cc: linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
In-Reply-To: <20260224222542.3458677-1-neilb@ownmail.net>
From: NeilBrown <neil@brown.name>
Rather then using lock_rename() and lookup_one() etc we can use
the new start_renaming_dentry(). This is part of centralising dir
locking and lookup so that locking rules can be changed.
Some error check are removed as not necessary. Checks for rep being a
non-dir or IS_DEADDIR and the check that ->graveyard is still a
directory only provide slightly more informative errors and have been
dropped.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: NeilBrown <neil@brown.name>
---
fs/cachefiles/namei.c | 76 ++++++++-----------------------------------
1 file changed, 14 insertions(+), 62 deletions(-)
diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c
index e5ec90dccc27..3af42ec78411 100644
--- a/fs/cachefiles/namei.c
+++ b/fs/cachefiles/namei.c
@@ -270,7 +270,8 @@ int cachefiles_bury_object(struct cachefiles_cache *cache,
struct dentry *rep,
enum fscache_why_object_killed why)
{
- struct dentry *grave, *trap;
+ struct dentry *grave;
+ struct renamedata rd = {};
struct path path, path_to_graveyard;
char nbuffer[8 + 8 + 1];
int ret;
@@ -302,77 +303,36 @@ int cachefiles_bury_object(struct cachefiles_cache *cache,
(uint32_t) ktime_get_real_seconds(),
(uint32_t) atomic_inc_return(&cache->gravecounter));
- /* do the multiway lock magic */
- trap = lock_rename(cache->graveyard, dir);
- if (IS_ERR(trap))
- return PTR_ERR(trap);
-
- /* do some checks before getting the grave dentry */
- if (rep->d_parent != dir || IS_DEADDIR(d_inode(rep))) {
- /* the entry was probably culled when we dropped the parent dir
- * lock */
- unlock_rename(cache->graveyard, dir);
- _leave(" = 0 [culled?]");
- return 0;
- }
-
- if (!d_can_lookup(cache->graveyard)) {
- unlock_rename(cache->graveyard, dir);
- cachefiles_io_error(cache, "Graveyard no longer a directory");
- return -EIO;
- }
-
- if (trap == rep) {
- unlock_rename(cache->graveyard, dir);
- cachefiles_io_error(cache, "May not make directory loop");
+ rd.mnt_idmap = &nop_mnt_idmap;
+ rd.old_parent = dir;
+ rd.new_parent = cache->graveyard;
+ rd.flags = 0;
+ ret = start_renaming_dentry(&rd, 0, rep, &QSTR(nbuffer));
+ if (ret) {
+ cachefiles_io_error(cache, "Cannot lock/lookup in graveyard");
return -EIO;
}
if (d_mountpoint(rep)) {
- unlock_rename(cache->graveyard, dir);
+ end_renaming(&rd);
cachefiles_io_error(cache, "Mountpoint in cache");
return -EIO;
}
- grave = lookup_one(&nop_mnt_idmap, &QSTR(nbuffer), cache->graveyard);
- if (IS_ERR(grave)) {
- unlock_rename(cache->graveyard, dir);
- trace_cachefiles_vfs_error(object, d_inode(cache->graveyard),
- PTR_ERR(grave),
- cachefiles_trace_lookup_error);
-
- if (PTR_ERR(grave) == -ENOMEM) {
- _leave(" = -ENOMEM");
- return -ENOMEM;
- }
-
- cachefiles_io_error(cache, "Lookup error %ld", PTR_ERR(grave));
- return -EIO;
- }
-
+ grave = rd.new_dentry;
if (d_is_positive(grave)) {
- unlock_rename(cache->graveyard, dir);
- dput(grave);
+ end_renaming(&rd);
grave = NULL;
cond_resched();
goto try_again;
}
if (d_mountpoint(grave)) {
- unlock_rename(cache->graveyard, dir);
- dput(grave);
+ end_renaming(&rd);
cachefiles_io_error(cache, "Mountpoint in graveyard");
return -EIO;
}
- /* target should not be an ancestor of source */
- if (trap == grave) {
- unlock_rename(cache->graveyard, dir);
- dput(grave);
- cachefiles_io_error(cache, "May not make directory loop");
- return -EIO;
- }
-
/* attempt the rename */
path.mnt = cache->mnt;
path.dentry = dir;
@@ -382,13 +342,6 @@ int cachefiles_bury_object(struct cachefiles_cache *cache,
if (ret < 0) {
cachefiles_io_error(cache, "Rename security error %d", ret);
} else {
- struct renamedata rd = {
- .mnt_idmap = &nop_mnt_idmap,
- .old_parent = dir,
- .old_dentry = rep,
- .new_parent = cache->graveyard,
- .new_dentry = grave,
- };
trace_cachefiles_rename(object, d_inode(rep)->i_ino, why);
ret = cachefiles_inject_read_error();
if (ret == 0)
@@ -402,8 +355,7 @@ int cachefiles_bury_object(struct cachefiles_cache *cache,
}
__cachefiles_unmark_inode_in_use(object, d_inode(rep));
- unlock_rename(cache->graveyard, dir);
- dput(grave);
+ end_renaming(&rd);
_leave(" = 0");
return 0;
}
--
2.50.0.107.gf914562f5916.dirty
^ permalink raw reply related
* [PATCH v3 09/15] ovl: Simplify ovl_lookup_real_one()
From: NeilBrown @ 2026-02-24 22:16 UTC (permalink / raw)
To: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, Paul Moore, James Morris, Serge E. Hallyn,
Stephen Smalley, Darrick J. Wong
Cc: linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
In-Reply-To: <20260224222542.3458677-1-neilb@ownmail.net>
From: NeilBrown <neil@brown.name>
The primary purpose of this patch is to remove the locking from
ovl_lookup_real_one() as part of centralising all locking of directories
for name operations.
The locking here isn't needed. By performing consistency tests after
the lookup we can be sure that the result of the lookup was valid at
least for a moment, which is all the original code promised.
lookup_noperm_unlocked() is used for the lookup and it will take the
lock if needed only where it is needed.
Also:
- don't take a reference to real->d_parent. The parent is
only use for a pointer comparison, and no reference is needed for
that.
- Several "if" statements have a "goto" followed by "else" - the
else isn't needed: the following statement can directly follow
the "if" as a new statement
- Use a consistent pattern of setting "err" before performing a test
and possibly going to "fail".
- remove the "out" label (now that we don't need to dput(parent) or
unlock) and simply return from fail:.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: NeilBrown <neil@brown.name>
---
fs/overlayfs/export.c | 71 ++++++++++++++++++++-----------------------
1 file changed, 33 insertions(+), 38 deletions(-)
diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c
index 83f80fdb1567..896f2e9af2e2 100644
--- a/fs/overlayfs/export.c
+++ b/fs/overlayfs/export.c
@@ -349,69 +349,64 @@ static struct dentry *ovl_dentry_real_at(struct dentry *dentry, int idx)
return NULL;
}
-/*
- * Lookup a child overlay dentry to get a connected overlay dentry whose real
- * dentry is @real. If @real is on upper layer, we lookup a child overlay
- * dentry with the same name as the real dentry. Otherwise, we need to consult
- * index for lookup.
+/**
+ * ovl_lookup_real_one - Lookup a child overlay dentry to get an overlay dentry whose real dentry is given
+ * @connected: parent overlay dentry
+ * @real: given child real dentry
+ * @layer: layer in which @real exists
+ *
+ *
+ * Lookup a child overlay dentry in @connected with the same name as the @real
+ * dentry. Then check that the parent of the result is the real dentry for
+ * @connected, and @real is the real dentry for the result.
+ *
+ * Returns:
+ * %-ECHILD if the parent of @real is no longer the real dentry for @connected.
+ * %-ESTALE if @real is not the real dentry of the found dentry.
+ * Otherwise the found dentry is returned.
*/
static struct dentry *ovl_lookup_real_one(struct dentry *connected,
struct dentry *real,
const struct ovl_layer *layer)
{
- struct inode *dir = d_inode(connected);
- struct dentry *this, *parent = NULL;
+ struct dentry *this;
struct name_snapshot name;
int err;
/*
- * Lookup child overlay dentry by real name. The dir mutex protects us
- * from racing with overlay rename. If the overlay dentry that is above
- * real has already been moved to a parent that is not under the
- * connected overlay dir, we return -ECHILD and restart the lookup of
- * connected real path from the top.
- */
- inode_lock_nested(dir, I_MUTEX_PARENT);
- err = -ECHILD;
- parent = dget_parent(real);
- if (ovl_dentry_real_at(connected, layer->idx) != parent)
- goto fail;
-
- /*
- * We also need to take a snapshot of real dentry name to protect us
+ * We need to take a snapshot of real dentry name to protect us
* from racing with underlying layer rename. In this case, we don't
* care about returning ESTALE, only from dereferencing a free name
* pointer because we hold no lock on the real dentry.
*/
take_dentry_name_snapshot(&name, real);
- /*
- * No idmap handling here: it's an internal lookup.
- */
- this = lookup_noperm(&name.name, connected);
+ this = lookup_noperm_unlocked(&name.name, connected);
release_dentry_name_snapshot(&name);
+
+ err = -ECHILD;
+ if (ovl_dentry_real_at(connected, layer->idx) != real->d_parent)
+ goto fail;
+
err = PTR_ERR(this);
- if (IS_ERR(this)) {
+ if (IS_ERR(this))
goto fail;
- } else if (!this || !this->d_inode) {
- dput(this);
- err = -ENOENT;
+
+ err = -ENOENT;
+ if (!this || !this->d_inode)
goto fail;
- } else if (ovl_dentry_real_at(this, layer->idx) != real) {
- dput(this);
- err = -ESTALE;
+
+ err = -ESTALE;
+ if (ovl_dentry_real_at(this, layer->idx) != real)
goto fail;
- }
-out:
- dput(parent);
- inode_unlock(dir);
return this;
fail:
pr_warn_ratelimited("failed to lookup one by real (%pd2, layer=%d, connected=%pd2, err=%i)\n",
real, layer->idx, connected, err);
- this = ERR_PTR(err);
- goto out;
+ if (!IS_ERR(this))
+ dput(this);
+ return ERR_PTR(err);
}
static struct dentry *ovl_lookup_real(struct super_block *sb,
--
2.50.0.107.gf914562f5916.dirty
^ permalink raw reply related
* [PATCH v3 08/15] VFS: make lookup_one_qstr_excl() static.
From: NeilBrown @ 2026-02-24 22:16 UTC (permalink / raw)
To: Christian Brauner, Alexander Viro, David Howells, Jan Kara,
Chuck Lever, Jeff Layton, Miklos Szeredi, Amir Goldstein,
John Johansen, Paul Moore, James Morris, Serge E. Hallyn,
Stephen Smalley, Darrick J. Wong
Cc: linux-kernel, netfs, linux-fsdevel, linux-nfs, linux-unionfs,
apparmor, linux-security-module, selinux
In-Reply-To: <20260224222542.3458677-1-neilb@ownmail.net>
From: NeilBrown <neil@brown.name>
lookup_one_qstr_excl() is no longer used outside of namei.c, so
make it static.
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: NeilBrown <neil@brown.name>
---
Documentation/filesystems/porting.rst | 7 +++++++
fs/namei.c | 5 ++---
include/linux/namei.h | 3 ---
3 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/Documentation/filesystems/porting.rst b/Documentation/filesystems/porting.rst
index 52ff1d19405b..1dd31ab417a2 100644
--- a/Documentation/filesystems/porting.rst
+++ b/Documentation/filesystems/porting.rst
@@ -1361,3 +1361,10 @@ to match what strlen() would return if it was ran on the string.
However, if the string is freely accessible for the duration of inode's
lifetime, consider using inode_set_cached_link() instead.
+
+---
+
+**mandatory**
+
+lookup_one_qstr_excl() is no longer exported - use start_creating() or
+similar.
diff --git a/fs/namei.c b/fs/namei.c
index 11c9a4a6c396..a5daa62399d7 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1782,8 +1782,8 @@ static struct dentry *lookup_dcache(const struct qstr *name,
* Will return -ENOENT if name isn't found and LOOKUP_CREATE wasn't passed.
* Will return -EEXIST if name is found and LOOKUP_EXCL was passed.
*/
-struct dentry *lookup_one_qstr_excl(const struct qstr *name,
- struct dentry *base, unsigned int flags)
+static struct dentry *lookup_one_qstr_excl(const struct qstr *name,
+ struct dentry *base, unsigned int flags)
{
struct dentry *dentry;
struct dentry *old;
@@ -1820,7 +1820,6 @@ struct dentry *lookup_one_qstr_excl(const struct qstr *name,
}
return dentry;
}
-EXPORT_SYMBOL(lookup_one_qstr_excl);
/**
* lookup_fast - do fast lockless (but racy) lookup of a dentry
diff --git a/include/linux/namei.h b/include/linux/namei.h
index 58600cf234bc..c7a7288cdd25 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -54,9 +54,6 @@ extern int path_pts(struct path *path);
extern int user_path_at(int, const char __user *, unsigned, struct path *);
-struct dentry *lookup_one_qstr_excl(const struct qstr *name,
- struct dentry *base,
- unsigned int flags);
extern int kern_path(const char *, unsigned, struct path *);
struct dentry *kern_path_parent(const char *name, struct path *parent);
--
2.50.0.107.gf914562f5916.dirty
^ permalink raw reply related
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