summary refs log tree commit diff
path: root/drivers/counter/counter-sysfs.c
diff options
context:
space:
mode:
authorWilliam Breathitt Gray <vilhelm.gray@gmail.com>2021-10-21 19:35:40 +0900
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2021-10-21 13:02:47 +0200
commit8ac33b8b6841e99a624ace543d92cbf598a91381 (patch)
treebfbb42f1f6f41bc9fb8cae0098746d382196ad67 /drivers/counter/counter-sysfs.c
parent310e75c72fefa3b0b4535f669c8b37c963a2dba5 (diff)
downloadlinux-8ac33b8b6841e99a624ace543d92cbf598a91381.tar.gz
counter: Fix use-after-free race condition for events_queue_size write
A race condition is possible when writing to events_queue_size where the
events kfifo is freed during the execution of a kfifo_in(), resulting in
a use-after-free. This patch prevents such a scenario by protecting the
events queue in operation with a spinlock and locking before performing
the events queue size adjustment.

The existing events_lock mutex is renamed to events_out_lock to reflect
that it only protects events queue out operations. Because the events
queue in operations can occur in an interrupt context, a new
events_in_lock spinlock is introduced and utilized.

Fixes: feff17a550c7 ("counter: Implement events_queue_size sysfs attribute")
Cc: David Lechner <david@lechnology.com>
Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
Link: https://lore.kernel.org/r/20211021103540.955639-1-vilhelm.gray@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/counter/counter-sysfs.c')
-rw-r--r--drivers/counter/counter-sysfs.c7
1 files changed, 7 insertions, 0 deletions
diff --git a/drivers/counter/counter-sysfs.c b/drivers/counter/counter-sysfs.c
index 67a988851657..7cc4d1d523ea 100644
--- a/drivers/counter/counter-sysfs.c
+++ b/drivers/counter/counter-sysfs.c
@@ -11,6 +11,8 @@
 #include <linux/kfifo.h>
 #include <linux/kstrtox.h>
 #include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
 #include <linux/string.h>
 #include <linux/sysfs.h>
 #include <linux/types.h>
@@ -796,6 +798,7 @@ static int counter_events_queue_size_write(struct counter_device *counter,
 {
 	DECLARE_KFIFO_PTR(events, struct counter_event);
 	int err;
+	unsigned long flags;
 
 	/* Allocate new events queue */
 	err = kfifo_alloc(&events, val, GFP_KERNEL);
@@ -803,8 +806,12 @@ static int counter_events_queue_size_write(struct counter_device *counter,
 		return err;
 
 	/* Swap in new events queue */
+	mutex_lock(&counter->events_out_lock);
+	spin_lock_irqsave(&counter->events_in_lock, flags);
 	kfifo_free(&counter->events);
 	counter->events.kfifo = events.kfifo;
+	spin_unlock_irqrestore(&counter->events_in_lock, flags);
+	mutex_unlock(&counter->events_out_lock);
 
 	return 0;
 }