diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h
index 9463db2dfa9d..ce1c8497dceb 100644
--- a/include/uapi/linux/xattr.h
+++ b/include/uapi/linux/xattr.h
@@ -76,6 +76,9 @@
 #define XATTR_CAPS_SUFFIX "capability"
 #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX

+#define XATTR_BRUTE_SUFFIX "brute"
+#define XATTR_NAME_BRUTE XATTR_SECURITY_PREFIX XATTR_BRUTE_SUFFIX
+
 #define XATTR_POSIX_ACL_ACCESS  "posix_acl_access"
 #define XATTR_NAME_POSIX_ACL_ACCESS XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_ACCESS
 #define XATTR_POSIX_ACL_DEFAULT  "posix_acl_default"
diff --git a/security/brute/brute.c b/security/brute/brute.c
index 0edb89a58ab0..66db0dd0664d 100644
--- a/security/brute/brute.c
+++ b/security/brute/brute.c
@@ -2,8 +2,85 @@

 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

+#include <linux/binfmts.h>
 #include <linux/lsm_hooks.h>
 #include <linux/sysctl.h>
+#include <linux/xattr.h>
+#include <net/ipv6.h>
+#include <net/sock.h>
+
+/**
+ * struct brute_stats - Fork brute force attack statistics.
+ * @faults: Number of crashes.
+ * @nsecs: Last crash timestamp as the number of nanoseconds in the
+ *         International Atomic Time (TAI) reference.
+ * @period: Crash period's moving average.
+ * @flags: Statistics flags as a whole.
+ * @not_allowed: Not allowed executable file flag.
+ * @unused: Remaining unused flags.
+ *
+ * This structure holds the statistical data shared by all the fork hierarchy
+ * processes.
+ */
+struct brute_stats {
+	u32 faults;
+	u64 nsecs;
+	u64 period;
+	union {
+		u8 flags;
+		struct {
+			u8 not_allowed : 1;
+			u8 unused : 7;
+		};
+	};
+};
+
+/**
+ * struct brute_raw_stats - Raw fork brute force attack statistics.
+ * @faults: Number of crashes.
+ * @nsecs: Last crash timestamp as the number of nanoseconds in the
+ *         International Atomic Time (TAI) reference.
+ * @period: Crash period's moving average.
+ * @flags: Statistics flags.
+ *
+ * This structure holds the statistical data on disk as an extended attribute.
+ * Since the filesystems on which extended attributes are stored might also be
+ * used on architectures with a different byte order and machine word size, care
+ * should be taken to store attribute values in an architecture-independent
+ * format.
+ */
+struct brute_raw_stats {
+	__le32 faults;
+	__le64 nsecs;
+	__le64 period;
+	u8 flags;
+} __packed;
+
+/**
+ * brute_get_current_exe_file() - Get the current task's executable file.
+ *
+ * Since all the kernel threads associated with a task share the same executable
+ * file, get the thread group leader's executable file.
+ *
+ * Context: The file must be released via fput().
+ * Return: NULL if the current task has no associated executable file. A pointer
+ *         to the executable file otherwise.
+ */
+static struct file *brute_get_current_exe_file(void)
+{
+	struct task_struct *task = current;
+	struct file *exe_file;
+
+	rcu_read_lock();
+	if (!thread_group_leader(task))
+		task = rcu_dereference(task->group_leader);
+	get_task_struct(task);
+	rcu_read_unlock();
+
+	exe_file = get_task_exe_file(task);
+	put_task_struct(task);
+	return exe_file;
+}

 /**
  * DOC: brute_ema_weight_numerator
@@ -19,6 +96,18 @@ static unsigned int brute_ema_weight_numerator __read_mostly = 7;
  */
 static unsigned int brute_ema_weight_denominator __read_mostly = 10;

+/**
+ * brute_mul_by_ema_weight() - Multiply by EMA weight.
+ * @value: Value to multiply by EMA weight.
+ *
+ * Return: The result of the multiplication operation.
+ */
+static inline u64 brute_mul_by_ema_weight(u64 value)
+{
+	return mul_u64_u32_div(value, brute_ema_weight_numerator,
+			       brute_ema_weight_denominator);
+}
+
 /**
  * DOC: brute_max_faults
  *
@@ -30,6 +119,56 @@ static unsigned int brute_ema_weight_denominator __read_mostly = 10;
  */
 static unsigned int brute_max_faults __read_mostly = 200;

+/**
+ * brute_update_crash_period() - Update the application crash period.
+ * @stats: Statistics that hold the application crash period to update. Cannot
+ *         be NULL.
+ *
+ * The application crash period must be a value that is not prone to change due
+ * to spurious data and follows the real crash period. So, to compute it, the
+ * exponential moving average (EMA) is used.
+ *
+ * This kind of average defines a weight (between 0 and 1) for the new value to
+ * add and applies the remainder of the weight to the current average value.
+ * This way, some spurious data will not excessively modify the average and only
+ * if the new values are persistent, the moving average will tend towards them.
+ *
+ * Mathematically the application crash period's EMA can be expressed as
+ * follows:
+ *
+ * period_ema = period * weight + period_ema * (1 - weight)
+ *
+ * If the operations are applied:
+ *
+ * period_ema = period * weight + period_ema - period_ema * weight
+ *
+ * If the operands are ordered:
+ *
+ * period_ema = period_ema - period_ema * weight + period * weight
+ *
+ * Finally, this formula can be written as follows:
+ *
+ * period_ema -= period_ema * weight;
+ * period_ema += period * weight;
+ */
+static void brute_update_crash_period(struct brute_stats *stats)
+{
+	u64 current_period;
+	u64 now = ktime_get_clocktai_ns();
+
+	if (stats->faults >= (u32)brute_max_faults)
+		return;
+
+	if (stats->nsecs) {
+		current_period = now > stats->nsecs ? now - stats->nsecs : 0;
+		stats->period -= brute_mul_by_ema_weight(stats->period);
+		stats->period += brute_mul_by_ema_weight(current_period);
+	}
+
+	stats->nsecs = now;
+	stats->faults += 1;
+}
+
 /**
  * DOC: brute_min_faults
  *
@@ -51,6 +190,343 @@ static unsigned int brute_min_faults __read_mostly = 5;
  */
 static unsigned int brute_crash_period_threshold __read_mostly = 30;

+/**
+ * brute_attack_running() - Test if a brute force attack is happening.
+ * @stats: Statistical data shared by all the fork hierarchy processes. Cannot
+ *         be NULL.
+ *
+ * The decision if a brute force attack is running is based on the statistical
+ * data shared by all the fork hierarchy processes.
+ *
+ * There are two types of brute force attacks that can be detected using the
+ * statistical data. The first one is a slow brute force attack that is detected
+ * if the maximum number of faults per fork hierarchy is reached. The second
+ * type is a fast brute force attack that is detected if the application crash
+ * period falls below a certain threshold.
+ *
+ * Moreover, it is important to note that no attacks will be detected until a
+ * minimum number of faults have occurred. This allows to have a trend in the
+ * crash period when the EMA is used.
+ *
+ * Return: True if a brute force attack is happening. False otherwise.
+ */
+static bool brute_attack_running(const struct brute_stats *stats)
+{
+	u64 threshold;
+
+	if (stats->faults < (u32)brute_min_faults)
+		return false;
+
+	if (stats->faults >= (u32)brute_max_faults)
+		return true;
+
+	threshold = (u64)brute_crash_period_threshold * (u64)NSEC_PER_SEC;
+	return stats->period < threshold;
+}
+
+/**
+ * brute_print_attack_running() - Warn about a fork brute force attack.
+ */
+static inline void brute_print_attack_running(void)
+{
+	pr_warn("fork brute force attack detected [pid %d: %s]\n", current->pid,
+		current->comm);
+}
+
+/**
+ * brute_get_xattr_stats() - Get the stats from an extended attribute.
+ * @dentry: The dentry of the file to get the extended attribute.
+ * @inode: The inode of the file to get the extended attribute.
+ * @stats: The stats where to store the info obtained from the extended
+ *         attribute. Cannot be NULL.
+ *
+ * Return: An error code if it is not possible to get the statistical data. Zero
+ *         otherwise.
+ */
+static int brute_get_xattr_stats(struct dentry *dentry, struct inode *inode,
+				 struct brute_stats *stats)
+{
+	int rc;
+	struct brute_raw_stats raw_stats;
+
+	rc = __vfs_getxattr(dentry, inode, XATTR_NAME_BRUTE, &raw_stats,
+			    sizeof(raw_stats));
+	if (rc < 0)
+		return rc;
+
+	stats->faults = le32_to_cpu(raw_stats.faults);
+	stats->nsecs = le64_to_cpu(raw_stats.nsecs);
+	stats->period = le64_to_cpu(raw_stats.period);
+	stats->flags = raw_stats.flags;
+	return 0;
+}
+
+/**
+ * brute_set_xattr_stats() - Set the stats to an extended attribute.
+ * @dentry: The dentry of the file to set the extended attribute.
+ * @inode: The inode of the file to set the extended attribute.
+ * @stats: The stats from where to extract the info to set the extended attribute.
+ *         Cannot be NULL.
+ *
+ * Return: An error code if it is not possible to set the statistical data. Zero
+ *         otherwise.
+ */
+static int brute_set_xattr_stats(struct dentry *dentry, struct inode *inode,
+				 const struct brute_stats *stats)
+{
+	struct brute_raw_stats raw_stats;
+
+	raw_stats.faults = cpu_to_le32(stats->faults);
+	raw_stats.nsecs = cpu_to_le64(stats->nsecs);
+	raw_stats.period = cpu_to_le64(stats->period);
+	raw_stats.flags = stats->flags;
+
+	return __vfs_setxattr(&init_user_ns, dentry, inode, XATTR_NAME_BRUTE,
+			      &raw_stats, sizeof(raw_stats), 0);
+}
+
+/**
+ * brute_update_xattr_stats() - Update the stats of a file.
+ * @file: The file that holds the statistical data to update. Cannot be NULL.
+ *
+ * For a correct management of a fork brute force attack it is only necessary to
+ * update the statistics and test if an attack is happening based on these data.
+ * It is important to note that if the file has no stats nothing is updated nor
+ * created. This way, the scenario where an application has not crossed any
+ * privilege boundary is avoided since the existence of the extended attribute
+ * denotes the crossing of bounds.
+ */
+static void brute_update_xattr_stats(const struct file *file)
+{
+	struct dentry *dentry = file_dentry(file);
+	struct inode *inode = file_inode(file);
+	struct brute_stats stats;
+	int rc;
+
+	inode_lock(inode);
+	rc = brute_get_xattr_stats(dentry, inode, &stats);
+	WARN_ON_ONCE(rc && rc != -ENODATA);
+	if (rc) {
+		inode_unlock(inode);
+		return;
+	}
+
+	brute_update_crash_period(&stats);
+	if (brute_attack_running(&stats)) {
+		brute_print_attack_running();
+		stats.not_allowed = true;
+	}
+
+	rc = brute_set_xattr_stats(dentry, inode, &stats);
+	WARN_ON_ONCE(rc);
+	inode_unlock(inode);
+}
+
+/**
+ * brute_reset_stats() - Reset the statistical data.
+ * @stats: Statistics to be reset. Cannot be NULL.
+ */
+static inline void brute_reset_stats(struct brute_stats *stats)
+{
+	memset(stats, 0, sizeof(*stats));
+}
+
+/**
+ * brute_new_xattr_stats() - New statistics for a file.
+ * @file: The file in which to create the new statistical data. Cannot be NULL.
+ *
+ * Only if the file has no statistical data create it. This function will be
+ * called to mark that a privilege boundary has been crossed so, if new stats
+ * are required, they do not contain any useful data. The existence of the
+ * extended attribute denotes the crossing of privilege bounds.
+ *
+ * Return: An error code if it is not possible to get or set the statistical
+ *         data. Zero otherwise.
+ */
+static int brute_new_xattr_stats(const struct file *file)
+{
+	struct dentry *dentry = file_dentry(file);
+	struct inode *inode = file_inode(file);
+	struct brute_stats stats;
+	int rc;
+
+	inode_lock(inode);
+	rc = brute_get_xattr_stats(dentry, inode, &stats);
+	if (rc && rc != -ENODATA)
+		goto unlock;
+
+	if (rc == -ENODATA) {
+		brute_reset_stats(&stats);
+		rc = brute_set_xattr_stats(dentry, inode, &stats);
+		if (rc)
+			goto unlock;
+	}
+
+unlock:
+	inode_unlock(inode);
+	return rc;
+}
+
+/**
+ * brute_current_new_xattr_stats() - New stats for the current task's exe file.
+ *
+ * Return: An error code if it is not possible to get or set the statistical
+ *         data. Zero otherwise.
+ */
+static int brute_current_new_xattr_stats(void)
+{
+	struct file *exe_file;
+	int rc;
+
+	exe_file = brute_get_current_exe_file();
+	if (WARN_ON_ONCE(!exe_file))
+		return -ENOENT;
+
+	rc = brute_new_xattr_stats(exe_file);
+	WARN_ON_ONCE(rc);
+	fput(exe_file);
+	return rc;
+}
+
+/**
+ * brute_signal_from_user() - Test if a signal is coming from userspace.
+ * @siginfo: Contains the signal information.
+ *
+ * To avoid false positives during the attack detection it is necessary to
+ * narrow the possible cases. So, only the signals delivered by the kernel are
+ * taken into account with the exception of the SIGABRT signal since the latter
+ * is used by glibc for stack canary, malloc, etc failures, which may indicate
+ * that a mitigation has been triggered.
+ *
+ * Return: True if the signal is coming from usersapce. False otherwise.
+ */
+static inline bool brute_signal_from_user(const kernel_siginfo_t *siginfo)
+{
+	return siginfo->si_signo == SIGKILL && siginfo->si_code != SIGABRT;
+}
+
+/**
+ * brute_task_fatal_signal() - Target for the task_fatal_signal hook.
+ * @siginfo: Contains the signal information.
+ *
+ * To detect a brute force attack it is necessary, as a first step, to test in
+ * every fatal crash if the signal is delibered by the kernel. If so, update the
+ * statistics and act based on these data.
+ */
+static void brute_task_fatal_signal(const kernel_siginfo_t *siginfo)
+{
+	struct file *exe_file;
+
+	if (brute_signal_from_user(siginfo))
+		return;
+
+	exe_file = brute_get_current_exe_file();
+	if (WARN_ON_ONCE(!exe_file))
+		return;
+
+	brute_update_xattr_stats(exe_file);
+	fput(exe_file);
+}
+
+/**
+ * brute_task_execve() - Target for the bprm_creds_from_file hook.
+ * @bprm: Contains the linux_binprm structure.
+ * @file: Binary that will be executed without an interpreter.
+ *
+ * This hook is useful to mark that a privilege boundary (setuid/setgid process)
+ * has been crossed. This is done based on the "secureexec" flag.
+ *
+ * To be defensive return an error code if it is not possible to get or set the
+ * stats using an extended attribute since this blocks the execution of the
+ * file. This scenario is treated as an attack.
+ *
+ * It is important to note that here the brute_new_xattr_stats function could be
+ * used with a previous test of the secureexec flag. However it is better to use
+ * the basic xattr functions since in a future commit a test if the execution is
+ * allowed (via the brute_stats::not_allowed flag) will be necessary. This way,
+ * the stats of the file will be get only once.
+ *
+ * Return: An error code if it is not possible to get or set the statistical
+ *         data. Zero otherwise.
+ */
+static int brute_task_execve(struct linux_binprm *bprm, struct file *file)
+{
+	struct dentry *dentry = file_dentry(bprm->file);
+	struct inode *inode = file_inode(bprm->file);
+	struct brute_stats stats;
+	int rc;
+
+	inode_lock(inode);
+	rc = brute_get_xattr_stats(dentry, inode, &stats);
+	if (WARN_ON_ONCE(rc && rc != -ENODATA))
+		goto unlock;
+
+	if (rc == -ENODATA && bprm->secureexec) {
+		brute_reset_stats(&stats);
+		rc = brute_set_xattr_stats(dentry, inode, &stats);
+		if (WARN_ON_ONCE(rc))
+			goto unlock;
+	}
+
+	rc = 0;
+unlock:
+	inode_unlock(inode);
+	return rc;
+}
+
+/**
+ * brute_task_change_priv() - Target for the task_fix_setid hooks.
+ * @new: The set of credentials that will be installed.
+ * @old: The set of credentials that are being replaced.
+ * @flags: Contains one of the LSM_SETID_* values.
+ *
+ * This hook is useful to mark that a privilege boundary (privilege changes) has
+ * been crossed.
+ *
+ * Return: An error code if it is not possible to get or set the statistical
+ *         data. Zero otherwise.
+ */
+static int brute_task_change_priv(struct cred *new, const struct cred *old, int flags)
+{
+	return brute_current_new_xattr_stats();
+}
+
+/**
+ * brute_socket_accept() - Target for the socket_accept hook.
+ * @sock: Contains the listening socket structure.
+ * @newsock: Contains the newly created server socket for connection.
+ *
+ * This hook is useful to mark that a privilege boundary (network to local) has
+ * been crossed. This is done only if the listening socket accepts external
+ * connections. The sockets for inter-process communication (IPC) and those that
+ * are listening on loopback addresses are not taken into account.
+ *
+ * Return: An error code if it is not possible to get or set the statistical
+ *         data. Zero otherwise.
+ */
+static int brute_socket_accept(struct socket *sock, struct socket *newsock)
+{
+	struct sock *sk = sock->sk;
+
+	if (sk->sk_family == AF_UNIX || sk->sk_family == AF_NETLINK ||
+	    sk->sk_rcv_saddr == htonl(INADDR_LOOPBACK) ||
+	    ipv6_addr_equal(&sk->sk_v6_rcv_saddr, &in6addr_loopback))
+		return 0;
+
+	return brute_current_new_xattr_stats();
+}
+
+/*
+ * brute_hooks - Targets for the LSM's hooks.
+ */
+static struct security_hook_list brute_hooks[] __lsm_ro_after_init = {
+	LSM_HOOK_INIT(task_fatal_signal, brute_task_fatal_signal),
+	LSM_HOOK_INIT(bprm_creds_from_file, brute_task_execve),
+	LSM_HOOK_INIT(task_fix_setuid, brute_task_change_priv),
+	LSM_HOOK_INIT(task_fix_setgid, brute_task_change_priv),
+	LSM_HOOK_INIT(socket_accept, brute_socket_accept),
+};
+
 #ifdef CONFIG_SYSCTL
 static unsigned int uint_max = UINT_MAX;
 #define SYSCTL_UINT_MAX (&uint_max)
@@ -137,6 +613,8 @@ static inline void brute_init_sysctl(void) { }
 static int __init brute_init(void)
 {
 	pr_info("becoming mindful\n");
+	security_add_hooks(brute_hooks, ARRAY_SIZE(brute_hooks),
+			   KBUILD_MODNAME);
 	brute_init_sysctl();
 	return 0;
 }
