selinux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 7/7] net, lsm: Change skb secmarks to x-array indexes
  2026-08-13 20:48 [PATCH 0/7] " Casey Schaufler
@ 2026-08-13 20:48 ` Casey Schaufler
  2026-08-14  2:39   ` sashiko-bot
  0 siblings, 1 reply; 17+ messages in thread
From: Casey Schaufler @ 2026-08-13 20:48 UTC (permalink / raw)
  To: casey, paul, linux-security-module, pablo, fw, phil
  Cc: linux-kernel, netfilter-devel, coreteam, jmorris, serge, keescook,
	john.johansen, penguin-kernel, stephen.smalley.work, selinux

Maintain a xarray of lsm_prop structures which represent the
LSM security information passed via skb->secmark. Pass the xarray
index of the appropriate lsm_prop (the secxa) instead of an LSM
specific secid. Allow multiple LSMs to specify their components
in xarray entries, or create new entries as necessary.

Change uses of security_secctx_to_secid() to security_secctx_to_lsmprop()
in the netfilter and iptables code. Change security_secmark_relabel_packet()
to accept an lsm_prop pointer rather than a secid. Change secxa_set_secmark()
to update and create new entries as necessary.

Update the SELinux, Smack and AppArmor hooks that use secmarks to
expect a secxa xarray index instead of a secid.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
 include/linux/lsm_hook_defs.h    |  2 +-
 include/linux/security.h         |  4 +-
 net/netfilter/nfnetlink_queue.c  | 12 +++++-
 net/netfilter/nft_meta.c         | 11 +++--
 net/netfilter/xt_SECMARK.c       | 12 ++++--
 security/apparmor/net.c          |  8 +++-
 security/lsm_secxa.c             | 18 ++++++--
 security/security.c              |  6 +--
 security/selinux/hooks.c         | 71 +++++++++++++++++++++++++++-----
 security/smack/smack_lsm.c       | 10 ++++-
 security/smack/smack_netfilter.c |  8 ++--
 11 files changed, 127 insertions(+), 35 deletions(-)

diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 3666d821b8a1..7ba4547daded 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -371,7 +371,7 @@ LSM_HOOK(void, LSM_RET_VOID, inet_csk_clone, struct sock *newsk,
 	 const struct request_sock *req)
 LSM_HOOK(void, LSM_RET_VOID, inet_conn_established, struct sock *sk,
 	 struct sk_buff *skb)
-LSM_HOOK(int, 0, secmark_relabel_packet, u32 secid)
+LSM_HOOK(int, 0, secmark_relabel_packet, struct lsm_prop *prop)
 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,
diff --git a/include/linux/security.h b/include/linux/security.h
index b209d681e79a..844fbef4a6a8 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1716,7 +1716,7 @@ void security_inet_csk_clone(struct sock *newsk,
 			const struct request_sock *req);
 void security_inet_conn_established(struct sock *sk,
 			struct sk_buff *skb);
-int security_secmark_relabel_packet(u32 secid);
+int security_secmark_relabel_packet(struct lsm_prop *prop);
 void security_secmark_refcount_inc(void);
 void security_secmark_refcount_dec(void);
 int security_tun_dev_alloc_security(void **security);
@@ -1899,7 +1899,7 @@ static inline void security_inet_conn_established(struct sock *sk,
 {
 }
 
-static inline int security_secmark_relabel_packet(u32 secid)
+static inline int security_secmark_relabel_packet(struct lsm_prop *prop)
 {
 	return 0;
 }
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index b8aaf39cb4d8..ebab037edc6b 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -32,6 +32,7 @@
 #include <linux/cgroup-defs.h>
 #include <linux/rhashtable.h>
 #include <linux/jhash.h>
+#include <linux/lsm_secxa.h>
 #include <net/gso.h>
 #include <net/sock.h>
 #include <net/tcp_states.h>
@@ -606,8 +607,15 @@ static int nfqnl_get_sk_secctx(struct sk_buff *skb, struct lsm_context *ctx)
 {
 	int seclen = 0;
 #if IS_ENABLED(CONFIG_NETWORK_SECMARK)
-	if (skb->secmark)
-		seclen = security_secid_to_secctx(skb->secmark, ctx);
+	struct lsm_prop *prop;
+	int rc;
+
+	if (skb->secmark) {
+		rc = secxa_get_lsmprop(&prop, skb->secmark);
+		if (rc)
+			return 0;
+		seclen = security_lsmprop_to_secctx(prop, ctx, LSM_ID_UNDEF);
+	}
 #endif
 	return seclen;
 }
diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c
index bd0f7a0931f4..24535c45dab3 100644
--- a/net/netfilter/nft_meta.c
+++ b/net/netfilter/nft_meta.c
@@ -927,17 +927,20 @@ static const struct nla_policy nft_secmark_policy[NFTA_SECMARK_MAX + 1] = {
 
 static int nft_secmark_compute_secid(struct nft_secmark *priv)
 {
+	struct lsm_prop tmp_prop;
 	u32 tmp_secid = 0;
 	int err;
 
-	err = security_secctx_to_secid(priv->ctx, strlen(priv->ctx), &tmp_secid);
+	err = security_secctx_to_lsmprop(priv->ctx, strlen(priv->ctx),
+					 &tmp_prop, LSM_ID_UNDEF);
 	if (err)
 		return err;
 
-	if (!tmp_secid)
-		return -ENOENT;
+	tmp_secid = secxa_from_lsmprop(&tmp_prop);
+	if (tmp_secid < 0)
+		return tmp_secid;
 
-	err = security_secmark_relabel_packet(tmp_secid);
+	err = security_secmark_relabel_packet(&tmp_prop);
 	if (err)
 		return err;
 
diff --git a/net/netfilter/xt_SECMARK.c b/net/netfilter/xt_SECMARK.c
index ea67aa92ddc2..5a7b83c67430 100644
--- a/net/netfilter/xt_SECMARK.c
+++ b/net/netfilter/xt_SECMARK.c
@@ -43,13 +43,15 @@ secmark_tg(struct sk_buff *skb, const struct xt_secmark_target_info_v1 *info)
 
 static int checkentry_lsm(struct xt_secmark_target_info_v1 *info)
 {
+	struct lsm_prop prop;
 	int err;
 
 	info->secctx[SECMARK_SECCTX_MAX - 1] = '\0';
 	info->secid = 0;
 
-	err = security_secctx_to_secid(info->secctx, strlen(info->secctx),
-				       &info->secid);
+	err = security_secctx_to_lsmprop(info->secctx, strlen(info->secctx),
+					 &prop, LSM_ID_UNDEF);
+
 	if (err) {
 		if (err == -EINVAL)
 			pr_info_ratelimited("invalid security context \'%s\'\n",
@@ -57,18 +59,20 @@ static int checkentry_lsm(struct xt_secmark_target_info_v1 *info)
 		return err;
 	}
 
-	if (!info->secid) {
+	if (!lsmprop_is_set(&prop)) {
 		pr_info_ratelimited("unable to map security context \'%s\'\n",
 				    info->secctx);
 		return -ENOENT;
 	}
 
-	err = security_secmark_relabel_packet(info->secid);
+	err = security_secmark_relabel_packet(&prop);
 	if (err) {
 		pr_info_ratelimited("unable to obtain relabeling permission\n");
 		return err;
 	}
 
+	info->secid = secxa_from_lsmprop(&prop);
+
 	security_secmark_refcount_inc();
 	return 0;
 }
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
index cf590dd08540..e26e15c2d947 100644
--- a/security/apparmor/net.c
+++ b/security/apparmor/net.c
@@ -8,6 +8,7 @@
  * Copyright 2009-2017 Canonical Ltd.
  */
 
+#include <linux/lsm_secxa.h>
 #include "include/af_unix.h"
 #include "include/apparmor.h"
 #include "include/audit.h"
@@ -365,12 +366,17 @@ static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid,
 			   struct apparmor_audit_data *ad)
 {
 	int i, ret;
+	struct lsm_prop *prop;
 	struct aa_perms perms = { };
 	struct aa_ruleset *rules = profile->label.rules[0];
 
 	if (rules->secmark_count == 0)
 		return 0;
 
+	ret = secxa_get_lsmprop(&prop, secid);
+	if (ret)
+		return ret;
+
 	for (i = 0; i < rules->secmark_count; i++) {
 		if (!rules->secmark[i].secid) {
 			ret = apparmor_secmark_init(&rules->secmark[i]);
@@ -378,7 +384,7 @@ static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid,
 				return ret;
 		}
 
-		if (rules->secmark[i].secid == secid ||
+		if (rules->secmark[i].secid == prop->apparmor.label->secid ||
 		    rules->secmark[i].secid == AA_SECID_WILDCARD) {
 			if (rules->secmark[i].deny)
 				perms.deny = ALL_PERMS_MASK;
diff --git a/security/lsm_secxa.c b/security/lsm_secxa.c
index 5b67d8218fd2..2015dab01bdf 100644
--- a/security/lsm_secxa.c
+++ b/security/lsm_secxa.c
@@ -71,8 +71,6 @@ int secxa_from_lsmprop(struct lsm_prop *prop)
 	int rc;
 
 	xa_for_each(&secxa_xa, il, lp) {
-		if (!memcmp(prop, lp, sizeof(*prop)))
-			pr_info("%s found at index %lu\n", __func__, il);
 		if (!memcmp(prop, lp, sizeof(*prop)))
 			return il;
 	}
@@ -101,7 +99,21 @@ EXPORT_SYMBOL(secxa_from_lsmprop);
  */
 void secxa_set_secmark(struct sk_buff *skb, u32 secxa)
 {
-	if (!skb->secmark)
+	struct lsm_prop *olp;
+	struct lsm_prop *nlp;
+	struct lsm_prop prop;
+
+	if (!skb->secmark) {
 		skb->secmark = secxa;
+		return;
+	}
+
+	olp = xa_load(&secxa_xa, skb->secmark);
+	nlp = xa_load(&secxa_xa, secxa);
+
+	prop = *olp;
+	security_update_lsmprop(&prop, nlp, LSM_ID_UNDEF);
+
+	skb->secmark = secxa_from_lsmprop(&prop);
 }
 EXPORT_SYMBOL(secxa_set_secmark);
diff --git a/security/security.c b/security/security.c
index 932a2eca28b3..059cf0a97d2c 100644
--- a/security/security.c
+++ b/security/security.c
@@ -4646,15 +4646,15 @@ EXPORT_SYMBOL(security_inet_conn_established);
 
 /**
  * security_secmark_relabel_packet() - Check if setting a secmark is allowed
- * @secid: new secmark value
+ * @lsmprop: new secmark value
  *
  * Check if the process should be allowed to relabel packets to @secid.
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_secmark_relabel_packet(u32 secid)
+int security_secmark_relabel_packet(struct lsm_prop *prop)
 {
-	return call_int_hook(secmark_relabel_packet, secid);
+	return call_int_hook(secmark_relabel_packet, prop);
 }
 EXPORT_SYMBOL(security_secmark_relabel_packet);
 
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index c05c05e71078..55d7679f3403 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -94,6 +94,7 @@
 #include <linux/io_uring/cmd.h>
 #include <uapi/linux/lsm.h>
 #include <linux/memfd.h>
+#include <linux/lsm_secxa.h>
 
 #include "initcalls.h"
 #include "avc.h"
@@ -5414,7 +5415,16 @@ 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,
+		struct lsm_prop *prop;
+		u32 secmark = 0;
+
+		if (skb->secmark) {
+			err = secxa_get_lsmprop(&prop, skb->secmark);
+			if (!err)
+				secmark = prop->selinux.secid;
+		}
+
+		err = avc_has_perm(sk_sid, secmark, SECCLASS_PACKET,
 				   PACKET__RECV, &ad);
 		if (err)
 			return err;
@@ -5483,7 +5493,15 @@ 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,
+		struct lsm_prop *prop;
+		u32 secmark = 0;
+
+		if (skb->secmark) {
+			err = secxa_get_lsmprop(&prop, skb->secmark);
+			if (!err)
+				secmark = prop->selinux.secid;
+		}
+		err = avc_has_perm(sk_sid, secmark, SECCLASS_PACKET,
 				   PACKET__RECV, &ad);
 		if (err)
 			return err;
@@ -5885,10 +5903,10 @@ static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
 	selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid);
 }
 
-static int selinux_secmark_relabel_packet(u32 sid)
+static int selinux_secmark_relabel_packet(struct lsm_prop *lsmprop)
 {
-	return avc_has_perm(current_sid(), sid, SECCLASS_PACKET, PACKET__RELABELTO,
-			    NULL);
+	return avc_has_perm(current_sid(), lsmprop->selinux.secid,
+			    SECCLASS_PACKET, PACKET__RELABELTO, NULL);
 }
 
 static void selinux_secmark_refcount_inc(void)
@@ -6016,10 +6034,21 @@ 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) {
+		struct lsm_prop *prop;
+		u32 secmark = 0;
+		int err;
+
+		if (skb->secmark) {
+			err = secxa_get_lsmprop(&prop, skb->secmark);
+			if (!err)
+				secmark = prop->selinux.secid;
+		}
+
+		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
@@ -6093,10 +6122,20 @@ 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()) {
+		struct lsm_prop *prop;
+		u32 secmark = 0;
+		int err;
+
+		if (skb->secmark) {
+			err = secxa_get_lsmprop(&prop, skb->secmark);
+			if (!err)
+				secmark = prop->selinux.secid;
+		}
+		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);
@@ -6215,10 +6254,20 @@ 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) {
+		struct lsm_prop *prop;
+		u32 secmark = 0;
+		int err;
+
+		if (skb->secmark) {
+			err = secxa_get_lsmprop(&prop, skb->secmark);
+			if (!err)
+				secmark = prop->selinux.secid;
+		}
+		if (avc_has_perm(peer_sid, secmark,
 				 SECCLASS_PACKET, secmark_perm, &ad))
 			return NF_DROP_ERR(-ECONNREFUSED);
+	}
 
 	if (peerlbl_active) {
 		u32 if_sid;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 8e3ab61dfdd9..38bd97c89179 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -42,6 +42,7 @@
 #include <linux/fs_context.h>
 #include <linux/fs_parser.h>
 #include <linux/watch_queue.h>
+#include <linux/lsm_secxa.h>
 #include <linux/io_uring/cmd.h>
 #include <uapi/linux/lsm.h>
 #include "smack.h"
@@ -4189,10 +4190,17 @@ 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)
 {
+	struct lsm_prop *prop;
+	int rc;
+
 	if (skb == NULL || skb->secmark == 0)
 		return NULL;
 
-	return smack_from_secid(skb->secmark);
+	rc = secxa_get_lsmprop(&prop, skb->secmark);
+	if (prop)
+		return prop->smack.skp;
+
+	return NULL;
 }
 #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 b363c42f252e..5270b55eaacf 100644
--- a/security/smack/smack_netfilter.c
+++ b/security/smack/smack_netfilter.c
@@ -24,13 +24,15 @@ static unsigned int smack_ip_output(void *priv,
 				    const struct nf_hook_state *state)
 {
 	struct sock *sk = skb_to_full_sk(skb);
+	struct lsm_prop prop = { };
 	struct socket_smack *ssp;
-	struct smack_known *skp;
+	int secxa;
 
 	if (sk) {
 		ssp = smack_sock(sk);
-		skp = ssp->smk_out;
-		secxa_set_secmark(skb, skp->smk_secid);
+		prop.smack.skp = ssp->smk_out;
+		secxa = secxa_from_lsmprop(&prop);
+		secxa_set_secmark(skb, secxa);
 	}
 
 	return NF_ACCEPT;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH 7/7] net, lsm: Change skb secmarks to x-array indexes
  2026-08-13 20:48 ` [PATCH 7/7] net, lsm: " Casey Schaufler
@ 2026-08-14  2:39   ` sashiko-bot
  0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-14  2:39 UTC (permalink / raw)
  Cc: selinux

Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider:
- [Critical] Missing NULL checks on the results of `xa_load()` in `secxa_set_secmark()` causes unconditional kernel panics.
- [Critical] In `smack_from_skb()`, failure from `secxa_get_lsmprop()` leaves a stack pointer uninitialized, leading to a wild pointer dereference.
- [High] Uninitialized stack memory causes XArray deduplication failures and leaks kernel stack data.
- [High] Negative error codes from `secxa_from_lsmprop()` are truncated to unsigned 32-bit integers, bypassing error checks.
- [High] The XArray is allowed to allocate index 0, breaking the `skb->secmark == 0` universal sentinel for unlabeled packets.
- [High] A newly allocated `struct lsm_prop` is inserted into the global XArray before its contents are initialized, leading to a race condition that corrupts packet labels.
- [High] AppArmor unconditionally dereferences `prop->apparmor.label`, leading to a crash if the secmark was created by another LSM.
- [Medium] The XArray allocation limit `xa_limit_32b` exceeds the maximum positive value representable by the `int` return type.
- [Medium] Missing synchronization around the read-allocate-insert sequence in `secxa_from_lsmprop()` allows concurrent threads to insert duplicate entries.
- [Medium] SELinux hooks evaluate packets as unlabeled when `secxa_get_lsmprop()` fails, potentially bypassing security policies.
--

--- Patch [7]: [PATCH 7/7] net, lsm: Change skb secmarks to x-array indexes ---
commit 4d10524e4c6ddab86481df7f6394707d0a6f8278
Author: Casey Schaufler <casey@schaufler-ca.com>

net, lsm: Change skb secmarks to x-array indexes

This commit shifts the skb secmark infrastructure to maintain an xarray of
lsm_prop structures. It replaces the LSM-specific secid with an xarray
index representing the security information.

> diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c
> index bd9a9377919cf..0ce662ec3de06 100644
> --- a/net/netfilter/nft_meta.c
> +++ b/net/netfilter/nft_meta.c
> @@ -926,17 +926,20 @@ static const struct nla_policy nft_secmark_policy[NFTA_SECMARK_MAX + 1] = {
>  
>  static int nft_secmark_compute_secid(struct nft_secmark *priv)
>  {
> +	struct lsm_prop tmp_prop;
>  	u32 tmp_secid = 0;
>  	int err;
>  
> -	err = security_secctx_to_secid(priv->ctx, strlen(priv->ctx), &tmp_secid);
> +	err = security_secctx_to_lsmprop(priv->ctx, strlen(priv->ctx),
> +					 &tmp_prop, LSM_ID_UNDEF);

[Severity: High]
Does this leave the tmp_prop structure partially uninitialized on the stack?

Since security_secctx_to_lsmprop() only initializes fields for the active
LSM, the rest of the struct might contain stack garbage. When passed to
secxa_from_lsmprop(), memcmp() is used on the entire struct, which can fail
to match due to garbage bytes, breaking XArray deduplication and leaking
kernel stack data.

Is it necessary to initialize this with an empty struct assignment?

>  	if (err)
>  		return err;
>  
> -	if (!tmp_secid)
> -		return -ENOENT;
> +	tmp_secid = secxa_from_lsmprop(&tmp_prop);
> +	if (tmp_secid < 0)
> +		return tmp_secid;

[Severity: High]
Can the tmp_secid < 0 check ever be true here?

Since tmp_secid is declared as a u32, a negative error code from
secxa_from_lsmprop() (like -ENOMEM) will be truncated to a large unsigned
integer, bypassing the error check. This could store an invalid secmark that
causes guaranteed lookup failures and crashes later.

> diff --git a/net/netfilter/xt_SECMARK.c b/net/netfilter/xt_SECMARK.c
> index ea67aa92ddc2f..5a7b83c674305 100644
> --- a/net/netfilter/xt_SECMARK.c
> +++ b/net/netfilter/xt_SECMARK.c
> @@ -43,13 +43,15 @@ secmark_tg(struct sk_buff *skb, const struct xt_secmark_target_info_v1 *info)
>  
>  static int checkentry_lsm(struct xt_secmark_target_info_v1 *info)
>  {
> +	struct lsm_prop prop;
>  	int err;
>  
>  	info->secctx[SECMARK_SECCTX_MAX - 1] = '\0';
>  	info->secid = 0;
>  
> -	err = security_secctx_to_secid(info->secctx, strlen(info->secctx),
> -				       &info->secid);
> +	err = security_secctx_to_lsmprop(info->secctx, strlen(info->secctx),
> +					 &prop, LSM_ID_UNDEF);
> +

[Severity: High]
Similar to the issue in nft_meta.c, does this leave the prop variable
partially uninitialized on the stack?

[ ... ]

> -	err = security_secmark_relabel_packet(info->secid);
> +	err = security_secmark_relabel_packet(&prop);
>  	if (err) {
>  		pr_info_ratelimited("unable to obtain relabeling permission\n");
>  		return err;
>  	}
>  
> +	info->secid = secxa_from_lsmprop(&prop);

[Severity: High]
Does this directly assign a potential negative error code to info->secid
without any error checking?

> diff --git a/security/apparmor/net.c b/security/apparmor/net.c
> index 44c04102062f3..1d33734d15594 100644
> --- a/security/apparmor/net.c
> +++ b/security/apparmor/net.c
> @@ -362,12 +363,17 @@ static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid,
>  			   struct apparmor_audit_data *ad)
>  {
>  	int i, ret;
> +	struct lsm_prop *prop;
>  	struct aa_perms perms = { };
>  	struct aa_ruleset *rules = profile->label.rules[0];
>  
>  	if (rules->secmark_count == 0)
>  		return 0;
>  
> +	ret = secxa_get_lsmprop(&prop, secid);
> +	if (ret)
> +		return ret;
> +
>  	for (i = 0; i < rules->secmark_count; i++) {
>  		if (!rules->secmark[i].secid) {
>  			ret = apparmor_secmark_init(&rules->secmark[i]);
> @@ -375,7 +381,7 @@ static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid,
>  				return ret;
>  		}
>  
> -		if (rules->secmark[i].secid == secid ||
> +		if (rules->secmark[i].secid == prop->apparmor.label->secid ||

[Severity: High]
Does this unconditionally dereference prop->apparmor.label?

If the packet's secmark was generated by a different LSM (like SELinux
setting iptables rules), the apparmor.label field might be NULL. Could this
cause a NULL pointer dereference crash?

> diff --git a/security/lsm_secxa.c b/security/lsm_secxa.c
> index 5b67d8218fd25..2015dab01bdf4 100644
> --- a/security/lsm_secxa.c
> +++ b/security/lsm_secxa.c
> @@ -71,8 +71,6 @@ int secxa_from_lsmprop(struct lsm_prop *prop)
>  	int rc;
>  
>  	xa_for_each(&secxa_xa, il, lp) {
> -		if (!memcmp(prop, lp, sizeof(*prop)))
> -			pr_info("%s found at index %lu\n", __func__, il);
>  		if (!memcmp(prop, lp, sizeof(*prop)))
>  			return il;
>  	}
> @@ -101,7 +99,21 @@ EXPORT_SYMBOL(secxa_from_lsmprop);

[Severity: Medium]
Is this read-allocate-insert sequence safe from concurrent updates?

Since there is no lock protecting this check-then-act block, multiple
threads processing packets with identical security contexts could
simultaneously fail the search phase and insert duplicate entries.

> +	rc = xa_alloc(&secxa_xa, &index, lp, xa_limit_32b, GFP_ATOMIC);

[Severity: High]
Could xa_alloc assign index 0 here?

The networking core across subsystems uses skb->secmark == 0 as a universal
sentinel for unlabeled packets. If index 0 is allocated to a valid context,
will those labeled packets be incorrectly evaluated as unlabeled?

[Severity: Medium]
Because xa_limit_32b allows indices up to U32_MAX, could an index larger
than INT_MAX be returned here as a negative integer, causing callers to
treat it as an error code since the function returns an int?

> +	if (rc) {
> +		kfree(lp);
> +		return -EINVAL;
> +	}
> +	*lp = *prop;
> +
> +	return index;
> +}

[Severity: High]
Does this introduce a race condition by assigning *lp = *prop after
xa_alloc() has made the pointer globally visible in the XArray?

Another thread doing a lookup might see the fully zeroed memory and use it
to label its packets before the memory is fully initialized here, leading to
incorrect policy application.

>  void secxa_set_secmark(struct sk_buff *skb, u32 secxa)
>  {
> -	if (!skb->secmark)
> +	struct lsm_prop *olp;
> +	struct lsm_prop *nlp;
> +	struct lsm_prop prop;
> +
> +	if (!skb->secmark) {
>  		skb->secmark = secxa;
> +		return;
> +	}
> +
> +	olp = xa_load(&secxa_xa, skb->secmark);
> +	nlp = xa_load(&secxa_xa, secxa);
> +
> +	prop = *olp;
> +	security_update_lsmprop(&prop, nlp, LSM_ID_UNDEF);

[Severity: Critical]
Does this safely handle xa_load() returning NULL?

If the secmark index does not exist, olp or nlp will be NULL, and the
subsequent prop = *olp assignment or security_update_lsmprop() call would
cause a panic.

> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index 66f32dfadf9b3..1739dd2eb0779 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -5996,10 +6014,21 @@ 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) {
> +		struct lsm_prop *prop;
> +		u32 secmark = 0;
> +		int err;
> +
> +		if (skb->secmark) {
> +			err = secxa_get_lsmprop(&prop, skb->secmark);
> +			if (!err)
> +				secmark = prop->selinux.secid;
> +		}
> +
> +		if (avc_has_perm(peer_sid, secmark,
>  				 SECCLASS_PACKET, PACKET__FORWARD_IN, &ad))
>  			return NF_DROP;
> +	}

[Severity: Medium]
If secxa_get_lsmprop() fails, secmark retains its default value of 0.
Will this cause the packet to be evaluated as unlabeled instead of being
dropped? This looks like it might act as a fallback that could bypass
restrictive policies. Similar logic applies to selinux_ip_postroute().

> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index cd9f5765979ff..2eddd47556c79 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -4194,10 +4195,17 @@ 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)
>  {
> +	struct lsm_prop *prop;
> +	int rc;
> +
>  	if (skb == NULL || skb->secmark == 0)
>  		return NULL;
>  
> -	return smack_from_secid(skb->secmark);
> +	rc = secxa_get_lsmprop(&prop, skb->secmark);
> +	if (prop)
> +		return prop->smack.skp;

[Severity: Critical]
If secxa_get_lsmprop() returns an error, could prop remain uninitialized on
the stack?

The if (prop) check evaluates this uninitialized stack memory, which might
evaluate to true and lead to a wild pointer dereference when accessing
prop->smack.skp.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260813204854.19211-1-casey@schaufler-ca.com?part=7

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 0/7] Change skb secmarks to x-array indexes
       [not found] <20260831223748.4304-1-casey.ref@schaufler-ca.com>
@ 2026-08-31 22:37 ` Casey Schaufler
  2026-08-31 22:37   ` [PATCH 1/7] net, smack: Create a function to set secmarks Casey Schaufler
                     ` (6 more replies)
  0 siblings, 7 replies; 17+ messages in thread
From: Casey Schaufler @ 2026-08-31 22:37 UTC (permalink / raw)
  To: casey, paul, linux-security-module, pablo, fw, phil
  Cc: linux-kernel, netfilter-devel, coreteam, jmorris, serge, keescook,
	john.johansen, penguin-kernel, stephen.smalley.work, selinux

When security secmarks were added to the Linux network stack there was
only one Linux Security Module (LSM), SELinux.  SELinux already used the
concept of a security ID (secid) as the representation of the security
information about a system subject (active entity) or object (passive
entity). Adding a container for a secid, the secmark, to the sk_buff
structure allowed for efficient transmission of the SELinux secid for
socket based access controls.

Subsequent LSMs have chosen to represent security information more
directly. Smack and AppArmor use pointers to structures containing
relevant information. Alas, these pointers do not fit in the u32 secmark
on most modern architectures. These LSMs are required to provide a secid
mapping to use secmarks.

Even with all LSMs that use secmarks having a secid to reference the
security information the mechanism is imperfect. A system that wants
to use multiple LSMs that use secmarks is constrained by the size
of the secmark. There is no rational way to fit multiple secids in a
secmark. While it would be possible to allow one LSM to use the secmark
and any others to be told it is unavailable, this has been deemed an
unacceptable limitation.

There is a lsm_prop structure available that contains security information
for any LSM that maintains it. The secmark cannot, unfortunately,
contain one. Instead, an x-array of lsm_prop structures is maintained,
and the index (secxa) is used in the secmark instead of the single LSM
restricted secid.

Uses of security_secctx_to_secid() have been changed to
security_secctx_to_lsmprop() in the netfilter and iptables code.
The security_secmark_relabel_packet() function has been updated to accept
an lsm_prop pointer rather than a secid.
To support multiple LSMs using a secmark it is necessary to re-evaluate
which lsm_prop structure represents the current security information
at each step where the secmark can be set. Smack sets the secmark for
every packet.  Netfilter, used by SELinux, Smack and AppArmor, will set
the secmark on selected packets at a later time. If Smack and AppArmor
are active on a system Smack will set the secmark initially, and AppArmor
may reset it by netfilter rule.

v2: Address issues raised by Sashiko
 - Configuration option insufficiencies
 - Locking issues

https://github.com/cschaufler/lsm-stacking#secmark-xa-7.2-rc7-v2

Casey Schaufler (7):
  net, smack: Create a function to set secmarks
  LSM: Implement x array functions for secmarks
  LSM: Two hooks for manipulating struct lsm_prop
  SELinux: hooks for secctx_to_lsmprop and update_lsmprop
  Smack: hooks for secctx_to_lsmprop and update_lsmprop
  Apparmor: hooks for secctx_to_lsmprop and update_lsmprop
  net, lsm: Change skb secmarks to x-array indexes

 include/linux/lsm_hook_defs.h     |   6 +-
 include/linux/lsm_secxa.h         |  33 ++++++++
 include/linux/security.h          |  20 ++++-
 net/netfilter/nfnetlink_queue.c   |  12 ++-
 net/netfilter/nft_meta.c          |  20 +++--
 net/netfilter/xt_CONNSECMARK.c    |   3 +-
 net/netfilter/xt_SECMARK.c        |  19 +++--
 security/Makefile                 |   1 +
 security/apparmor/include/secid.h |   4 +
 security/apparmor/lsm.c           |   2 +
 security/apparmor/net.c           |   8 +-
 security/apparmor/secid.c         |  21 +++++
 security/lsm_secxa.c              | 128 ++++++++++++++++++++++++++++++
 security/security.c               |  38 ++++++++-
 security/selinux/hooks.c          |  87 +++++++++++++++++---
 security/smack/smack_lsm.c        |  43 +++++++++-
 security/smack/smack_netfilter.c  |  11 ++-
 17 files changed, 418 insertions(+), 38 deletions(-)
 create mode 100644 include/linux/lsm_secxa.h
 create mode 100644 security/lsm_secxa.c

-- 
2.54.0


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 1/7] net, smack: Create a function to set secmarks
  2026-08-31 22:37 ` [PATCH 0/7] Change skb secmarks to x-array indexes Casey Schaufler
@ 2026-08-31 22:37   ` Casey Schaufler
  2026-08-31 22:57     ` sashiko-bot
  2026-08-31 22:37   ` [PATCH 2/7] LSM: Implement x array functions for secmarks Casey Schaufler
                     ` (5 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: Casey Schaufler @ 2026-08-31 22:37 UTC (permalink / raw)
  To: casey, paul, linux-security-module, pablo, fw, phil
  Cc: linux-kernel, netfilter-devel, coreteam, jmorris, serge, keescook,
	john.johansen, penguin-kernel, stephen.smalley.work, selinux

Rather than open coding assignments to skb->secmark, use a helper function
secxa_set_secmark(). This allows for a case where assigning a secmark
is more complex than a simple assignment. The version of the function
here does the legacy simple assignment.

Change the functions that currently assign values to skb->secmark to
use this function.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
 include/linux/lsm_secxa.h        | 28 ++++++++++++++++++++++++++++
 net/netfilter/nft_meta.c         |  5 +++--
 net/netfilter/xt_CONNSECMARK.c   |  3 ++-
 net/netfilter/xt_SECMARK.c       |  3 ++-
 security/smack/smack_netfilter.c |  3 ++-
 5 files changed, 37 insertions(+), 5 deletions(-)
 create mode 100644 include/linux/lsm_secxa.h

diff --git a/include/linux/lsm_secxa.h b/include/linux/lsm_secxa.h
new file mode 100644
index 000000000000..926257d4730c
--- /dev/null
+++ b/include/linux/lsm_secxa.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/*
+ * Copyright (C) 2026 Casey Schaufler <casey@schaufler-ca.com>
+ */
+
+#ifndef __LINUX_LSM_SECXA_H
+#define __LINUX_LSM_SECXA_H
+
+#ifdef CONFIG_NETWORK_SECMARK
+
+#include <linux/security.h>
+#include <linux/skbuff.h>
+
+static inline void secxa_set_secmark(struct sk_buff *skb, u32 secxa)
+{
+	skb->secmark = secxa;
+}
+#else /* CONFIG_NETWORK_SECMARK */
+
+struct sk_buff;
+
+static inline void secxa_set_secmark(struct sk_buff *skb, u32 secxa)
+{
+}
+#endif /* CONFIG_NETWORK_SECMARK */
+
+#endif  /* __LINUX_LSM_SECXA_H */
diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c
index 0a43e0787a68..bd0f7a0931f4 100644
--- a/net/netfilter/nft_meta.c
+++ b/net/netfilter/nft_meta.c
@@ -17,6 +17,7 @@
 #include <linux/random.h>
 #include <linux/smp.h>
 #include <linux/static_key.h>
+#include <linux/lsm_secxa.h>
 #include <net/dst.h>
 #include <net/ip.h>
 #include <net/sock.h>
@@ -502,7 +503,7 @@ void nft_meta_set_eval(const struct nft_expr *expr,
 		break;
 #ifdef CONFIG_NETWORK_SECMARK
 	case NFT_META_SECMARK:
-		skb->secmark = value;
+		secxa_set_secmark(skb, value);
 		break;
 #endif
 	default:
@@ -950,7 +951,7 @@ static void nft_secmark_obj_eval(struct nft_object *obj, struct nft_regs *regs,
 	const struct nft_secmark *priv = nft_obj_data(obj);
 	struct sk_buff *skb = pkt->skb;
 
-	skb->secmark = priv->secid;
+	secxa_set_secmark(skb, priv->secid);
 }
 
 static int nft_secmark_obj_init(const struct nft_ctx *ctx,
diff --git a/net/netfilter/xt_CONNSECMARK.c b/net/netfilter/xt_CONNSECMARK.c
index 1494b3ee30e1..9d799d2459dc 100644
--- a/net/netfilter/xt_CONNSECMARK.c
+++ b/net/netfilter/xt_CONNSECMARK.c
@@ -14,6 +14,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 #include <linux/module.h>
 #include <linux/skbuff.h>
+#include <linux/lsm_secxa.h>
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_CONNSECMARK.h>
 #include <net/netfilter/nf_conntrack.h>
@@ -55,7 +56,7 @@ static void secmark_restore(struct sk_buff *skb)
 
 		ct = nf_ct_get(skb, &ctinfo);
 		if (ct && ct->secmark)
-			skb->secmark = ct->secmark;
+			secxa_set_secmark(skb, ct->secmark);
 	}
 }
 
diff --git a/net/netfilter/xt_SECMARK.c b/net/netfilter/xt_SECMARK.c
index 5bc5ea505eb9..ea67aa92ddc2 100644
--- a/net/netfilter/xt_SECMARK.c
+++ b/net/netfilter/xt_SECMARK.c
@@ -11,6 +11,7 @@
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 #include <linux/module.h>
 #include <linux/security.h>
+#include <linux/lsm_secxa.h>
 #include <linux/skbuff.h>
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_SECMARK.h>
@@ -36,7 +37,7 @@ secmark_tg(struct sk_buff *skb, const struct xt_secmark_target_info_v1 *info)
 		BUG();
 	}
 
-	skb->secmark = secmark;
+	secxa_set_secmark(skb, secmark);
 	return XT_CONTINUE;
 }
 
diff --git a/security/smack/smack_netfilter.c b/security/smack/smack_netfilter.c
index 17ba578b1308..b363c42f252e 100644
--- a/security/smack/smack_netfilter.c
+++ b/security/smack/smack_netfilter.c
@@ -14,6 +14,7 @@
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter_ipv6.h>
 #include <linux/netdevice.h>
+#include <linux/lsm_secxa.h>
 #include <net/inet_sock.h>
 #include <net/net_namespace.h>
 #include "smack.h"
@@ -29,7 +30,7 @@ static unsigned int smack_ip_output(void *priv,
 	if (sk) {
 		ssp = smack_sock(sk);
 		skp = ssp->smk_out;
-		skb->secmark = skp->smk_secid;
+		secxa_set_secmark(skb, skp->smk_secid);
 	}
 
 	return NF_ACCEPT;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 2/7] LSM: Implement x array functions for secmarks
  2026-08-31 22:37 ` [PATCH 0/7] Change skb secmarks to x-array indexes Casey Schaufler
  2026-08-31 22:37   ` [PATCH 1/7] net, smack: Create a function to set secmarks Casey Schaufler
@ 2026-08-31 22:37   ` Casey Schaufler
  2026-08-31 22:59     ` sashiko-bot
  2026-08-31 22:37   ` [PATCH 3/7] LSM: Two hooks for manipulating struct lsm_prop Casey Schaufler
                     ` (4 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: Casey Schaufler @ 2026-08-31 22:37 UTC (permalink / raw)
  To: casey, paul, linux-security-module, pablo, fw, phil
  Cc: linux-kernel, netfilter-devel, coreteam, jmorris, serge, keescook,
	john.johansen, penguin-kernel, stephen.smalley.work, selinux

Implement, but don't use (yet) the functions required to use
xarray indexes in secmarks.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
 include/linux/lsm_secxa.h |  19 ++++---
 security/Makefile         |   1 +
 security/lsm_secxa.c      | 105 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 117 insertions(+), 8 deletions(-)
 create mode 100644 security/lsm_secxa.c

diff --git a/include/linux/lsm_secxa.h b/include/linux/lsm_secxa.h
index 926257d4730c..569c7f242b78 100644
--- a/include/linux/lsm_secxa.h
+++ b/include/linux/lsm_secxa.h
@@ -7,19 +7,22 @@
 #ifndef __LINUX_LSM_SECXA_H
 #define __LINUX_LSM_SECXA_H
 
-#ifdef CONFIG_NETWORK_SECMARK
+#ifdef CONFIG_SECURITY
 
-#include <linux/security.h>
-#include <linux/skbuff.h>
+struct lsm_prop;
 
-static inline void secxa_set_secmark(struct sk_buff *skb, u32 secxa)
-{
-	skb->secmark = secxa;
-}
-#else /* CONFIG_NETWORK_SECMARK */
+int secxa_from_lsmprop(struct lsm_prop *prop, u32 *secxa);
+int secxa_get_lsmprop(struct lsm_prop **pro, u32 secxa);
+
+#endif /* CONFIG_SECURITY */
+
+#ifdef CONFIG_NETWORK_SECMARK
 
 struct sk_buff;
 
+void secxa_set_secmark(struct sk_buff *skb, u32 secxa);
+#else /* CONFIG_NETWORK_SECMARK */
+
 static inline void secxa_set_secmark(struct sk_buff *skb, u32 secxa)
 {
 }
diff --git a/security/Makefile b/security/Makefile
index 4601230ba442..e93be00bb6ae 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_KEYS)			+= keys/
 # always enable default capabilities
 obj-y					+= commoncap.o
 obj-$(CONFIG_SECURITY) 			+= lsm_syscalls.o
+obj-$(CONFIG_NETWORK_SECMARK)		+= lsm_secxa.o
 obj-$(CONFIG_MMU)			+= min_addr.o
 
 # Object file lists
diff --git a/security/lsm_secxa.c b/security/lsm_secxa.c
new file mode 100644
index 000000000000..50ce613e35c0
--- /dev/null
+++ b/security/lsm_secxa.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/*
+ * Implement functions supporting an x array for LSM properties.
+ *
+ * Copyright (C) 2026 Casey Schaufler <casey@schaufler-ca.com>
+ */
+#define pr_fmt(fmt) "secxa: "fmt
+
+#include <linux/xarray.h>
+#include <linux/export.h>
+#include <linux/security.h>
+#include <linux/lsm_secxa.h>
+#include <linux/skbuff.h>
+
+/*
+ * An Xarray of lsm_prop structures.
+ */
+struct xarray secxa_xa;
+
+/**
+ * secxa_init - initialize the xarry of lsm_prop structures.
+ */
+static int __init secxa_init(void)
+{
+	xa_init_flags(&secxa_xa, XA_FLAGS_ALLOC1 | XA_FLAGS_LOCK_BH);
+
+	return 0;
+}
+core_initcall(secxa_init);
+
+/**
+ * secxa_get_lsmprop - get the lsm_prop associated with a secxa
+ * @pro: destination for the lsm_prop pointer
+ * @secxa: index to look up
+ *
+ * Find the lsm_prop associated with @secxa and place a pointer
+ * to it in @pro.
+ *
+ * Returns 0, or -EINVAL if the mapping can't be found.
+ */
+int secxa_get_lsmprop(struct lsm_prop **pro, u32 secxa)
+{
+	struct lsm_prop *lp;
+
+	if (!secxa)
+		return -EINVAL;
+
+	lp = xa_load(&secxa_xa, secxa);
+	if (!lp)
+		return -EINVAL;
+
+	*pro = lp;
+	return 0;
+}
+EXPORT_SYMBOL(secxa_get_lsmprop);
+
+/**
+ * secxa_from_lsmprop - get the secxa associated with a lsm_prop
+ * @prop: lsm_prop pointer CBS * @secxa: result
+ *
+ * Find the secxa associated with @prop. If there is none, create it.
+ *
+ * Returns 0, or an error if the mapping cannot be created
+ */
+int secxa_from_lsmprop(struct lsm_prop *prop, u32 *secxa)
+{
+	struct lsm_prop *lp;
+	unsigned long il;
+	u32 index = 0;
+	int rc;
+
+	xa_for_each(&secxa_xa, il, lp) {
+		if (!memcmp(prop, lp, sizeof(*prop))) {
+			*secxa = il; return 0; CBS }
+	}
+
+	lp = kzalloc(sizeof(*lp), GFP_ATOMIC);
+	if (!lp)
+		return -ENOMEM;
+	*lp = *prop;
+
+	rc = xa_alloc_bh(&secxa_xa, &index, lp, xa_limit_31b, GFP_ATOMIC);
+	if (rc) {
+		kfree(lp);
+		return -EINVAL;
+	}
+
+	*secxa = index; return 0; CBS
+}
+EXPORT_SYMBOL(secxa_from_lsmprop);
+
+/**
+ * secxa_set_secmark - add LSM information to a secmark
+ * @skb: buffer with the secmark
+ * @secxa: index of the information to add
+ *
+ * If the secmark in @skb is not set, set it to @secxa.
+ */
+void secxa_set_secmark(struct sk_buff *skb, u32 secxa)
+{
+	if (!skb->secmark)
+		skb->secmark = secxa;
+}
+EXPORT_SYMBOL(secxa_set_secmark);
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 3/7] LSM: Two hooks for manipulating struct lsm_prop
  2026-08-31 22:37 ` [PATCH 0/7] Change skb secmarks to x-array indexes Casey Schaufler
  2026-08-31 22:37   ` [PATCH 1/7] net, smack: Create a function to set secmarks Casey Schaufler
  2026-08-31 22:37   ` [PATCH 2/7] LSM: Implement x array functions for secmarks Casey Schaufler
@ 2026-08-31 22:37   ` Casey Schaufler
  2026-08-31 23:03     ` sashiko-bot
  2026-08-31 22:37   ` [PATCH 4/7] SELinux: hooks for secctx_to_lsmprop and update_lsmprop Casey Schaufler
                     ` (3 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: Casey Schaufler @ 2026-08-31 22:37 UTC (permalink / raw)
  To: casey, paul, linux-security-module, pablo, fw, phil
  Cc: linux-kernel, netfilter-devel, coreteam, jmorris, serge, keescook,
	john.johansen, penguin-kernel, stephen.smalley.work, selinux

security_update_lsmprop() updates the property of the
specified LSM in the @dest structure with that in the @src.

security_secctx_to_lsmprop() sets the @prop field associated
with the LSM specified to the value of the passed security
context.

LSM specific implementations of these hooks to follow.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
 include/linux/lsm_hook_defs.h |  4 ++++
 include/linux/security.h      | 16 ++++++++++++++++
 security/security.c           | 32 ++++++++++++++++++++++++++++++++
 3 files changed, 52 insertions(+)

diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 65c9609ec207..679c40a8e127 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -305,7 +305,11 @@ 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,
 	 struct lsm_context *cp)
+LSM_HOOK(void, LSM_RET_VOID, update_lsmprop, struct lsm_prop *dest,
+	 struct lsm_prop *src, int lsmid)
 LSM_HOOK(int, 0, secctx_to_secid, const char *secdata, u32 seclen, u32 *secid)
+LSM_HOOK(int, -EINVAL, secctx_to_lsmprop, const char *secdata, u32 seclen,
+	 struct lsm_prop *prop)
 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)
diff --git a/include/linux/security.h b/include/linux/security.h
index 153e9043058f..19adc19eb9af 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -576,6 +576,11 @@ int security_secid_to_secctx(u32 secid, struct lsm_context *cp);
 int security_lsmprop_to_secctx(struct lsm_prop *prop, struct lsm_context *cp,
 			       int lsmid);
 int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
+int security_secctx_to_lsmprop(const char *secdata, u32 seclen,
+			       struct lsm_prop *prop, int lsmid);
+
+void security_update_lsmprop(struct lsm_prop *dest, struct lsm_prop *src,
+			     int lsmid);
 void security_release_secctx(struct lsm_context *cp);
 void security_inode_invalidate_secctx(struct inode *inode);
 int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
@@ -1581,6 +1586,11 @@ static inline int security_lsmprop_to_secctx(struct lsm_prop *prop,
 	return -EOPNOTSUPP;
 }
 
+static inline void security_update_lsmprop(struct lsm_prop *dest,
+					   struct lsm_prop *src, int lsmid)
+{
+}
+
 static inline int security_secctx_to_secid(const char *secdata,
 					   u32 seclen,
 					   u32 *secid)
@@ -1588,6 +1598,12 @@ static inline int security_secctx_to_secid(const char *secdata,
 	return -EOPNOTSUPP;
 }
 
+static inline int security_secctx_to_lsmprop(const char *secdata, u32 seclen,
+					     struct lsm_prop *prop, int lsmid)
+{
+	return -EOPNOTSUPP;
+}
+
 static inline void security_release_secctx(struct lsm_context *cp)
 {
 }
diff --git a/security/security.c b/security/security.c
index 71aea8fdf014..1dec0037370b 100644
--- a/security/security.c
+++ b/security/security.c
@@ -3965,6 +3965,13 @@ int security_lsmprop_to_secctx(struct lsm_prop *prop, struct lsm_context *cp,
 }
 EXPORT_SYMBOL(security_lsmprop_to_secctx);
 
+void security_update_lsmprop(struct lsm_prop *dest, struct lsm_prop *src,
+			     int lsmid)
+{
+	call_void_hook(update_lsmprop, dest, src, lsmid);
+}
+EXPORT_SYMBOL(security_update_lsmprop);
+
 /**
  * security_secctx_to_secid() - Convert a secctx to a secid
  * @secdata: secctx
@@ -3982,6 +3989,31 @@ int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
 }
 EXPORT_SYMBOL(security_secctx_to_secid);
 
+/**
+ * security_secctx_to_lsmprop() - Convert a secctx to a lsmprop
+ * @secdata: secctx
+ * @seclen: length of secctx
+ * @prop: prop
+ * @lsmid: which LSM the context is appropriate to.
+ *
+ * Convert security context to an lsmprop.
+ *
+ * Return: Returns 0 on success, error on failure.
+ */
+int security_secctx_to_lsmprop(const char *secdata, u32 seclen,
+			       struct lsm_prop *prop, int lsmid)
+{
+	struct lsm_static_call *scall;
+
+	lsm_for_each_hook(scall, secctx_to_lsmprop) {
+		if (lsmid != LSM_ID_UNDEF && lsmid != scall->hl->lsmid->id)
+			continue;
+		return scall->hl->hook.secctx_to_lsmprop(secdata, seclen, prop);
+	}
+	return LSM_RET_DEFAULT(secctx_to_lsmprop);
+}
+EXPORT_SYMBOL(security_secctx_to_lsmprop);
+
 /**
  * security_release_secctx() - Free a secctx buffer
  * @cp: the security context
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 4/7] SELinux: hooks for secctx_to_lsmprop and update_lsmprop
  2026-08-31 22:37 ` [PATCH 0/7] Change skb secmarks to x-array indexes Casey Schaufler
                     ` (2 preceding siblings ...)
  2026-08-31 22:37   ` [PATCH 3/7] LSM: Two hooks for manipulating struct lsm_prop Casey Schaufler
@ 2026-08-31 22:37   ` Casey Schaufler
  2026-08-31 22:55     ` sashiko-bot
  2026-08-31 22:37   ` [PATCH 5/7] Smack: " Casey Schaufler
                     ` (2 subsequent siblings)
  6 siblings, 1 reply; 17+ messages in thread
From: Casey Schaufler @ 2026-08-31 22:37 UTC (permalink / raw)
  To: casey, paul, linux-security-module, pablo, fw, phil
  Cc: linux-kernel, netfilter-devel, coreteam, jmorris, serge, keescook,
	john.johansen, penguin-kernel, stephen.smalley.work, selinux

Implement these hooks.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
 security/selinux/hooks.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 18dd28b2bb13..12614478b638 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -6946,6 +6946,13 @@ static int selinux_ismaclabel(const char *name)
 	return (strcmp(name, XATTR_SELINUX_SUFFIX) == 0);
 }
 
+static void selinux_update_lsmprop(struct lsm_prop *dest, struct lsm_prop *src,
+				   int lsmid)
+{
+	if (lsmid == LSM_ID_SELINUX || lsmid == LSM_ID_UNDEF)
+		dest->selinux.secid = src->selinux.secid;
+}
+
 static int selinux_secid_to_secctx(u32 secid, struct lsm_context *cp)
 {
 	u32 seclen;
@@ -6964,6 +6971,13 @@ static int selinux_secid_to_secctx(u32 secid, struct lsm_context *cp)
 	return seclen;
 }
 
+static int selinux_secctx_to_lsmprop(const char *secdata, u32 seclen,
+				     struct lsm_prop *prop)
+{
+	return security_context_to_sid(secdata, seclen, &prop->selinux.secid,
+				       GFP_KERNEL);
+}
+
 static int selinux_lsmprop_to_secctx(struct lsm_prop *prop,
 				     struct lsm_context *cp)
 {
@@ -7694,6 +7708,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
 
 	LSM_HOOK_INIT(ismaclabel, selinux_ismaclabel),
 	LSM_HOOK_INIT(secctx_to_secid, selinux_secctx_to_secid),
+	LSM_HOOK_INIT(secctx_to_lsmprop, selinux_secctx_to_lsmprop),
 	LSM_HOOK_INIT(release_secctx, selinux_release_secctx),
 	LSM_HOOK_INIT(inode_invalidate_secctx, selinux_inode_invalidate_secctx),
 	LSM_HOOK_INIT(inode_notifysecctx, selinux_inode_notifysecctx),
@@ -7812,6 +7827,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(sem_alloc_security, selinux_sem_alloc_security),
 	LSM_HOOK_INIT(secid_to_secctx, selinux_secid_to_secctx),
 	LSM_HOOK_INIT(lsmprop_to_secctx, selinux_lsmprop_to_secctx),
+	LSM_HOOK_INIT(update_lsmprop, selinux_update_lsmprop),
 	LSM_HOOK_INIT(inode_getsecctx, selinux_inode_getsecctx),
 	LSM_HOOK_INIT(sk_alloc_security, selinux_sk_alloc_security),
 	LSM_HOOK_INIT(tun_dev_alloc_security, selinux_tun_dev_alloc_security),
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 5/7] Smack: hooks for secctx_to_lsmprop and update_lsmprop
  2026-08-31 22:37 ` [PATCH 0/7] Change skb secmarks to x-array indexes Casey Schaufler
                     ` (3 preceding siblings ...)
  2026-08-31 22:37   ` [PATCH 4/7] SELinux: hooks for secctx_to_lsmprop and update_lsmprop Casey Schaufler
@ 2026-08-31 22:37   ` Casey Schaufler
  2026-08-31 23:01     ` sashiko-bot
  2026-08-31 22:37   ` [PATCH 6/7] Apparmor: " Casey Schaufler
  2026-08-31 22:37   ` [PATCH 7/7] net, lsm: Change skb secmarks to x-array indexes Casey Schaufler
  6 siblings, 1 reply; 17+ messages in thread
From: Casey Schaufler @ 2026-08-31 22:37 UTC (permalink / raw)
  To: casey, paul, linux-security-module, pablo, fw, phil
  Cc: linux-kernel, netfilter-devel, coreteam, jmorris, serge, keescook,
	john.johansen, penguin-kernel, stephen.smalley.work, selinux

Implement these hooks.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
 security/smack/smack_lsm.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index ff115068c5c0..fcfadd5d9994 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -4909,6 +4909,37 @@ static int smack_lsmprop_to_secctx(struct lsm_prop *prop,
 	return smack_to_secctx(prop->smack.skp, cp);
 }
 
+/**
+ * smack_secctx_to_lsmprop - add the smack label to an lsmprop
+ * @secdata: smack label
+ * @seclen: how long label is
+ * @prop: where to put the result
+ *
+ * Exists for audit and networking code.
+ */
+static int smack_secctx_to_lsmprop(const char *secdata, u32 seclen,
+				   struct lsm_prop *prop)
+{
+	prop->smack.skp = smk_find_entry(secdata);
+
+	return 0;
+}
+
+/**
+ * smack_update_lsmprop - set the smack label in an lsmprop
+ * @dest: destination properties
+ * @src: source properties
+ * @lsmid: which LSM is relevant.
+ *
+ * Set the Smack entry in the @dest if appropriate.
+ */
+static void smack_update_lsmprop(struct lsm_prop *dest, struct lsm_prop *src,
+				 int lsmid)
+{
+	if (lsmid == LSM_ID_SMACK || lsmid == LSM_ID_UNDEF)
+		dest->smack.skp = src->smack.skp;
+}
+
 /**
  * smack_secctx_to_secid - return the secid for a smack label
  * @secdata: smack label
@@ -5269,6 +5300,8 @@ static struct security_hook_list smack_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(secid_to_secctx, smack_secid_to_secctx),
 	LSM_HOOK_INIT(lsmprop_to_secctx, smack_lsmprop_to_secctx),
 	LSM_HOOK_INIT(secctx_to_secid, smack_secctx_to_secid),
+	LSM_HOOK_INIT(secctx_to_lsmprop, smack_secctx_to_lsmprop),
+	LSM_HOOK_INIT(update_lsmprop, smack_update_lsmprop),
 	LSM_HOOK_INIT(inode_notifysecctx, smack_inode_notifysecctx),
 	LSM_HOOK_INIT(inode_setsecctx, smack_inode_setsecctx),
 	LSM_HOOK_INIT(inode_getsecctx, smack_inode_getsecctx),
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 6/7] Apparmor: hooks for secctx_to_lsmprop and update_lsmprop
  2026-08-31 22:37 ` [PATCH 0/7] Change skb secmarks to x-array indexes Casey Schaufler
                     ` (4 preceding siblings ...)
  2026-08-31 22:37   ` [PATCH 5/7] Smack: " Casey Schaufler
@ 2026-08-31 22:37   ` Casey Schaufler
  2026-08-31 23:00     ` sashiko-bot
  2026-08-31 22:37   ` [PATCH 7/7] net, lsm: Change skb secmarks to x-array indexes Casey Schaufler
  6 siblings, 1 reply; 17+ messages in thread
From: Casey Schaufler @ 2026-08-31 22:37 UTC (permalink / raw)
  To: casey, paul, linux-security-module, pablo, fw, phil
  Cc: linux-kernel, netfilter-devel, coreteam, jmorris, serge, keescook,
	john.johansen, penguin-kernel, stephen.smalley.work, selinux

Implement these hooks.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
 security/apparmor/include/secid.h |  4 ++++
 security/apparmor/lsm.c           |  2 ++
 security/apparmor/secid.c         | 21 +++++++++++++++++++++
 3 files changed, 27 insertions(+)

diff --git a/security/apparmor/include/secid.h b/security/apparmor/include/secid.h
index 6025d3849cf8..ba7adf2fc09e 100644
--- a/security/apparmor/include/secid.h
+++ b/security/apparmor/include/secid.h
@@ -28,6 +28,10 @@ struct aa_label *aa_secid_to_label(u32 secid);
 int apparmor_secid_to_secctx(u32 secid, struct lsm_context *cp);
 int apparmor_lsmprop_to_secctx(struct lsm_prop *prop, struct lsm_context *cp);
 int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
+int apparmor_secctx_to_lsmprop(const char *secdata, u32 seclen,
+			       struct lsm_prop *prop);
+void apparmor_update_lsmprop(struct lsm_prop *dest, struct lsm_prop *src,
+			     int lsmid);
 void apparmor_release_secctx(struct lsm_context *cp);
 
 
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 88d12e89d115..1f304b88eaf9 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -1766,6 +1766,8 @@ static struct security_hook_list apparmor_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(secid_to_secctx, apparmor_secid_to_secctx),
 	LSM_HOOK_INIT(lsmprop_to_secctx, apparmor_lsmprop_to_secctx),
 	LSM_HOOK_INIT(secctx_to_secid, apparmor_secctx_to_secid),
+	LSM_HOOK_INIT(secctx_to_lsmprop, apparmor_secctx_to_lsmprop),
+	LSM_HOOK_INIT(update_lsmprop, apparmor_update_lsmprop),
 	LSM_HOOK_INIT(release_secctx, apparmor_release_secctx),
 
 #ifdef CONFIG_IO_URING
diff --git a/security/apparmor/secid.c b/security/apparmor/secid.c
index 28caf66b9033..d35fdbf074e0 100644
--- a/security/apparmor/secid.c
+++ b/security/apparmor/secid.c
@@ -106,6 +106,27 @@ int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
 	return 0;
 }
 
+int apparmor_secctx_to_lsmprop(const char *secdata, u32 seclen,
+			       struct lsm_prop *prop)
+{
+	struct aa_label *label;
+
+	label = aa_label_strn_parse(&root_ns->unconfined->label, secdata,
+				    seclen, GFP_KERNEL, false, false);
+	if (IS_ERR(label))
+		return PTR_ERR(label);
+	prop->apparmor.label = label;
+
+	return 0;
+}
+
+void apparmor_update_lsmprop(struct lsm_prop *dest, struct lsm_prop *src,
+			     int lsmid)
+{
+	if (lsmid == LSM_ID_APPARMOR || lsmid == LSM_ID_UNDEF)
+		dest->apparmor.label = src->apparmor.label;
+}
+
 void apparmor_release_secctx(struct lsm_context *cp)
 {
 	if (cp->id == LSM_ID_APPARMOR) {
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH 7/7] net, lsm: Change skb secmarks to x-array indexes
  2026-08-31 22:37 ` [PATCH 0/7] Change skb secmarks to x-array indexes Casey Schaufler
                     ` (5 preceding siblings ...)
  2026-08-31 22:37   ` [PATCH 6/7] Apparmor: " Casey Schaufler
@ 2026-08-31 22:37   ` Casey Schaufler
  2026-08-31 23:07     ` sashiko-bot
  6 siblings, 1 reply; 17+ messages in thread
From: Casey Schaufler @ 2026-08-31 22:37 UTC (permalink / raw)
  To: casey, paul, linux-security-module, pablo, fw, phil
  Cc: linux-kernel, netfilter-devel, coreteam, jmorris, serge, keescook,
	john.johansen, penguin-kernel, stephen.smalley.work, selinux

Maintain a xarray of lsm_prop structures which represent the
LSM security information passed via skb->secmark. Pass the xarray
index of the appropriate lsm_prop (the secxa) instead of an LSM
specific secid. Allow multiple LSMs to specify their components
in xarray entries, or create new entries as necessary.

Change uses of security_secctx_to_secid() to security_secctx_to_lsmprop()
in the netfilter and iptables code. Change security_secmark_relabel_packet()
to accept an lsm_prop pointer rather than a secid. Change secxa_set_secmark()
to update and create new entries as necessary.

Update the SELinux, Smack and AppArmor hooks that use secmarks to
expect a secxa xarray index instead of a secid.

Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
---
 include/linux/lsm_hook_defs.h    |  2 +-
 include/linux/lsm_secxa.h        |  4 +-
 include/linux/security.h         |  4 +-
 net/netfilter/nfnetlink_queue.c  | 12 +++++-
 net/netfilter/nft_meta.c         | 15 ++++---
 net/netfilter/xt_SECMARK.c       | 16 +++++--
 security/apparmor/net.c          |  8 +++-
 security/lsm_secxa.c             | 37 +++++++++++++----
 security/security.c              |  6 +--
 security/selinux/hooks.c         | 71 +++++++++++++++++++++++++++-----
 security/smack/smack_lsm.c       | 10 ++++-
 security/smack/smack_netfilter.c | 10 +++--
 12 files changed, 153 insertions(+), 42 deletions(-)

diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index 679c40a8e127..8ecf07e0e3f0 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -371,7 +371,7 @@ LSM_HOOK(void, LSM_RET_VOID, inet_csk_clone, struct sock *newsk,
 	 const struct request_sock *req)
 LSM_HOOK(void, LSM_RET_VOID, inet_conn_established, struct sock *sk,
 	 struct sk_buff *skb)
-LSM_HOOK(int, 0, secmark_relabel_packet, u32 secid)
+LSM_HOOK(int, 0, secmark_relabel_packet, struct lsm_prop *prop)
 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,
diff --git a/include/linux/lsm_secxa.h b/include/linux/lsm_secxa.h
index 569c7f242b78..5be9d64e67e4 100644
--- a/include/linux/lsm_secxa.h
+++ b/include/linux/lsm_secxa.h
@@ -7,11 +7,13 @@
 #ifndef __LINUX_LSM_SECXA_H
 #define __LINUX_LSM_SECXA_H
 
+#include <linux/types.h>
+
 #ifdef CONFIG_SECURITY
 
 struct lsm_prop;
 
-int secxa_from_lsmprop(struct lsm_prop *prop, u32 *secxa);
+int secxa_from_lsmprop(struct lsm_prop *prop, u32 *secxa);
 int secxa_get_lsmprop(struct lsm_prop **pro, u32 secxa);
 
 #endif /* CONFIG_SECURITY */
diff --git a/include/linux/security.h b/include/linux/security.h
index 19adc19eb9af..ffbd1708065f 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1715,7 +1715,7 @@ void security_inet_csk_clone(struct sock *newsk,
 			const struct request_sock *req);
 void security_inet_conn_established(struct sock *sk,
 			struct sk_buff *skb);
-int security_secmark_relabel_packet(u32 secid);
+int security_secmark_relabel_packet(struct lsm_prop *prop);
 void security_secmark_refcount_inc(void);
 void security_secmark_refcount_dec(void);
 int security_tun_dev_alloc_security(void **security);
@@ -1898,7 +1898,7 @@ static inline void security_inet_conn_established(struct sock *sk,
 {
 }
 
-static inline int security_secmark_relabel_packet(u32 secid)
+static inline int security_secmark_relabel_packet(struct lsm_prop *prop)
 {
 	return 0;
 }
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index b8aaf39cb4d8..ebab037edc6b 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -32,6 +32,7 @@
 #include <linux/cgroup-defs.h>
 #include <linux/rhashtable.h>
 #include <linux/jhash.h>
+#include <linux/lsm_secxa.h>
 #include <net/gso.h>
 #include <net/sock.h>
 #include <net/tcp_states.h>
@@ -606,8 +607,15 @@ static int nfqnl_get_sk_secctx(struct sk_buff *skb, struct lsm_context *ctx)
 {
 	int seclen = 0;
 #if IS_ENABLED(CONFIG_NETWORK_SECMARK)
-	if (skb->secmark)
-		seclen = security_secid_to_secctx(skb->secmark, ctx);
+	struct lsm_prop *prop;
+	int rc;
+
+	if (skb->secmark) {
+		rc = secxa_get_lsmprop(&prop, skb->secmark);
+		if (rc)
+			return 0;
+		seclen = security_lsmprop_to_secctx(prop, ctx, LSM_ID_UNDEF);
+	}
 #endif
 	return seclen;
 }
diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c
index bd0f7a0931f4..664191dfa4b2 100644
--- a/net/netfilter/nft_meta.c
+++ b/net/netfilter/nft_meta.c
@@ -927,21 +927,24 @@ static const struct nla_policy nft_secmark_policy[NFTA_SECMARK_MAX + 1] = {
 
 static int nft_secmark_compute_secid(struct nft_secmark *priv)
 {
-	u32 tmp_secid = 0;
+	struct lsm_prop tmp_prop;
+	u32 secxa = 0;
 	int err;
 
-	err = security_secctx_to_secid(priv->ctx, strlen(priv->ctx), &tmp_secid);
+	err = security_secctx_to_lsmprop(priv->ctx, strlen(priv->ctx),
+					 &tmp_prop, LSM_ID_UNDEF);
 	if (err)
 		return err;
 
-	if (!tmp_secid)
-		return -ENOENT;
+	err = secxa_from_lsmprop(&tmp_prop, &secxa);
+	if (err)
+		return err;
 
-	err = security_secmark_relabel_packet(tmp_secid);
+	err = security_secmark_relabel_packet(&tmp_prop);
 	if (err)
 		return err;
 
-	priv->secid = tmp_secid;
+	priv->secid = secxa;
 	return 0;
 }
 
diff --git a/net/netfilter/xt_SECMARK.c b/net/netfilter/xt_SECMARK.c
index ea67aa92ddc2..05b023a7c576 100644
--- a/net/netfilter/xt_SECMARK.c
+++ b/net/netfilter/xt_SECMARK.c
@@ -43,13 +43,15 @@ secmark_tg(struct sk_buff *skb, const struct xt_secmark_target_info_v1 *info)
 
 static int checkentry_lsm(struct xt_secmark_target_info_v1 *info)
 {
+	struct lsm_prop prop;
 	int err;
 
 	info->secctx[SECMARK_SECCTX_MAX - 1] = '\0';
 	info->secid = 0;
 
-	err = security_secctx_to_secid(info->secctx, strlen(info->secctx),
-				       &info->secid);
+	err = security_secctx_to_lsmprop(info->secctx, strlen(info->secctx),
+					 &prop, LSM_ID_UNDEF);
+
 	if (err) {
 		if (err == -EINVAL)
 			pr_info_ratelimited("invalid security context \'%s\'\n",
@@ -57,18 +59,24 @@ static int checkentry_lsm(struct xt_secmark_target_info_v1 *info)
 		return err;
 	}
 
-	if (!info->secid) {
+	if (!lsmprop_is_set(&prop)) {
 		pr_info_ratelimited("unable to map security context \'%s\'\n",
 				    info->secctx);
 		return -ENOENT;
 	}
 
-	err = security_secmark_relabel_packet(info->secid);
+	err = security_secmark_relabel_packet(&prop);
 	if (err) {
 		pr_info_ratelimited("unable to obtain relabeling permission\n");
 		return err;
 	}
 
+	err = secxa_from_lsmprop(&prop, &info->secid);
+	if (err) {
+		pr_info_ratelimited("unable to obtain secmark\n");
+		return err;
+	}
+
 	security_secmark_refcount_inc();
 	return 0;
 }
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
index cf590dd08540..e26e15c2d947 100644
--- a/security/apparmor/net.c
+++ b/security/apparmor/net.c
@@ -8,6 +8,7 @@
  * Copyright 2009-2017 Canonical Ltd.
  */
 
+#include <linux/lsm_secxa.h>
 #include "include/af_unix.h"
 #include "include/apparmor.h"
 #include "include/audit.h"
@@ -365,12 +366,17 @@ static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid,
 			   struct apparmor_audit_data *ad)
 {
 	int i, ret;
+	struct lsm_prop *prop;
 	struct aa_perms perms = { };
 	struct aa_ruleset *rules = profile->label.rules[0];
 
 	if (rules->secmark_count == 0)
 		return 0;
 
+	ret = secxa_get_lsmprop(&prop, secid);
+	if (ret)
+		return ret;
+
 	for (i = 0; i < rules->secmark_count; i++) {
 		if (!rules->secmark[i].secid) {
 			ret = apparmor_secmark_init(&rules->secmark[i]);
@@ -378,7 +384,7 @@ static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid,
 				return ret;
 		}
 
-		if (rules->secmark[i].secid == secid ||
+		if (rules->secmark[i].secid == prop->apparmor.label->secid ||
 		    rules->secmark[i].secid == AA_SECID_WILDCARD) {
 			if (rules->secmark[i].deny)
 				perms.deny = ALL_PERMS_MASK;
diff --git a/security/lsm_secxa.c b/security/lsm_secxa.c
index 50ce613e35c0..2991aee204df 100644
--- a/security/lsm_secxa.c
+++ b/security/lsm_secxa.c
@@ -99,7 +104,25 @@ EXPORT_SYMBOL(secxa_from_lsmprop);
  */
 void secxa_set_secmark(struct sk_buff *skb, u32 secxa)
 {
-	if (!skb->secmark)
+	struct lsm_prop *olp;
+	struct lsm_prop *nlp;
+	struct lsm_prop prop;
+	u32 tsecxa;
+	int rc;
+
+	if (!skb->secmark) {
 		skb->secmark = secxa;
+		return;
+	}
+
+	olp = xa_load(&secxa_xa, skb->secmark);
+	nlp = xa_load(&secxa_xa, secxa);
+
+	prop = *olp;
+	security_update_lsmprop(&prop, nlp, LSM_ID_UNDEF);
+
+	rc = secxa_from_lsmprop(&prop, &tsecxa);
+	if (!rc)
+		skb->secmark = tsecxa;
 }
 EXPORT_SYMBOL(secxa_set_secmark);
diff --git a/security/security.c b/security/security.c
index 1dec0037370b..e80e7823ce14 100644
--- a/security/security.c
+++ b/security/security.c
@@ -4646,15 +4646,15 @@ EXPORT_SYMBOL(security_inet_conn_established);
 
 /**
  * security_secmark_relabel_packet() - Check if setting a secmark is allowed
- * @secid: new secmark value
+ * @lsmprop: new secmark value
  *
  * Check if the process should be allowed to relabel packets to @secid.
  *
  * Return: Returns 0 if permission is granted.
  */
-int security_secmark_relabel_packet(u32 secid)
+int security_secmark_relabel_packet(struct lsm_prop *prop)
 {
-	return call_int_hook(secmark_relabel_packet, secid);
+	return call_int_hook(secmark_relabel_packet, prop);
 }
 EXPORT_SYMBOL(security_secmark_relabel_packet);
 
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 12614478b638..bf832eff0b92 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -94,6 +94,7 @@
 #include <linux/io_uring/cmd.h>
 #include <uapi/linux/lsm.h>
 #include <linux/memfd.h>
+#include <linux/lsm_secxa.h>
 
 #include "initcalls.h"
 #include "avc.h"
@@ -5414,7 +5415,16 @@ 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,
+		struct lsm_prop *prop;
+		u32 secmark = 0;
+
+		if (skb->secmark) {
+			err = secxa_get_lsmprop(&prop, skb->secmark);
+			if (!err)
+				secmark = prop->selinux.secid;
+		}
+
+		err = avc_has_perm(sk_sid, secmark, SECCLASS_PACKET,
 				   PACKET__RECV, &ad);
 		if (err)
 			return err;
@@ -5483,7 +5493,15 @@ 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,
+		struct lsm_prop *prop;
+		u32 secmark = 0;
+
+		if (skb->secmark) {
+			err = secxa_get_lsmprop(&prop, skb->secmark);
+			if (!err)
+				secmark = prop->selinux.secid;
+		}
+		err = avc_has_perm(sk_sid, secmark, SECCLASS_PACKET,
 				   PACKET__RECV, &ad);
 		if (err)
 			return err;
@@ -5885,10 +5903,10 @@ static void selinux_inet_conn_established(struct sock *sk, struct sk_buff *skb)
 	selinux_skb_peerlbl_sid(skb, family, &sksec->peer_sid);
 }
 
-static int selinux_secmark_relabel_packet(u32 sid)
+static int selinux_secmark_relabel_packet(struct lsm_prop *lsmprop)
 {
-	return avc_has_perm(current_sid(), sid, SECCLASS_PACKET, PACKET__RELABELTO,
-			    NULL);
+	return avc_has_perm(current_sid(), lsmprop->selinux.secid,
+			    SECCLASS_PACKET, PACKET__RELABELTO, NULL);
 }
 
 static void selinux_secmark_refcount_inc(void)
@@ -6016,10 +6034,21 @@ 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) {
+		struct lsm_prop *prop;
+		u32 secmark = 0;
+		int err;
+
+		if (skb->secmark) {
+			err = secxa_get_lsmprop(&prop, skb->secmark);
+			if (!err)
+				secmark = prop->selinux.secid;
+		}
+
+		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
@@ -6093,10 +6122,20 @@ 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()) {
+		struct lsm_prop *prop;
+		u32 secmark = 0;
+		int err;
+
+		if (skb->secmark) {
+			err = secxa_get_lsmprop(&prop, skb->secmark);
+			if (!err)
+				secmark = prop->selinux.secid;
+		}
+		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);
@@ -6215,10 +6254,20 @@ 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) {
+		struct lsm_prop *prop;
+		u32 secmark = 0;
+		int err;
+
+		if (skb->secmark) {
+			err = secxa_get_lsmprop(&prop, skb->secmark);
+			if (!err)
+				secmark = prop->selinux.secid;
+		}
+		if (avc_has_perm(peer_sid, secmark,
 				 SECCLASS_PACKET, secmark_perm, &ad))
 			return NF_DROP_ERR(-ECONNREFUSED);
+	}
 
 	if (peerlbl_active) {
 		u32 if_sid;
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index fcfadd5d9994..79140e6829a4 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -42,6 +42,7 @@
 #include <linux/fs_context.h>
 #include <linux/fs_parser.h>
 #include <linux/watch_queue.h>
+#include <linux/lsm_secxa.h>
 #include <linux/io_uring/cmd.h>
 #include <uapi/linux/lsm.h>
 #include "smack.h"
@@ -4189,10 +4190,17 @@ 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)
 {
+	struct lsm_prop *prop;
+	int rc;
+
 	if (skb == NULL || skb->secmark == 0)
 		return NULL;
 
-	return smack_from_secid(skb->secmark);
+	rc = secxa_get_lsmprop(&prop, skb->secmark);
+	if (prop)
+		return prop->smack.skp;
+
+	return NULL;
 }
 #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 b363c42f252e..0378f74aa22b 100644
--- a/security/smack/smack_netfilter.c
+++ b/security/smack/smack_netfilter.c
@@ -24,13 +24,17 @@ static unsigned int smack_ip_output(void *priv,
 				    const struct nf_hook_state *state)
 {
 	struct sock *sk = skb_to_full_sk(skb);
+	struct lsm_prop prop = { };
 	struct socket_smack *ssp;
-	struct smack_known *skp;
+	u32 secxa;
+	int rc;
 
 	if (sk) {
 		ssp = smack_sock(sk);
-		skp = ssp->smk_out;
-		secxa_set_secmark(skb, skp->smk_secid);
+		prop.smack.skp = ssp->smk_out;
+		rc = secxa_from_lsmprop(&prop, &secxa);
+		if (!rc)
+			secxa_set_secmark(skb, secxa);
 	}
 
 	return NF_ACCEPT;
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH 4/7] SELinux: hooks for secctx_to_lsmprop and update_lsmprop
  2026-08-31 22:37   ` [PATCH 4/7] SELinux: hooks for secctx_to_lsmprop and update_lsmprop Casey Schaufler
@ 2026-08-31 22:55     ` sashiko-bot
  0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-31 22:55 UTC (permalink / raw)
  Cc: selinux

> Implement these hooks.
> 
> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260831223748.4304-1-casey@schaufler-ca.com?part=4


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 1/7] net, smack: Create a function to set secmarks
  2026-08-31 22:37   ` [PATCH 1/7] net, smack: Create a function to set secmarks Casey Schaufler
@ 2026-08-31 22:57     ` sashiko-bot
  0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-31 22:57 UTC (permalink / raw)
  Cc: selinux

> Rather than open coding assignments to skb->secmark, use a helper function
> secxa_set_secmark(). This allows for a case where assigning a secmark
> is more complex than a simple assignment. The version of the function
> here does the legacy simple assignment.
> 
> Change the functions that currently assign values to skb->secmark to
> use this function.
> 
> Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260831223748.4304-1-casey@schaufler-ca.com?part=1


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 2/7] LSM: Implement x array functions for secmarks
  2026-08-31 22:37   ` [PATCH 2/7] LSM: Implement x array functions for secmarks Casey Schaufler
@ 2026-08-31 22:59     ` sashiko-bot
  0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-31 22:59 UTC (permalink / raw)
  Cc: selinux

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Iterating over XArray without holding RCU read lock causes Use-After-Free.
- [High] Uninitialized stack memory compared via `memcmp` leads to unbounded memory leaks.
- [High] Missing validation of xa_load() returns leads to a NULL pointer dereference.
- [Medium] Time-of-check to time-of-use (TOCTOU) race condition allows duplicate entries in the xarray.
- [Low] Syntax error due to literal 'CBS' in source code.
--

commit a6930d2d8e929c19f0987dd477cc0280c1d486a1
Author: Casey Schaufler <casey@schaufler-ca.com>

LSM: Implement x array functions for secmarks

Implement, but don't use (yet) the functions required to use xarray
indexes in secmarks.

> diff --git a/security/lsm_secxa.c b/security/lsm_secxa.c
> new file mode 100644
> index 0000000000000..361ce6770edc4
> --- /dev/null
> +++ b/security/lsm_secxa.c

[ ... ]

> +int secxa_from_lsmprop(struct lsm_prop *prop, u32 *secxa)
> +{
> +	struct lsm_prop *lp;
> +	unsigned long il;
> +	u32 index = 0;
> +	int rc;
> +
> +	xa_for_each(&secxa_xa, il, lp) {

[Severity: High]
Does this iteration need an RCU read lock or the xarray lock? Iterating
over an XArray locklessly without an RCU read lock can lead to a
use-after-free if concurrent calls split or free XArray nodes via RCU.

[Severity: Medium]
Is there a time-of-check to time-of-use (TOCTOU) race condition here?
This code checks for an existing property using xa_for_each(), and if
not found, inserts it later using xa_alloc_bh(). Without holding a lock
that spans both the check and the insertion, two threads could concurrently
observe the property missing and both insert identical properties at
different indices.

> +		if (!memcmp(prop, lp, sizeof(*prop))) {

[Severity: High]
Can this memcmp() over the full sizeof(*prop) cause duplicate entries
and memory leaks? If callers allocate struct lsm_prop on the stack without
zero-initializing the entire structure, this comparison will include
uninitialized padding bytes. This could cause memcmp() to fail to match
existing identical properties, resulting in a new entry allocated for every
lookup and eventually exhausting the index space.

> +			*secxa = il; return 0; CBS }

[Severity: Low]
This isn't a bug in logic, but is this literal CBS a typo? It appears
to be a syntax error that will prevent compilation.

> +	}
> +
> +	lp = kzalloc(sizeof(*lp), GFP_ATOMIC);
> +	if (!lp)
> +		return -ENOMEM;
> +	*lp = *prop;
> +
> +	rc = xa_alloc_bh(&secxa_xa, &index, lp, xa_limit_31b, GFP_ATOMIC);
> +	if (rc) {
> +		kfree(lp);
> +		return -EINVAL;
> +	}
> +
> +	*secxa = index; return 0; CBS

[Severity: Low]
This isn't a bug in logic, but again, does this literal CBS cause a
compilation failure?

> +}
> +EXPORT_SYMBOL(secxa_from_lsmprop);
> +
> +/**
> + * secxa_set_secmark - add LSM information to a secmark
> + * @skb: buffer with the secmark
> + * @secxa: index of the information to add
> + *
> + * If the secmark in @skb is not set, set it to @secxa.
> + */
> +void secxa_set_secmark(struct sk_buff *skb, u32 secxa)
> +{

[Severity: High]
Does this function properly validate xa_load() returns before dereferencing
them?

If the code evaluates:

    olp = xa_load(&secxa_xa, skb->secmark);
    prop = *olp;

Unconditionally dereferencing the pointer can lead to a kernel panic.
This can happen if an invalid or unmapped secmark is present on the packet,
such as one restored from a connection tracking entry synced from another
node.

> +	if (!skb->secmark)
> +		skb->secmark = secxa;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260831223748.4304-1-casey@schaufler-ca.com?part=2

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 6/7] Apparmor: hooks for secctx_to_lsmprop and update_lsmprop
  2026-08-31 22:37   ` [PATCH 6/7] Apparmor: " Casey Schaufler
@ 2026-08-31 23:00     ` sashiko-bot
  0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-31 23:00 UTC (permalink / raw)
  Cc: selinux

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] apparmor_secctx_to_lsmprop and apparmor_secctx_to_secid leak aa_label reference counts.
--

commit 571cc7b76439569c095b0f5a60fbb3a11fc2aa6b
Author: Casey Schaufler <casey@schaufler-ca.com>

Apparmor: hooks for secctx_to_lsmprop and update_lsmprop
    
Implement the secctx_to_lsmprop and update_lsmprop hooks for AppArmor.

> diff --git a/security/apparmor/secid.c b/security/apparmor/secid.c
> index 28caf66b90335..d35fdbf074e05 100644
> --- a/security/apparmor/secid.c
> +++ b/security/apparmor/secid.c
> @@ -106,6 +106,27 @@ int apparmor_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
>  	return 0;
>  }

[Severity: High]
This isn't a bug introduced by this patch, but apparmor_secctx_to_secid()
appears to leak the ref-counted aa_label object. When aa_label_strn_parse()
is called earlier in this function, it returns a label with an incremented
reference count. The secid is extracted, but aa_put_label() is never called
before returning, leaving the reference permanently leaked.

>  
> +int apparmor_secctx_to_lsmprop(const char *secdata, u32 seclen,
> +			       struct lsm_prop *prop)
> +{
> +	struct aa_label *label;
> +
> +	label = aa_label_strn_parse(&root_ns->unconfined->label, secdata,
> +				    seclen, GFP_KERNEL, false, false);
> +	if (IS_ERR(label))
> +		return PTR_ERR(label);
> +	prop->apparmor.label = label;
> +
> +	return 0;
> +}

[Severity: High]
Does this leak the newly acquired label reference?

When security_secctx_to_lsmprop() is called (for example, by
nft_secmark_compute_secid() to create a new nftables SECMARK rule),
aa_label_strn_parse() parses the string and returns an aa_label pointer
with an incremented reference count.

The reference is placed into prop->apparmor.label here, but since the
generic LSM framework treats struct lsm_prop as a transient unmanaged
container and lacks a release_lsmprop hook, this reference is never
dropped. 

If the lsm_prop matches an existing entry in secxa_xa, secxa_from_lsmprop()
simply discards the duplicate prop, permanently leaking the newly acquired
reference. Since this is reachable by unprivileged userspaces configuring
SECMARK rules, could this be abused to cause memory exhaustion?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260831223748.4304-1-casey@schaufler-ca.com?part=6

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 5/7] Smack: hooks for secctx_to_lsmprop and update_lsmprop
  2026-08-31 22:37   ` [PATCH 5/7] Smack: " Casey Schaufler
@ 2026-08-31 23:01     ` sashiko-bot
  0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-31 23:01 UTC (permalink / raw)
  Cc: selinux

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:

New issues:
- [High] `smack_secctx_to_lsmprop` sets `prop->smack.skp` to NULL for unknown contexts but returns success, leading to a later NULL pointer dereference in `smack_lsmprop_to_secctx`.

Pre-existing issues:
- [High] The `smack_secctx_to_lsmprop` function ignores the `seclen` parameter and passes a potentially non-NUL-terminated buffer to `smk_find_entry()`, resulting in an out-of-bounds read.
--

commit 7c2ef4130b3b7eeea3057be8cb9283a398ff6cf7
Author: Casey Schaufler <casey@schaufler-ca.com>

Smack: hooks for secctx_to_lsmprop and update_lsmprop

This commit implements the secctx_to_lsmprop and update_lsmprop hooks for
the Smack LSM module. These hooks provide conversions and updates between
security contexts and lsm_prop structures.

> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index ff115068c5c06..fcfadd5d99941 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
[ ... ]
> @@ -4909,6 +4909,37 @@ static int smack_lsmprop_to_secctx(struct lsm_prop *prop,
>  	return smack_to_secctx(prop->smack.skp, cp);
>  }
>  
> +/**
> + * smack_secctx_to_lsmprop - add the smack label to an lsmprop
> + * @secdata: smack label
> + * @seclen: how long label is
> + * @prop: where to put the result
> + *
> + * Exists for audit and networking code.
> + */
> +static int smack_secctx_to_lsmprop(const char *secdata, u32 seclen,
> +				   struct lsm_prop *prop)
> +{
> +	prop->smack.skp = smk_find_entry(secdata);

[Severity: High]
This isn't a bug introduced by this patch, but does this ignore the seclen
parameter and pass a potentially non-NUL-terminated buffer to
smk_find_entry()?

If secdata comes from a netlink NLA_BINARY attribute and isn't inherently
NUL-terminated, passing it directly to smk_find_entry() will trigger an
out-of-bounds read, because smk_find_entry() in security/smack/smack_access.c
unconditionally calls strlen() on the string. This seems to duplicate a
pre-existing flaw from smack_secctx_to_secid, but re-establishes it in this
new API contract.

> +
> +	return 0;

[Severity: High]
Can smk_find_entry() return NULL for an unknown context, and if so, is it
safe to return 0 (success) here?

If an unknown context is passed in, prop->smack.skp becomes NULL. Because
this returns 0, the caller (such as SECMARK via iptables) might assume the
property is fully populated. Later, if the system tries to convert this
property back to a security context string via smack_lsmprop_to_secctx(), it
passes the NULL pointer to smack_to_secctx():

security/smack/smack_lsm.c:smack_lsmprop_to_secctx() {
    return smack_to_secctx(prop->smack.skp, cp);
}

security/smack/smack_lsm.c:smack_to_secctx() {
    int len = strlen(skp->smk_known);
    ...
}

Will this cause a NULL pointer dereference and kernel panic when skp is NULL?

> +}
[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260831223748.4304-1-casey@schaufler-ca.com?part=5

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 3/7] LSM: Two hooks for manipulating struct lsm_prop
  2026-08-31 22:37   ` [PATCH 3/7] LSM: Two hooks for manipulating struct lsm_prop Casey Schaufler
@ 2026-08-31 23:03     ` sashiko-bot
  0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-31 23:03 UTC (permalink / raw)
  Cc: selinux

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] security_secctx_to_lsmprop() fails to zero-initialize the output `struct lsm_prop` before invoking the LSM hooks, leading to uninitialized memory usage, kernel stack leaks, and memory exhaustion.
--

commit 13e7b169a884dc3bcbf465977a96e0509ce74a40
Author: Casey Schaufler <casey@schaufler-ca.com>

LSM: Two hooks for manipulating struct lsm_prop

This commit introduces security_update_lsmprop() to update a destination
LSM property from a source, and security_secctx_to_lsmprop() to set an
LSM property based on a security context. LSM specific implementations
will follow.

> diff --git a/security/security.c b/security/security.c
> index 71aea8fdf0142..1dec0037370ba 100644
> --- a/security/security.c
> +++ b/security/security.c
[ ... ]
> @@ -3982,6 +3989,31 @@ int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid)
>  }
>  EXPORT_SYMBOL(security_secctx_to_secid);
>  
> +/**
> + * security_secctx_to_lsmprop() - Convert a secctx to a lsmprop
> + * @secdata: secctx
> + * @seclen: length of secctx
> + * @prop: prop
> + * @lsmid: which LSM the context is appropriate to.
> + *
> + * Convert security context to an lsmprop.
> + *
> + * Return: Returns 0 on success, error on failure.
> + */
> +int security_secctx_to_lsmprop(const char *secdata, u32 seclen,
> +			       struct lsm_prop *prop, int lsmid)
> +{
> +	struct lsm_static_call *scall;
> +
> +	lsm_for_each_hook(scall, secctx_to_lsmprop) {

[Severity: High]
Does this code leave prop partially uninitialized?

If callers allocate struct lsm_prop on the stack without zeroing it (such as
in nft_secmark_compute_secid or checkentry_lsm), failing to call
lsmprop_init(prop) here before invoking the LSM hooks might leave stack
garbage in untouched fields.

When a single LSM populates its specific field, the remainder of the struct
could retain this uninitialized data.

Later on, if the caller passes this struct to secxa_from_lsmprop(), the
memcmp used for deduplication will evaluate the entire struct. Because of the
stack garbage, identical security contexts could fail the memcmp check.

This could lead to uninitialized kernel stack data being copied into new heap
allocations in the xarray, and potentially cause unbounded memory consumption
since identical contexts wouldn't be deduplicated as expected.

Should lsmprop_init(prop) be called before iterating over the hooks?

> +		if (lsmid != LSM_ID_UNDEF && lsmid != scall->hl->lsmid->id)
> +			continue;
> +		return scall->hl->hook.secctx_to_lsmprop(secdata, seclen, prop);
> +	}
> +	return LSM_RET_DEFAULT(secctx_to_lsmprop);
> +}
> +EXPORT_SYMBOL(security_secctx_to_lsmprop);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260831223748.4304-1-casey@schaufler-ca.com?part=3

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 7/7] net, lsm: Change skb secmarks to x-array indexes
  2026-08-31 22:37   ` [PATCH 7/7] net, lsm: Change skb secmarks to x-array indexes Casey Schaufler
@ 2026-08-31 23:07     ` sashiko-bot
  0 siblings, 0 replies; 17+ messages in thread
From: sashiko-bot @ 2026-08-31 23:07 UTC (permalink / raw)
  Cc: selinux

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] Uninitialized stack memory in `struct lsm_prop` causes duplicate xarray insertions and potential kernel stack leaks.
- [High] Unconditional NULL pointer dereference in `secxa_set_secmark` on invalid secmarks.
- [High] UAPI Breakage / Type Confusion in Netfilter conntrack secmark dumps.
- [High] Unconditional NULL pointer dereference in AppArmor secmark evaluation.
- [High] Uninitialized pointer dereference in `smack_from_skb` due to ignored error code.
- [Medium] Check-then-act (TOCTOU) race condition in `secxa_from_lsmprop`.
- [Low] Syntax error due to stray `CBS` tokens in `secxa_from_lsmprop`.
--

commit c9d03ed061e68de8297b30e74360121ca809d4cc
Author: Casey Schaufler <casey@schaufler-ca.com>

net, lsm: Change skb secmarks to x-array indexes

This commit changes skb->secmark to store an xarray index representing an
lsm_prop structure instead of a direct LSM secid. It updates various netfilter
modules and LSM hooks to translate between this index and the underlying
security properties.

> Maintain a xarray of lsm_prop structures which represent the
> LSM security information passed via skb->secmark. Pass the xarray
> index of the appropriate lsm_prop (the secxa) instead of an LSM
> specific secid. Allow multiple LSMs to specify their components
> in xarray entries, or create new entries as necessary.
>
> Change uses of security_secctx_to_secid() to security_secctx_to_lsmprop()
> in the netfilter and iptables code. Change security_secmark_relabel_packet()
> to accept an lsm_prop pointer rather than a secid. Change secxa_set_secmark()
> to update and create new entries as necessary.

[Severity: High]
Are there missing updates for connection tracking? The commit updates
netfilter and iptables code, but it appears conntrack dumps via netlink
(ctnetlink_dump_secctx) and procfs (seq_print_acct) still pass ct->secmark
directly to security_secid_to_secctx(). Since the secmark is now an xarray
index rather than an LSM secid, won't this result in invalid memory lookups
and broken userspace dumps?

[ ... ]

> diff --git a/net/netfilter/nft_meta.c b/net/netfilter/nft_meta.c
> index bd0f7a0931f44..664191dfa4b29 100644
> --- a/net/netfilter/nft_meta.c
> +++ b/net/netfilter/nft_meta.c
> @@ -927,21 +927,24 @@ static const struct nla_policy nft_secmark_policy[NFTA_SECMARK_MAX + 1] = {
>  
>  static int nft_secmark_compute_secid(struct nft_secmark *priv)
>  {
> -	u32 tmp_secid = 0;
> +	struct lsm_prop tmp_prop;
> +	u32 secxa = 0;
>  	int err;
>  
> -	err = security_secctx_to_secid(priv->ctx, strlen(priv->ctx), &tmp_secid);
> +	err = security_secctx_to_lsmprop(priv->ctx, strlen(priv->ctx),
> +					 &tmp_prop, LSM_ID_UNDEF);

[Severity: High]
Can this leak uninitialized stack memory into the xarray cache? The tmp_prop
structure is declared on the stack without zeroing. When
security_secctx_to_lsmprop() populates it, it may only write to the specific
active LSM's fields, leaving the padding and other fields uninitialized.
Later, when secxa_from_lsmprop() performs a memcmp on this struct, the
uninitialized stack garbage will prevent matches on identical labels and cause
boundless duplicate entries to be allocated.

[ ... ]

> diff --git a/net/netfilter/xt_SECMARK.c b/net/netfilter/xt_SECMARK.c
> index ea67aa92ddc2f..05b023a7c5760 100644
> --- a/net/netfilter/xt_SECMARK.c
> +++ b/net/netfilter/xt_SECMARK.c
> @@ -43,13 +43,15 @@ secmark_tg(struct sk_buff *skb, const struct xt_secmark_target_info_v1 *info)
>  
>  static int checkentry_lsm(struct xt_secmark_target_info_v1 *info)
>  {
> +	struct lsm_prop prop;
>  	int err;
>  
>  	info->secctx[SECMARK_SECCTX_MAX - 1] = '\0';
>  	info->secid = 0;
>  
> -	err = security_secctx_to_secid(info->secctx, strlen(info->secctx),
> -				       &info->secid);
> +	err = security_secctx_to_lsmprop(info->secctx, strlen(info->secctx),
> +					 &prop, LSM_ID_UNDEF);
> +

[Severity: High]
Similar to the issue in nft_secmark_compute_secid(), does this code also pass
an uninitialized struct lsm_prop to security_secctx_to_lsmprop()? This looks
like it could cause the same memcmp failures and duplicate xarray allocations.

[ ... ]

> diff --git a/security/apparmor/net.c b/security/apparmor/net.c
> index cf590dd08540c..e26e15c2d9473 100644
> --- a/security/apparmor/net.c
> +++ b/security/apparmor/net.c
> @@ -365,12 +366,17 @@ static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid,
>  			   struct apparmor_audit_data *ad)
>  {
[ ... ]
> +	ret = secxa_get_lsmprop(&prop, secid);
> +	if (ret)
> +		return ret;
> +
>  	for (i = 0; i < rules->secmark_count; i++) {
>  		if (!rules->secmark[i].secid) {
>  			ret = apparmor_secmark_init(&rules->secmark[i]);
> @@ -378,7 +384,7 @@ static int aa_secmark_perm(struct aa_profile *profile, u32 request, u32 secid,
>  				return ret;
>  		}
>  
> -		if (rules->secmark[i].secid == secid ||
> +		if (rules->secmark[i].secid == prop->apparmor.label->secid ||

[Severity: High]
Will this unconditionally dereference a NULL pointer if the packet was labeled
by a different LSM? If another LSM (like SELinux) populated the lsm_prop
structure but AppArmor did not, prop->apparmor.label would be NULL, causing
a panic when accessing its secid field.

[ ... ]

> diff --git a/security/lsm_secxa.c b/security/lsm_secxa.c
> index 361ce6770edc4..7ad6a6711f881 100644
> --- a/security/lsm_secxa.c
> +++ b/security/lsm_secxa.c
> @@ -99,7 +99,25 @@ EXPORT_SYMBOL(secxa_from_lsmprop);
>   */
>  void secxa_set_secmark(struct sk_buff *skb, u32 secxa)
>  {
> -	if (!skb->secmark)
> +	struct lsm_prop *olp;
> +	struct lsm_prop *nlp;
> +	struct lsm_prop prop;
> +	u32 tsecxa;
> +	int rc;
> +
> +	if (!skb->secmark) {
>  		skb->secmark = secxa;
> +		return;
> +	}
> +
> +	olp = xa_load(&secxa_xa, skb->secmark);
> +	nlp = xa_load(&secxa_xa, secxa);
> +
> +	prop = *olp;

[Severity: High]
Can xa_load() return NULL here and cause an unconditional NULL pointer
dereference? If network packets arrive with an invalid or untrusted secmark,
the xarray lookup could fail. Does this code need to check if olp and nlp
are valid before dereferencing them?

[ ... ]

> diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
> index fcfadd5d99941..79140e6829a47 100644
> --- a/security/smack/smack_lsm.c
> +++ b/security/smack/smack_lsm.c
> @@ -4189,10 +4190,17 @@ 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)
>  {
> +	struct lsm_prop *prop;
> +	int rc;
> +
>  	if (skb == NULL || skb->secmark == 0)
>  		return NULL;
>  
> -	return smack_from_secid(skb->secmark);
> +	rc = secxa_get_lsmprop(&prop, skb->secmark);
> +	if (prop)
> +		return prop->smack.skp;

[Severity: High]
Does this code ignore the return value of secxa_get_lsmprop() and evaluate
an uninitialized pointer? If the lookup fails, prop remains uninitialized
and checking if (prop) will evaluate stack garbage, potentially leading to
a wild pointer dereference when accessing prop->smack.skp. Should this check
if (!rc) instead?

[ ... ]

Additionally, in the new secxa_from_lsmprop() function introduced in this
series:

[Severity: Low]
Is there a syntax error here that will prevent the kernel from compiling?

security/lsm_secxa.c:secxa_from_lsmprop() {
    ...
    xa_for_each(&secxa_xa, il, lp) {
        if (!memcmp(prop, lp, sizeof(*prop))) {
            *secxa = il; return 0; CBS }
                                   ^^^
The CBS token appears to be a stray undeclared identifier at the end of the
return statement.

[Severity: Medium]
Is there a check-then-act (TOCTOU) race condition during concurrent xarray
insertions?

security/lsm_secxa.c:secxa_from_lsmprop() {
    ...
    xa_for_each(&secxa_xa, il, lp) {
        if (!memcmp(prop, lp, sizeof(*prop))) {
            *secxa = il; return 0;
        }
    }
    ...
    rc = xa_alloc_bh(&secxa_xa, &index, lp, xa_limit_31b, GFP_ATOMIC);
    ...
}

The lockless xa_for_each search and the subsequent xa_alloc_bh insertion
are not atomic. If two threads check for the same property simultaneously,
could they both fail the initial check and blindly insert duplicates into
the xarray cache?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260831223748.4304-1-casey@schaufler-ca.com?part=7

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2026-08-31 23:07 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20260831223748.4304-1-casey.ref@schaufler-ca.com>
2026-08-31 22:37 ` [PATCH 0/7] Change skb secmarks to x-array indexes Casey Schaufler
2026-08-31 22:37   ` [PATCH 1/7] net, smack: Create a function to set secmarks Casey Schaufler
2026-08-31 22:57     ` sashiko-bot
2026-08-31 22:37   ` [PATCH 2/7] LSM: Implement x array functions for secmarks Casey Schaufler
2026-08-31 22:59     ` sashiko-bot
2026-08-31 22:37   ` [PATCH 3/7] LSM: Two hooks for manipulating struct lsm_prop Casey Schaufler
2026-08-31 23:03     ` sashiko-bot
2026-08-31 22:37   ` [PATCH 4/7] SELinux: hooks for secctx_to_lsmprop and update_lsmprop Casey Schaufler
2026-08-31 22:55     ` sashiko-bot
2026-08-31 22:37   ` [PATCH 5/7] Smack: " Casey Schaufler
2026-08-31 23:01     ` sashiko-bot
2026-08-31 22:37   ` [PATCH 6/7] Apparmor: " Casey Schaufler
2026-08-31 23:00     ` sashiko-bot
2026-08-31 22:37   ` [PATCH 7/7] net, lsm: Change skb secmarks to x-array indexes Casey Schaufler
2026-08-31 23:07     ` sashiko-bot
2026-08-13 20:48 [PATCH 0/7] " Casey Schaufler
2026-08-13 20:48 ` [PATCH 7/7] net, lsm: " Casey Schaufler
2026-08-14  2:39   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).