diff mbox series

ring-buffer: Fix a race in rb_time_cmpxchg() for 32 bit archs

Message ID 20231212115301.7a9c9a64@gandalf.local.home (mailing list archive)
State Accepted
Commit fff88fa0fbc7067ba46dde570912d63da42c59a9
Headers show
Series ring-buffer: Fix a race in rb_time_cmpxchg() for 32 bit archs | expand

Commit Message

Steven Rostedt Dec. 12, 2023, 4:53 p.m. UTC
From: "Steven Rostedt (Google)" <rostedt@goodmis.org>

Mathieu Desnoyers pointed out an issue in the rb_time_cmpxchg() for 32 bit
architectures. That is:

 static bool rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set)
 {
	unsigned long cnt, top, bottom, msb;
	unsigned long cnt2, top2, bottom2, msb2;
	u64 val;

	/* The cmpxchg always fails if it interrupted an update */
	 if (!__rb_time_read(t, &val, &cnt2))
		 return false;

	 if (val != expect)
		 return false;

<<<< interrupted here!

	 cnt = local_read(&t->cnt);

The problem is that the synchronization counter in the rb_time_t is read
*after* the value of the timestamp is read. That means if an interrupt
were to come in between the value being read and the counter being read,
it can change the value and the counter and the interrupted process would
be clueless about it!

The counter needs to be read first and then the value. That way it is easy
to tell if the value is stale or not. If the counter hasn't been updated,
then the value is still good.

Link: https://lore.kernel.org/linux-trace-kernel/20231211201324.652870-1-mathieu.desnoyers@efficios.com/

Cc: stable@vger.kernel.org
Fixes: 10464b4aa605e ("ring-buffer: Add rb_time_t 64 bit operations for speeding up 32 bit")
Reported-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>
---
 kernel/trace/ring_buffer.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

Comments

Mathieu Desnoyers Dec. 12, 2023, 7:39 p.m. UTC | #1
On 2023-12-12 11:53, Steven Rostedt wrote:
> From: "Steven Rostedt (Google)" <rostedt@goodmis.org>
> 
> Mathieu Desnoyers pointed out an issue in the rb_time_cmpxchg() for 32 bit
> architectures. That is:
> 
>   static bool rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set)
>   {
> 	unsigned long cnt, top, bottom, msb;
> 	unsigned long cnt2, top2, bottom2, msb2;
> 	u64 val;
> 
> 	/* The cmpxchg always fails if it interrupted an update */
> 	 if (!__rb_time_read(t, &val, &cnt2))
> 		 return false;
> 
> 	 if (val != expect)
> 		 return false;
> 
> <<<< interrupted here!
> 
> 	 cnt = local_read(&t->cnt);
> 
> The problem is that the synchronization counter in the rb_time_t is read
> *after* the value of the timestamp is read. That means if an interrupt
> were to come in between the value being read and the counter being read,
> it can change the value and the counter and the interrupted process would
> be clueless about it!
> 
> The counter needs to be read first and then the value. That way it is easy
> to tell if the value is stale or not. If the counter hasn't been updated,
> then the value is still good.
> 
> Link: https://lore.kernel.org/linux-trace-kernel/20231211201324.652870-1-mathieu.desnoyers@efficios.com/
> 
> Cc: stable@vger.kernel.org
> Fixes: 10464b4aa605e ("ring-buffer: Add rb_time_t 64 bit operations for speeding up 32 bit")
> Reported-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
> Signed-off-by: Steven Rostedt (Google) <rostedt@goodmis.org>

Reviewed-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>

> ---
>   kernel/trace/ring_buffer.c | 4 +++-
>   1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
> index 1d9caee7f542..e110cde685ea 100644
> --- a/kernel/trace/ring_buffer.c
> +++ b/kernel/trace/ring_buffer.c
> @@ -706,6 +706,9 @@ static bool rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set)
>   	unsigned long cnt2, top2, bottom2, msb2;
>   	u64 val;
>   
> +	/* Any interruptions in this function should cause a failure */
> +	cnt = local_read(&t->cnt);
> +
>   	/* The cmpxchg always fails if it interrupted an update */
>   	 if (!__rb_time_read(t, &val, &cnt2))
>   		 return false;
> @@ -713,7 +716,6 @@ static bool rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set)
>   	 if (val != expect)
>   		 return false;
>   
> -	 cnt = local_read(&t->cnt);
>   	 if ((cnt & 3) != cnt2)
>   		 return false;
>
diff mbox series

Patch

diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 1d9caee7f542..e110cde685ea 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -706,6 +706,9 @@  static bool rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set)
 	unsigned long cnt2, top2, bottom2, msb2;
 	u64 val;
 
+	/* Any interruptions in this function should cause a failure */
+	cnt = local_read(&t->cnt);
+
 	/* The cmpxchg always fails if it interrupted an update */
 	 if (!__rb_time_read(t, &val, &cnt2))
 		 return false;
@@ -713,7 +716,6 @@  static bool rb_time_cmpxchg(rb_time_t *t, u64 expect, u64 set)
 	 if (val != expect)
 		 return false;
 
-	 cnt = local_read(&t->cnt);
 	 if ((cnt & 3) != cnt2)
 		 return false;