diff --git a/security/Kconfig b/security/Kconfig
index 0ced7fd33e4d..2df1727f2c2c 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -241,6 +241,7 @@ source "security/lockdown/Kconfig"
 source "security/landlock/Kconfig"

 source "security/integrity/Kconfig"
+source "security/brute/Kconfig"

 choice
 	prompt "First legacy 'major LSM' to be initialized"
@@ -278,11 +279,11 @@ endchoice

 config LSM
 	string "Ordered list of enabled LSMs"
-	default "landlock,lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK
-	default "landlock,lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR
-	default "landlock,lockdown,yama,loadpin,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO
-	default "landlock,lockdown,yama,loadpin,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC
-	default "landlock,lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf"
+	default "landlock,lockdown,brute,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK
+	default "landlock,lockdown,brute,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR
+	default "landlock,lockdown,brute,yama,loadpin,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO
+	default "landlock,lockdown,brute,yama,loadpin,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC
+	default "landlock,lockdown,brute,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf"
 	help
 	  A comma-separated list of LSMs, in initialization order.
 	  Any LSMs left off this list will be ignored. This can be
diff --git a/security/Makefile b/security/Makefile
index 47e432900e24..94d325256413 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -14,6 +14,7 @@ subdir-$(CONFIG_SECURITY_SAFESETID)    += safesetid
 subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM)	+= lockdown
 subdir-$(CONFIG_BPF_LSM)		+= bpf
 subdir-$(CONFIG_SECURITY_LANDLOCK)	+= landlock
+subdir-$(CONFIG_SECURITY_FORK_BRUTE)	+= brute

 # always enable default capabilities
 obj-y					+= commoncap.o
@@ -34,6 +35,7 @@ obj-$(CONFIG_SECURITY_LOCKDOWN_LSM)	+= lockdown/
 obj-$(CONFIG_CGROUPS)			+= device_cgroup.o
 obj-$(CONFIG_BPF_LSM)			+= bpf/
 obj-$(CONFIG_SECURITY_LANDLOCK)		+= landlock/
+obj-$(CONFIG_SECURITY_FORK_BRUTE)	+= brute/

 # Object integrity file lists
 subdir-$(CONFIG_INTEGRITY)		+= integrity
diff --git a/security/brute/Kconfig b/security/brute/Kconfig
new file mode 100644
index 000000000000..5da314d221aa
--- /dev/null
+++ b/security/brute/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+config SECURITY_FORK_BRUTE
+	bool "Fork brute force attack detection and mitigation"
+	depends on SECURITY
+	help
+	  This is an LSM that stops any fork brute force attack against
+	  vulnerable userspace processes. The detection method is based on
+	  the application crash period and as a mitigation procedure all the
+	  offending tasks are killed. Also, the executable file involved in the
+	  attack will be marked as "not allowed" and new execve system calls
+	  using this file will fail. Like capabilities, this security module
+	  stacks with other LSMs.
+
+	  If you are unsure how to answer this question, answer N.
diff --git a/security/brute/Makefile b/security/brute/Makefile
new file mode 100644
index 000000000000..d3f233a132a9
--- /dev/null
+++ b/security/brute/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_SECURITY_FORK_BRUTE) += brute.o
diff --git a/security/brute/brute.c b/security/brute/brute.c
new file mode 100644
index 000000000000..0edb89a58ab0
--- /dev/null
+++ b/security/brute/brute.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/lsm_hooks.h>
+#include <linux/sysctl.h>
+
+/**
+ * DOC: brute_ema_weight_numerator
+ *
+ * Weight's numerator of EMA.
+ */
+static unsigned int brute_ema_weight_numerator __read_mostly = 7;
+
+/**
+ * DOC: brute_ema_weight_denominator
+ *
+ * Weight's denominator of EMA.
+ */
+static unsigned int brute_ema_weight_denominator __read_mostly = 10;
+
+/**
+ * DOC: brute_max_faults
+ *
+ * Maximum number of faults.
+ *
+ * If a brute force attack is running slowly for a long time, the application
+ * crash period's EMA is not suitable for the detection. This type of attack
+ * must be detected using a maximum number of faults.
+ */
+static unsigned int brute_max_faults __read_mostly = 200;
+
+/**
+ * DOC: brute_min_faults
+ *
+ * Minimum number of faults.
+ *
+ * The application crash period's EMA cannot be used until a minimum number of
+ * data has been applied to it. This constraint allows getting a trend when this
+ * moving average is used.
+ */
+static unsigned int brute_min_faults __read_mostly = 5;
+
+/**
+ * DOC: brute_crash_period_threshold
+ *
+ * Application crash period threshold.
+ *
+ * A fast brute force attack is detected when the application crash period falls
+ * below this threshold. The units are expressed in seconds.
+ */
+static unsigned int brute_crash_period_threshold __read_mostly = 30;
+
+#ifdef CONFIG_SYSCTL
+static unsigned int uint_max = UINT_MAX;
+#define SYSCTL_UINT_MAX (&uint_max)
+
+/*
+ * brute_sysctl_path - Sysctl attributes path.
+ */
+static struct ctl_path brute_sysctl_path[] = {
+	{ .procname = "kernel", },
+	{ .procname = "brute", },
+	{ }
+};
+
+/*
+ * brute_sysctl_table - Sysctl attributes.
+ */
+static struct ctl_table brute_sysctl_table[] = {
+	{
+		.procname	= "ema_weight_numerator",
+		.data		= &brute_ema_weight_numerator,
+		.maxlen		= sizeof(brute_ema_weight_numerator),
+		.mode		= 0644,
+		.proc_handler	= proc_douintvec_minmax,
+		.extra1		= SYSCTL_ZERO,
+		.extra2		= &brute_ema_weight_denominator,
+	},
+	{
+		.procname	= "ema_weight_denominator",
+		.data		= &brute_ema_weight_denominator,
+		.maxlen		= sizeof(brute_ema_weight_denominator),
+		.mode		= 0644,
+		.proc_handler	= proc_douintvec_minmax,
+		.extra1		= &brute_ema_weight_numerator,
+		.extra2		= SYSCTL_UINT_MAX,
+	},
+	{
+		.procname	= "max_faults",
+		.data		= &brute_max_faults,
+		.maxlen		= sizeof(brute_max_faults),
+		.mode		= 0644,
+		.proc_handler	= proc_douintvec_minmax,
+		.extra1		= &brute_min_faults,
+		.extra2		= SYSCTL_UINT_MAX,
+	},
+	{
+		.procname	= "min_faults",
+		.data		= &brute_min_faults,
+		.maxlen		= sizeof(brute_min_faults),
+		.mode		= 0644,
+		.proc_handler	= proc_douintvec_minmax,
+		.extra1		= SYSCTL_ONE,
+		.extra2		= &brute_max_faults,
+	},
+	{
+		.procname	= "crash_period_threshold",
+		.data		= &brute_crash_period_threshold,
+		.maxlen		= sizeof(brute_crash_period_threshold),
+		.mode		= 0644,
+		.proc_handler	= proc_douintvec_minmax,
+		.extra1		= SYSCTL_ONE,
+		.extra2		= SYSCTL_UINT_MAX,
+	},
+	{ }
+};
+
+/**
+ * brute_init_sysctl() - Initialize the sysctl interface.
+ */
+static void __init brute_init_sysctl(void)
+{
+	if (!register_sysctl_paths(brute_sysctl_path, brute_sysctl_table))
+		panic("sysctl registration failed\n");
+}
+
+#else
+static inline void brute_init_sysctl(void) { }
+#endif /* CONFIG_SYSCTL */
+
+/**
+ * brute_init() - Initialize the brute LSM.
+ *
+ * Return: Always returns zero.
+ */
+static int __init brute_init(void)
+{
+	pr_info("becoming mindful\n");
+	brute_init_sysctl();
+	return 0;
+}
+
+DEFINE_LSM(brute) = {
+	.name = KBUILD_MODNAME,
+	.init = brute_init,
+};
