lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250318083422.21489-5-cgoettsche@seltendoof.de>
Date: Tue, 18 Mar 2025 09:33:33 +0100
From: Christian Göttsche <cgoettsche@...tendoof.de>
To: 
Cc: Christian Göttsche <cgzones@...glemail.com>,
	Paul Moore <paul@...l-moore.com>,
	Stephen Smalley <stephen.smalley.work@...il.com>,
	Ondrej Mosnacek <omosnace@...hat.com>,
	Thiébaud Weksteen <tweek@...gle.com>,
	Bram Bonné <brambonne@...gle.com>,
	Casey Schaufler <casey@...aufler-ca.com>,
	Canfeng Guo <guocanfeng@...ontech.com>,
	GUO Zihua <guozihua@...wei.com>,
	selinux@...r.kernel.org,
	linux-kernel@...r.kernel.org
Subject: [RFC PATCH 6/6] selinux: add cache stats for network tables

From: Christian Göttsche <cgzones@...glemail.com>

Export utilization statistics for network object labeling related hash
tables, similar to AVC and SID hash tables, to userspace via new
selinuxfs files under /stats/.

Guard this functionality with a new compile time option
SECURITY_SELINUX_NETTABLE_STATS.

Signed-off-by: Christian Göttsche <cgzones@...glemail.com>
---
 security/selinux/Kconfig           |   8 ++
 security/selinux/ibpkey.c          |  33 +++++++
 security/selinux/include/ibpkey.h  |   5 +
 security/selinux/include/netif.h   |   4 +
 security/selinux/include/netnode.h |   4 +
 security/selinux/include/netport.h |   4 +
 security/selinux/netif.c           |  39 ++++++++
 security/selinux/netnode.c         |  33 +++++++
 security/selinux/netport.c         |  33 +++++++
 security/selinux/selinuxfs.c       | 152 ++++++++++++++++++++++++++++-
 10 files changed, 313 insertions(+), 2 deletions(-)

diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig
index 61abc1e094a8..cae0c7b2c994 100644
--- a/security/selinux/Kconfig
+++ b/security/selinux/Kconfig
@@ -46,6 +46,14 @@ config SECURITY_SELINUX_AVC_STATS
 	  /sys/fs/selinux/avc/cache_stats, which may be monitored via
 	  tools such as avcstat.
 
+config SECURITY_SELINUX_NETTABLE_STATS
+	bool "SELinux Network Hashtable Statistics"
+	depends on SECURITY_SELINUX
+	default y
+	help
+	  This option collects network hash table statistics to
+	  /sys/fs/selinux/stats/.
+
 config SECURITY_SELINUX_SIDTAB_HASH_BITS
 	int "SELinux sidtab hashtable size"
 	depends on SECURITY_SELINUX
diff --git a/security/selinux/ibpkey.c b/security/selinux/ibpkey.c
index 470481cfe0e8..c1ad58297ac4 100644
--- a/security/selinux/ibpkey.c
+++ b/security/selinux/ibpkey.c
@@ -218,6 +218,39 @@ void sel_ib_pkey_flush(void)
 	spin_unlock_irqrestore(&sel_ib_pkey_lock, flags);
 }
 
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+/**
+ * sel_ib_pkey_get_hash_stats - Dump pkey table statistics
+ * @page: the page sized buffer to write to
+ *
+ * Description:
+ * Make the utilization of the hash table available for userspace for
+ * introspection.
+ *
+ */
+int sel_ib_pkey_get_hash_stats(char *page)
+{
+	unsigned int idx, chain_len, max_chain_len = 0, slots_used = 0, total = 0;
+	unsigned long long chain2_len_sum = 0;
+
+	for (idx = 0; idx < SEL_PKEY_HASH_SIZE; idx++) {
+		chain_len = sel_ib_pkey_hash[idx].size;
+
+		if (chain_len > 0)
+			slots_used++;
+		if (chain_len > max_chain_len)
+			max_chain_len = chain_len;
+		total += chain_len;
+		chain2_len_sum += (unsigned long long)chain_len * chain_len;
+	}
+
+	return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n"
+			 "longest chain: %d\nsum of chain length^2: %llu\n",
+			 total, slots_used, SEL_PKEY_HASH_SIZE, max_chain_len,
+			 chain2_len_sum);
+}
+#endif /* CONFIG_SECURITY_SELINUX_NETTABLE_STATS */
+
 static __init int sel_ib_pkey_init(void)
 {
 	int iter;
diff --git a/security/selinux/include/ibpkey.h b/security/selinux/include/ibpkey.h
index 875b055849e1..2d84877fc8c5 100644
--- a/security/selinux/include/ibpkey.h
+++ b/security/selinux/include/ibpkey.h
@@ -20,6 +20,11 @@
 #ifdef CONFIG_SECURITY_INFINIBAND
 void sel_ib_pkey_flush(void);
 int sel_ib_pkey_sid(u64 subnet_prefix, u16 pkey, u32 *sid);
+
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+int sel_ib_pkey_get_hash_stats(char *page);
+#endif
+
 #else
 static inline void sel_ib_pkey_flush(void)
 {
diff --git a/security/selinux/include/netif.h b/security/selinux/include/netif.h
index 2838bdc170dd..7246eb3ebc71 100644
--- a/security/selinux/include/netif.h
+++ b/security/selinux/include/netif.h
@@ -21,4 +21,8 @@ void sel_netif_flush(void);
 
 int sel_netif_sid(struct net *ns, int ifindex, u32 *sid);
 
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+int sel_netif_get_hash_stats(char *page);
+#endif
+
 #endif /* _SELINUX_NETIF_H_ */
diff --git a/security/selinux/include/netnode.h b/security/selinux/include/netnode.h
index e4dc904c3585..897c72b4b664 100644
--- a/security/selinux/include/netnode.h
+++ b/security/selinux/include/netnode.h
@@ -23,4 +23,8 @@ void sel_netnode_flush(void);
 
 int sel_netnode_sid(const void *addr, u16 family, u32 *sid);
 
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+int sel_netnode_get_hash_stats(char *page);
+#endif
+
 #endif
diff --git a/security/selinux/include/netport.h b/security/selinux/include/netport.h
index 9096a8289948..1b9744656616 100644
--- a/security/selinux/include/netport.h
+++ b/security/selinux/include/netport.h
@@ -22,4 +22,8 @@ void sel_netport_flush(void);
 
 int sel_netport_sid(u8 protocol, u16 pnum, u32 *sid);
 
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+int sel_netport_get_hash_stats(char *page);
+#endif
+
 #endif
diff --git a/security/selinux/netif.c b/security/selinux/netif.c
index 2ab7fe9e1ea2..f7bdf75f871e 100644
--- a/security/selinux/netif.c
+++ b/security/selinux/netif.c
@@ -250,6 +250,45 @@ void sel_netif_flush(void)
 	spin_unlock_bh(&sel_netif_lock);
 }
 
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+/**
+ * sel_netif_get_hash_stats - Dump network interface table statistics
+ * @page: the page sized buffer to write to
+ *
+ * Description:
+ * Make the utilization of the hash table available for userspace for
+ * introspection.
+ *
+ */
+int sel_netif_get_hash_stats(char *page)
+{
+	unsigned int idx, chain_len, max_chain_len = 0, slots_used = 0, total = 0;
+	unsigned long long chain2_len_sum = 0;
+	const struct sel_netif *netif;
+
+	rcu_read_lock();
+	for (idx = 0; idx < SEL_NETIF_HASH_SIZE; idx++) {
+		chain_len = 0;
+
+		list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list)
+			chain_len++;
+
+		if (chain_len > 0)
+			slots_used++;
+		if (chain_len > max_chain_len)
+			max_chain_len = chain_len;
+		total += chain_len;
+		chain2_len_sum += (unsigned long long)chain_len * chain_len;
+	}
+	rcu_read_unlock();
+
+	return scnprintf(page, PAGE_SIZE, "entries: %d/%d\nbuckets used: %d/%d\n"
+			 "longest chain: %d\nsum of chain length^2: %llu\n",
+			 total, SEL_NETIF_HASH_MAX, slots_used, SEL_NETIF_HASH_SIZE,
+			 max_chain_len, chain2_len_sum);
+}
+#endif /* CONFIG_SECURITY_SELINUX_NETTABLE_STATS */
+
 static int sel_netif_netdev_notifier_handler(struct notifier_block *this,
 					     unsigned long event, void *ptr)
 {
diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c
index 15fdf385062e..0e380ee82eb2 100644
--- a/security/selinux/netnode.c
+++ b/security/selinux/netnode.c
@@ -290,6 +290,39 @@ void sel_netnode_flush(void)
 	spin_unlock_bh(&sel_netnode_lock);
 }
 
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+/**
+ * sel_netnode_get_hash_stats - Dump network address table statistics
+ * @page: the page sized buffer to write to
+ *
+ * Description:
+ * Make the utilization of the hash table available for userspace for
+ * introspection.
+ *
+ */
+int sel_netnode_get_hash_stats(char *page)
+{
+	unsigned int idx, chain_len, max_chain_len = 0, slots_used = 0, total = 0;
+	unsigned long long chain2_len_sum = 0;
+
+	for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) {
+		chain_len = sel_netnode_hash[idx].size;
+
+		if (chain_len > 0)
+			slots_used++;
+		if (chain_len > max_chain_len)
+			max_chain_len = chain_len;
+		total += chain_len;
+		chain2_len_sum += (unsigned long long)chain_len * chain_len;
+	}
+
+	return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n"
+			 "longest chain: %d\nsum of chain length^2: %llu\n",
+			 total, slots_used, SEL_NETNODE_HASH_SIZE, max_chain_len,
+			 chain2_len_sum);
+}
+#endif /* CONFIG_SECURITY_SELINUX_NETTABLE_STATS */
+
 static __init int sel_netnode_init(void)
 {
 	int iter;
diff --git a/security/selinux/netport.c b/security/selinux/netport.c
index 648c2bce83a7..2a315dcc4344 100644
--- a/security/selinux/netport.c
+++ b/security/selinux/netport.c
@@ -224,6 +224,39 @@ void sel_netport_flush(void)
 	spin_unlock_bh(&sel_netport_lock);
 }
 
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+/**
+ * sel_netport_get_hash_stats - Dump network port table statistics
+ * @page: the page sized buffer to write to
+ *
+ * Description:
+ * Make the utilization of the hash table available for userspace for
+ * introspection.
+ *
+ */
+int sel_netport_get_hash_stats(char *page)
+{
+	unsigned int idx, chain_len, max_chain_len = 0, slots_used = 0, total = 0;
+	unsigned long long chain2_len_sum = 0;
+
+	for (idx = 0; idx < SEL_NETPORT_HASH_SIZE; idx++) {
+		chain_len = sel_netport_hash[idx].size;
+
+		if (chain_len > 0)
+			slots_used++;
+		if (chain_len > max_chain_len)
+			max_chain_len = chain_len;
+		total += chain_len;
+		chain2_len_sum += (unsigned long long)chain_len * chain_len;
+	}
+
+	return scnprintf(page, PAGE_SIZE, "entries: %d\nbuckets used: %d/%d\n"
+			 "longest chain: %d\nsum of chain length^2: %llu\n",
+			 total, slots_used, SEL_NETPORT_HASH_SIZE, max_chain_len,
+			 chain2_len_sum);
+}
+#endif /* CONFIG_SECURITY_SELINUX_NETTABLE_STATS */
+
 static __init int sel_netport_init(void)
 {
 	int iter;
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 47480eb2189b..815c509a633b 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -42,6 +42,10 @@
 #include "objsec.h"
 #include "conditional.h"
 #include "ima.h"
+#include "ibpkey.h"
+#include "netif.h"
+#include "netnode.h"
+#include "netport.h"
 
 enum sel_inos {
 	SEL_ROOT_INO = 2,
@@ -1619,6 +1623,138 @@ static int sel_make_avc_files(struct dentry *dir)
 	return 0;
 }
 
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+static ssize_t sel_read_netif_stats(struct file *filp, char __user *buf,
+				    size_t count, loff_t *ppos)
+{
+	char *page;
+	ssize_t length;
+
+	page = (char *)__get_free_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	length = sel_netif_get_hash_stats(page);
+	if (length >= 0)
+		length = simple_read_from_buffer(buf, count, ppos, page, length);
+	free_page((unsigned long)page);
+
+	return length;
+}
+
+static const struct file_operations sel_netif_stats_ops = {
+	.read		= sel_read_netif_stats,
+	.llseek		= generic_file_llseek,
+};
+
+static ssize_t sel_read_netnode_stats(struct file *filp, char __user *buf,
+				      size_t count, loff_t *ppos)
+{
+	char *page;
+	ssize_t length;
+
+	page = (char *)__get_free_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	length = sel_netnode_get_hash_stats(page);
+	if (length >= 0)
+		length = simple_read_from_buffer(buf, count, ppos, page, length);
+	free_page((unsigned long)page);
+
+	return length;
+}
+
+static const struct file_operations sel_netnode_stats_ops = {
+	.read		= sel_read_netnode_stats,
+	.llseek		= generic_file_llseek,
+};
+
+static ssize_t sel_read_netport_stats(struct file *filp, char __user *buf,
+				      size_t count, loff_t *ppos)
+{
+	char *page;
+	ssize_t length;
+
+	page = (char *)__get_free_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	length = sel_netport_get_hash_stats(page);
+	if (length >= 0)
+		length = simple_read_from_buffer(buf, count, ppos, page, length);
+	free_page((unsigned long)page);
+
+	return length;
+}
+
+static const struct file_operations sel_netport_stats_ops = {
+	.read		= sel_read_netport_stats,
+	.llseek		= generic_file_llseek,
+};
+
+#ifdef CONFIG_SECURITY_INFINIBAND
+static ssize_t sel_read_ib_pkey_stats(struct file *filp, char __user *buf,
+				      size_t count, loff_t *ppos)
+{
+	char *page;
+	ssize_t length;
+
+	page = (char *)__get_free_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	length = sel_ib_pkey_get_hash_stats(page);
+	if (length >= 0)
+		length = simple_read_from_buffer(buf, count, ppos, page, length);
+	free_page((unsigned long)page);
+
+	return length;
+}
+
+static const struct file_operations sel_ib_pkey_stats_ops = {
+	.read		= sel_read_ib_pkey_stats,
+	.llseek		= generic_file_llseek,
+};
+#endif /* CONFIG_SECURITY_INFINIBAND */
+
+static int sel_make_stats_files(struct dentry *dir)
+{
+	struct super_block *sb = dir->d_sb;
+	struct selinux_fs_info *fsi = sb->s_fs_info;
+	unsigned int i;
+	static const struct tree_descr files[] = {
+		{ "netif_hash_stats", &sel_netif_stats_ops, 0444 },
+		{ "netnode_hash_stats", &sel_netnode_stats_ops, 0444 },
+		{ "netport_hash_stats", &sel_netport_stats_ops, 0444 },
+#ifdef CONFIG_SECURITY_INFINIBAND
+		{ "ibpkey_hash_stats", &sel_ib_pkey_stats_ops, 0444 },
+#endif
+	};
+
+	for (i = 0; i < ARRAY_SIZE(files); i++) {
+		struct inode *inode;
+		struct dentry *dentry;
+
+		dentry = d_alloc_name(dir, files[i].name);
+		if (!dentry)
+			return -ENOMEM;
+
+		inode = sel_make_inode(dir->d_sb, S_IFREG|files[i].mode);
+		if (!inode) {
+			dput(dentry);
+			return -ENOMEM;
+		}
+
+		inode->i_fop = files[i].ops;
+		inode->i_ino = ++fsi->last_ino;
+		d_add(dentry, inode);
+	}
+
+	return 0;
+}
+#endif /* CONFIG_SECURITY_SELINUX_NETTABLE_STATS */
+
 static int sel_make_ss_files(struct dentry *dir)
 {
 	struct super_block *sb = dir->d_sb;
@@ -2051,6 +2187,18 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
 	if (ret)
 		goto err;
 
+#ifdef CONFIG_SECURITY_SELINUX_NETTABLE_STATS
+	dentry = sel_make_dir(sb->s_root, "stats", &fsi->last_ino);
+	if (IS_ERR(dentry)) {
+		ret = PTR_ERR(dentry);
+		goto err;
+	}
+
+	ret = sel_make_stats_files(dentry);
+	if (ret)
+		goto err;
+#endif /* CONFIG_SECURITY_SELINUX_NETTABLE_STATS */
+
 	dentry = sel_make_dir(sb->s_root, "ss", &fsi->last_ino);
 	if (IS_ERR(dentry)) {
 		ret = PTR_ERR(dentry);
@@ -2094,8 +2242,8 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
 
 	return 0;
 err:
-	pr_err("SELinux: %s:  failed while creating inodes\n",
-		__func__);
+	pr_err("SELinux: %s:  failed while creating inodes:  %d\n",
+		__func__, ret);
 
 	selinux_fs_info_free(sb);
 
-- 
2.49.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ