diff --git a/security/brute/brute.c b/security/brute/brute.c
index ece876695af1..7ebc8dbf5e86 100644
--- a/security/brute/brute.c
+++ b/security/brute/brute.c
@@ -11,9 +11,14 @@
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/lsm_hooks.h>
+#include <linux/math64.h>
 #include <linux/printk.h>
 #include <linux/refcount.h>
+#include <linux/rwlock.h>
+#include <linux/rwlock_types.h>
 #include <linux/sched.h>
+#include <linux/sched/signal.h>
+#include <linux/sched/task.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
@@ -37,6 +42,11 @@ struct brute_stats {
 	u64 period;
 };

+/*
+ * brute_stats_ptr_lock - Lock to protect the brute_stats structure pointer.
+ */
+static DEFINE_RWLOCK(brute_stats_ptr_lock);
+
 /*
  * brute_blob_sizes - LSM blob sizes.
  *
@@ -74,7 +84,7 @@ static struct brute_stats *brute_new_stats(void)
 {
 	struct brute_stats *stats;

-	stats = kmalloc(sizeof(struct brute_stats), GFP_KERNEL);
+	stats = kmalloc(sizeof(struct brute_stats), GFP_ATOMIC);
 	if (!stats)
 		return NULL;

@@ -99,16 +109,17 @@ static struct brute_stats *brute_new_stats(void)
  * It's mandatory to disable interrupts before acquiring the brute_stats::lock
  * since the task_free hook can be called from an IRQ context during the
  * execution of the task_alloc hook.
+ *
+ * Context: Must be called with interrupts disabled and brute_stats_ptr_lock
+ *          held.
  */
 static void brute_share_stats(struct brute_stats *src,
 			      struct brute_stats **dst)
 {
-	unsigned long flags;
-
-	spin_lock_irqsave(&src->lock, flags);
+	spin_lock(&src->lock);
 	refcount_inc(&src->refc);
 	*dst = src;
-	spin_unlock_irqrestore(&src->lock, flags);
+	spin_unlock(&src->lock);
 }

 /**
@@ -126,26 +137,36 @@ static void brute_share_stats(struct brute_stats *src,
  * this task and the new one being allocated. Otherwise, share the statistics
  * that the current task already has.
  *
+ * It's mandatory to disable interrupts before acquiring brute_stats_ptr_lock
+ * and brute_stats::lock since the task_free hook can be called from an IRQ
+ * context during the execution of the task_alloc hook.
+ *
  * Return: -ENOMEM if the allocation of the new statistics structure fails. Zero
  *         otherwise.
  */
 static int brute_task_alloc(struct task_struct *task, unsigned long clone_flags)
 {
 	struct brute_stats **stats, **p_stats;
+	unsigned long flags;

 	stats = brute_stats_ptr(task);
 	p_stats = brute_stats_ptr(current);
+	write_lock_irqsave(&brute_stats_ptr_lock, flags);

 	if (likely(*p_stats)) {
 		brute_share_stats(*p_stats, stats);
+		write_unlock_irqrestore(&brute_stats_ptr_lock, flags);
 		return 0;
 	}

 	*stats = brute_new_stats();
-	if (!*stats)
+	if (!*stats) {
+		write_unlock_irqrestore(&brute_stats_ptr_lock, flags);
 		return -ENOMEM;
+	}

 	brute_share_stats(*stats, p_stats);
+	write_unlock_irqrestore(&brute_stats_ptr_lock, flags);
 	return 0;
 }

@@ -167,9 +188,9 @@ static int brute_task_alloc(struct task_struct *task, unsigned long clone_flags)
  * only one task (the task that calls the execve function) points to the data.
  * In this case, the previous allocation is used but the statistics are reset.
  *
- * It's mandatory to disable interrupts before acquiring the brute_stats::lock
- * since the task_free hook can be called from an IRQ context during the
- * execution of the bprm_committing_creds hook.
+ * It's mandatory to disable interrupts before acquiring brute_stats_ptr_lock
+ * and brute_stats::lock since the task_free hook can be called from an IRQ
+ * context during the execution of the bprm_committing_creds hook.
  */
 static void brute_task_execve(struct linux_binprm *bprm)
 {
@@ -177,24 +198,33 @@ static void brute_task_execve(struct linux_binprm *bprm)
 	unsigned long flags;

 	stats = brute_stats_ptr(current);
-	if (WARN(!*stats, "No statistical data\n"))
+	read_lock_irqsave(&brute_stats_ptr_lock, flags);
+
+	if (WARN(!*stats, "No statistical data\n")) {
+		read_unlock_irqrestore(&brute_stats_ptr_lock, flags);
 		return;
+	}

-	spin_lock_irqsave(&(*stats)->lock, flags);
+	spin_lock(&(*stats)->lock);

 	if (!refcount_dec_not_one(&(*stats)->refc)) {
 		/* execve call after an execve call */
 		(*stats)->faults = 0;
 		(*stats)->jiffies = get_jiffies_64();
 		(*stats)->period = 0;
-		spin_unlock_irqrestore(&(*stats)->lock, flags);
+		spin_unlock(&(*stats)->lock);
+		read_unlock_irqrestore(&brute_stats_ptr_lock, flags);
 		return;
 	}

 	/* execve call after a fork call */
-	spin_unlock_irqrestore(&(*stats)->lock, flags);
+	spin_unlock(&(*stats)->lock);
+	read_unlock_irqrestore(&brute_stats_ptr_lock, flags);
+
+	write_lock_irqsave(&brute_stats_ptr_lock, flags);
 	*stats = brute_new_stats();
 	WARN(!*stats, "Cannot allocate statistical data\n");
+	write_unlock_irqrestore(&brute_stats_ptr_lock, flags);
 }

 /**
@@ -210,17 +240,446 @@ static void brute_task_free(struct task_struct *task)
 	bool refc_is_zero;

 	stats = brute_stats_ptr(task);
-	if (WARN(!*stats, "No statistical data\n"))
+	read_lock(&brute_stats_ptr_lock);
+
+	if (WARN(!*stats, "No statistical data\n")) {
+		read_unlock(&brute_stats_ptr_lock);
 		return;
+	}

 	spin_lock(&(*stats)->lock);
 	refc_is_zero = refcount_dec_and_test(&(*stats)->refc);
 	spin_unlock(&(*stats)->lock);
+	read_unlock(&brute_stats_ptr_lock);

 	if (refc_is_zero) {
+		write_lock(&brute_stats_ptr_lock);
 		kfree(*stats);
 		*stats = NULL;
+		write_unlock(&brute_stats_ptr_lock);
+	}
+}
+
+/*
+ * BRUTE_EMA_WEIGHT_NUMERATOR - Weight's numerator of EMA.
+ */
+static const u64 BRUTE_EMA_WEIGHT_NUMERATOR = 7;
+
+/*
+ * BRUTE_EMA_WEIGHT_DENOMINATOR - Weight's denominator of EMA.
+ */
+static const u64 BRUTE_EMA_WEIGHT_DENOMINATOR = 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_u64_div_u64(value, BRUTE_EMA_WEIGHT_NUMERATOR,
+				   BRUTE_EMA_WEIGHT_DENOMINATOR);
+}
+
+/*
+ * 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 const unsigned char BRUTE_MAX_FAULTS = 200;
+
+/**
+ * brute_update_crash_period() - Update the application crash period.
+ * @stats: Statistics that hold the application crash period to update.
+ * @now: The current timestamp in jiffies.
+ *
+ * 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;
+ *
+ * The statistics that hold the application crash period to update cannot be
+ * NULL.
+ *
+ * It's mandatory to disable interrupts before acquiring the brute_stats::lock
+ * since the task_free hook can be called from an IRQ context during the
+ * execution of the task_fatal_signal hook.
+ *
+ * Context: Must be called with interrupts disabled and brute_stats_ptr_lock
+ *          held.
+ * Return: The last crash timestamp before updating it.
+ */
+static u64 brute_update_crash_period(struct brute_stats *stats, u64 now)
+{
+	u64 current_period;
+	u64 last_crash_timestamp;
+
+	spin_lock(&stats->lock);
+	current_period = now - stats->jiffies;
+	last_crash_timestamp = stats->jiffies;
+	stats->jiffies = now;
+
+	stats->period -= brute_mul_by_ema_weight(stats->period);
+	stats->period += brute_mul_by_ema_weight(current_period);
+
+	if (stats->faults < BRUTE_MAX_FAULTS)
+		stats->faults += 1;
+
+	spin_unlock(&stats->lock);
+	return last_crash_timestamp;
+}
+
+/*
+ * 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. Moreover, it avoids the scenario where an application
+ * fails quickly from execve system call due to reasons unrelated to a real
+ * attack.
+ */
+static const unsigned char BRUTE_MIN_FAULTS = 5;
+
+/*
+ * BRUTE_CRASH_PERIOD_THRESHOLD - Application crash period threshold.
+ *
+ * The units are expressed in milliseconds.
+ *
+ * A fast brute force attack is detected when the application crash period falls
+ * below this threshold.
+ */
+static const u64 BRUTE_CRASH_PERIOD_THRESHOLD = 30000;
+
+/**
+ * brute_attack_running() - Test if a brute force attack is happening.
+ * @stats: Statistical data shared by all the fork hierarchy processes.
+ *
+ * The decision if a brute force attack is running is based on the statistical
+ * data shared by all the fork hierarchy processes. This statistics cannot be
+ * NULL.
+ *
+ * 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 and also avoids the scenario where an
+ * application fails quickly from execve system call due to reasons unrelated to
+ * a real attack.
+ *
+ * It's mandatory to disable interrupts before acquiring the brute_stats::lock
+ * since the task_free hook can be called from an IRQ context during the
+ * execution of the task_fatal_signal hook.
+ *
+ * Context: Must be called with interrupts disabled and brute_stats_ptr_lock
+ *          held.
+ * Return: True if a brute force attack is happening. False otherwise.
+ */
+static bool brute_attack_running(struct brute_stats *stats)
+{
+	u64 crash_period;
+
+	spin_lock(&stats->lock);
+	if (stats->faults < BRUTE_MIN_FAULTS) {
+		spin_unlock(&stats->lock);
+		return false;
+	}
+
+	if (stats->faults >= BRUTE_MAX_FAULTS) {
+		spin_unlock(&stats->lock);
+		return true;
+	}
+
+	crash_period = jiffies64_to_msecs(stats->period);
+	spin_unlock(&stats->lock);
+
+	return crash_period < BRUTE_CRASH_PERIOD_THRESHOLD;
+}
+
+/**
+ * print_fork_attack_running() - Warn about a fork brute force attack.
+ */
+static inline void print_fork_attack_running(void)
+{
+	pr_warn("Fork brute force attack detected [%s]\n", current->comm);
+}
+
+/**
+ * brute_manage_fork_attack() - Manage a fork brute force attack.
+ * @stats: Statistical data shared by all the fork hierarchy processes.
+ * @now: The current timestamp in jiffies.
+ *
+ * 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.
+ *
+ * The statistical data shared by all the fork hierarchy processes cannot be
+ * NULL.
+ *
+ * It's mandatory to disable interrupts before acquiring the brute_stats::lock
+ * since the task_free hook can be called from an IRQ context during the
+ * execution of the task_fatal_signal hook.
+ *
+ * Context: Must be called with interrupts disabled and brute_stats_ptr_lock
+ *          held.
+ * Return: The last crash timestamp before updating it.
+ */
+static u64 brute_manage_fork_attack(struct brute_stats *stats, u64 now)
+{
+	u64 last_fork_crash;
+
+	last_fork_crash = brute_update_crash_period(stats, now);
+	if (brute_attack_running(stats))
+		print_fork_attack_running();
+
+	return last_fork_crash;
+}
+
+/**
+ * brute_get_exec_stats() - Get the exec statistics.
+ * @stats: When this function is called, this parameter must point to the
+ *         current process' statistical data. When this function returns, this
+ *         parameter points to the parent process' statistics of the fork
+ *         hierarchy that hold the current process' statistics.
+ *
+ * To manage a brute force attack that happens through the execve system call it
+ * is not possible to use the statistical data hold by this process due to these
+ * statistics disappear when this task is finished. In this scenario this data
+ * should be tracked by the statistics of a higher fork hierarchy (the hierarchy
+ * that contains the process that forks before the execve system call).
+ *
+ * To find these statistics the current fork hierarchy must be traversed up
+ * until new statistics are found.
+ *
+ * Context: Must be called with tasklist_lock and brute_stats_ptr_lock held.
+ */
+static void brute_get_exec_stats(struct brute_stats **stats)
+{
+	const struct task_struct *task = current;
+	struct brute_stats **p_stats;
+
+	do {
+		if (!task->real_parent) {
+			*stats = NULL;
+			return;
+		}
+
+		p_stats = brute_stats_ptr(task->real_parent);
+		task = task->real_parent;
+	} while (*stats == *p_stats);
+
+	*stats = *p_stats;
+}
+
+/**
+ * brute_update_exec_crash_period() - Update the exec crash period.
+ * @stats: When this function is called, this parameter must point to the
+ *         current process' statistical data. When this function returns, this
+ *         parameter points to the updated statistics (statistics that track the
+ *         info to manage a brute force attack that happens through the execve
+ *         system call).
+ * @now: The current timestamp in jiffies.
+ * @last_fork_crash: The last fork crash timestamp before updating it.
+ *
+ * If this is the first update of the statistics used to manage a brute force
+ * attack that happens through the execve system call, its last crash timestamp
+ * (the timestamp that shows when the execve was called) cannot be used to
+ * compute the crash period's EMA. Instead, the last fork crash timestamp should
+ * be used (the last crash timestamp of the child fork hierarchy before updating
+ * the crash period). This allows that in a brute force attack that happens
+ * through the fork system call, the exec and fork statistics are the same. In
+ * this situation, the mitigation method will act only in the processes that are
+ * sharing the fork statistics. This way, the process that forked before the
+ * execve system call will not be involved in the mitigation method. In this
+ * scenario, the parent is not responsible of the child's behaviour.
+ *
+ * It's mandatory to disable interrupts before acquiring the brute_stats::lock
+ * since the task_free hook can be called from an IRQ context during the
+ * execution of the task_fatal_signal hook.
+ *
+ * Context: Must be called with interrupts disabled and tasklist_lock and
+ *          brute_stats_ptr_lock held.
+ * Return: -EFAULT if there are no exec statistics. Zero otherwise.
+ */
+static int brute_update_exec_crash_period(struct brute_stats **stats,
+					  u64 now, u64 last_fork_crash)
+{
+	brute_get_exec_stats(stats);
+	if (!*stats)
+		return -EFAULT;
+
+	spin_lock(&(*stats)->lock);
+	if (!(*stats)->faults)
+		(*stats)->jiffies = last_fork_crash;
+	spin_unlock(&(*stats)->lock);
+
+	brute_update_crash_period(*stats, now);
+	return 0;
+}
+
+/**
+ * brute_get_crash_period() - Get the application crash period.
+ * @stats: Statistical data shared by all the fork hierarchy processes.
+ *
+ * The statistical data shared by all the fork hierarchy processes cannot be
+ * NULL.
+ *
+ * It's mandatory to disable interrupts before acquiring the brute_stats::lock
+ * since the task_free hook can be called from an IRQ context during the
+ * execution of the task_fatal_signal hook.
+ *
+ * Context: Must be called with interrupts disabled and brute_stats_ptr_lock
+ *          held.
+ * Return: The application crash period.
+ */
+static u64 brute_get_crash_period(struct brute_stats *stats)
+{
+	u64 crash_period;
+
+	spin_lock(&stats->lock);
+	crash_period = stats->period;
+	spin_unlock(&stats->lock);
+
+	return crash_period;
+}
+
+/**
+ * print_exec_attack_running() - Warn about an exec brute force attack.
+ * @stats: Statistical data shared by all the fork hierarchy processes.
+ *
+ * The statistical data shared by all the fork hierarchy processes cannot be
+ * NULL.
+ *
+ * Before showing the process name it is mandatory to find a process that holds
+ * a pointer to the exec statistics.
+ *
+ * Context: Must be called with tasklist_lock and brute_stats_ptr_lock held.
+ */
+static void print_exec_attack_running(const struct brute_stats *stats)
+{
+	struct task_struct *p;
+	struct brute_stats **p_stats;
+	bool found = false;
+
+	for_each_process(p) {
+		p_stats = brute_stats_ptr(p);
+		if (*p_stats == stats) {
+			found = true;
+			break;
+		}
 	}
+
+	if (WARN(!found, "No exec process\n"))
+		return;
+
+	pr_warn("Exec brute force attack detected [%s]\n", p->comm);
+}
+
+/**
+ * brute_manage_exec_attack() - Manage an exec brute force attack.
+ * @stats: Statistical data shared by all the fork hierarchy processes.
+ * @now: The current timestamp in jiffies.
+ * @last_fork_crash: The last fork crash timestamp before updating it.
+ *
+ * For a correct management of an exec brute force attack it is only necessary
+ * to update the exec statistics and test if an attack is happening based on
+ * these data.
+ *
+ * It is important to note that if the fork and exec crash periods are the same,
+ * the attack test is avoided. This allows that in a brute force attack that
+ * happens through the fork system call, the mitigation method does not act on
+ * the parent process of the fork hierarchy.
+ *
+ * The statistical data shared by all the fork hierarchy processes cannot be
+ * NULL.
+ *
+ * It's mandatory to disable interrupts before acquiring the brute_stats::lock
+ * since the task_free hook can be called from an IRQ context during the
+ * execution of the task_fatal_signal hook.
+ *
+ * Context: Must be called with interrupts disabled and tasklist_lock and
+ *          brute_stats_ptr_lock held.
+ */
+static void brute_manage_exec_attack(struct brute_stats *stats, u64 now,
+				     u64 last_fork_crash)
+{
+	int ret;
+	struct brute_stats *exec_stats = stats;
+	u64 fork_period;
+	u64 exec_period;
+
+	ret = brute_update_exec_crash_period(&exec_stats, now, last_fork_crash);
+	if (WARN(ret, "No exec statistical data\n"))
+		return;
+
+	fork_period = brute_get_crash_period(stats);
+	exec_period = brute_get_crash_period(exec_stats);
+	if (fork_period == exec_period)
+		return;
+
+	if (brute_attack_running(exec_stats))
+		print_exec_attack_running(exec_stats);
+}
+
+/**
+ * brute_task_fatal_signal() - Target for the task_fatal_signal hook.
+ * @siginfo: Contains the signal information.
+ *
+ * To detect a brute force attack is necessary to update the fork and exec
+ * statistics in every fatal crash and act based on these data.
+ *
+ * It's mandatory to disable interrupts before acquiring brute_stats_ptr_lock
+ * and brute_stats::lock since the task_free hook can be called from an IRQ
+ * context during the execution of the task_fatal_signal hook.
+ */
+static void brute_task_fatal_signal(const kernel_siginfo_t *siginfo)
+{
+	struct brute_stats **stats;
+	unsigned long flags;
+	u64 last_fork_crash;
+	u64 now = get_jiffies_64();
+
+	stats = brute_stats_ptr(current);
+	read_lock(&tasklist_lock);
+	read_lock_irqsave(&brute_stats_ptr_lock, flags);
+
+	if (WARN(!*stats, "No statistical data\n")) {
+		read_unlock_irqrestore(&brute_stats_ptr_lock, flags);
+		read_unlock(&tasklist_lock);
+		return;
+	}
+
+	last_fork_crash = brute_manage_fork_attack(*stats, now);
+	brute_manage_exec_attack(*stats, now, last_fork_crash);
+	read_unlock_irqrestore(&brute_stats_ptr_lock, flags);
+	read_unlock(&tasklist_lock);
 }

 /*
@@ -230,6 +689,7 @@ static struct security_hook_list brute_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(task_alloc, brute_task_alloc),
 	LSM_HOOK_INIT(bprm_committing_creds, brute_task_execve),
 	LSM_HOOK_INIT(task_free, brute_task_free),
+	LSM_HOOK_INIT(task_fatal_signal, brute_task_fatal_signal),
 };

 /**
