* [PATCH liburcu] Fix lifetime of rcu_barrier()'s completion structure
@ 2014-04-18 20:12 Keir Fraser
0 siblings, 0 replies; 4+ messages in thread
From: Keir Fraser @ 2014-04-18 20:12 UTC (permalink / raw)
To: lttng-dev; +Cc: Paul E. McKenney
[-- Attachment #1: Type: text/plain, Size: 551 bytes --]
[Another attachment, I must sort out to integrate with git send-mail]
rcu_barrier() can return as soon as completion.barrier_count==0, which
frees the completion struct along with the rest of its stack frame. But
its call_rcu callbacks may yet try to read and write completion.futex
via the wake_up function.
Fix this by calloc()ing the completion struct and implementing a
reference count to determine when it is eventually free()d.
This also fixes bug #787, since calloc() initialises all fields of the
structure to zero.
Regards,
Keir
[-- Attachment #2: rcu_barrier_completion_delay_free.patch --]
[-- Type: text/plain, Size: 3216 bytes --]
From aa1d5d0000c6776b0dbf3dbf3681acf6e85b2623 Mon Sep 17 00:00:00 2001
From: Keir Fraser <keir@cohodata.com>
Date: Fri, 18 Apr 2014 20:53:52 +0100
Subject: [PATCH] Do not free the rcu_barrier() completion struct until all
threads are done with it.
It cannot reside on the waiter's stack as rcu_barrier() may return
before the call_rcu handlers have finished checking whether it needs a
futex wakeup. Instead we dynamically allocate the structure and
determine its lifetime with a reference count.
Signed-off-by: Keir Fraser <keir@cohodata.com>
---
urcu-call-rcu-impl.h | 32 ++++++++++++++++++++++++--------
1 file changed, 24 insertions(+), 8 deletions(-)
diff --git a/urcu-call-rcu-impl.h b/urcu-call-rcu-impl.h
index 5c25a75..8e9ab21 100644
--- a/urcu-call-rcu-impl.h
+++ b/urcu-call-rcu-impl.h
@@ -66,6 +66,7 @@ struct call_rcu_data {
struct call_rcu_completion {
int barrier_count;
+ int ref_count;
int32_t futex;
};
@@ -266,6 +267,12 @@ static void call_rcu_completion_wake_up(struct call_rcu_completion *completion)
}
}
+static void call_rcu_completion_put(struct call_rcu_completion *completion)
+{
+ if (!uatomic_add_return(&completion->ref_count, -1))
+ free(completion);
+}
+
/* This is the code run by each call_rcu thread. */
static void *call_rcu_thread(void *arg)
@@ -776,8 +783,9 @@ void _rcu_barrier_complete(struct rcu_head *head)
work = caa_container_of(head, struct call_rcu_completion_work, head);
completion = work->completion;
- uatomic_dec(&completion->barrier_count);
- call_rcu_completion_wake_up(completion);
+ if (!uatomic_add_return(&completion->barrier_count, -1))
+ call_rcu_completion_wake_up(completion);
+ call_rcu_completion_put(completion);
free(work);
}
@@ -787,7 +795,7 @@ void _rcu_barrier_complete(struct rcu_head *head)
void rcu_barrier(void)
{
struct call_rcu_data *crdp;
- struct call_rcu_completion completion;
+ struct call_rcu_completion *completion;
int count = 0;
int was_online;
@@ -809,11 +817,16 @@ void rcu_barrier(void)
goto online;
}
+ completion = calloc(sizeof(*completion), 1);
+ if (!completion)
+ urcu_die(errno);
+
call_rcu_lock(&call_rcu_mutex);
cds_list_for_each_entry(crdp, &call_rcu_data_list, list)
count++;
- completion.barrier_count = count;
+ completion->ref_count = count + 1;
+ completion->barrier_count = count;
cds_list_for_each_entry(crdp, &call_rcu_data_list, list) {
struct call_rcu_completion_work *work;
@@ -821,20 +834,23 @@ void rcu_barrier(void)
work = calloc(sizeof(*work), 1);
if (!work)
urcu_die(errno);
- work->completion = &completion;
+ work->completion = completion;
_call_rcu(&work->head, _rcu_barrier_complete, crdp);
}
call_rcu_unlock(&call_rcu_mutex);
/* Wait for them */
for (;;) {
- uatomic_dec(&completion.futex);
+ uatomic_dec(&completion->futex);
/* Decrement futex before reading barrier_count */
cmm_smp_mb();
- if (!uatomic_read(&completion.barrier_count))
+ if (!uatomic_read(&completion->barrier_count))
break;
- call_rcu_completion_wait(&completion);
+ call_rcu_completion_wait(completion);
}
+
+ call_rcu_completion_put(completion);
+
online:
if (was_online)
rcu_thread_online();
--
1.8.3.2
[-- Attachment #3: Type: text/plain, Size: 155 bytes --]
_______________________________________________
lttng-dev mailing list
lttng-dev@lists.lttng.org
http://lists.lttng.org/cgi-bin/mailman/listinfo/lttng-dev
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH liburcu] Fix lifetime of rcu_barrier()'s completion structure
[not found] ` <53535CB2.2030407@cohodata.com>
@ 2014-04-20 16:26 ` Mathieu Desnoyers
0 siblings, 0 replies; 4+ messages in thread
From: Mathieu Desnoyers @ 2014-04-20 16:26 UTC (permalink / raw)
To: Keir Fraser; +Cc: lttng-dev, Paul E. McKenney
----- Original Message -----
> From: "Keir Fraser" <keir@cohodata.com>
> To: "Mathieu Desnoyers" <mathieu.desnoyers@efficios.com>
> Cc: lttng-dev@lists.lttng.org, "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
> Sent: Sunday, April 20, 2014 1:35:46 AM
> Subject: Re: [PATCH liburcu] Fix lifetime of rcu_barrier()'s completion structure
>
>
>
> Mathieu Desnoyers wrote:
> > ----- Original Message -----
> >> From: "Keir Fraser"<keir@cohodata.com>
> >> To: lttng-dev@lists.lttng.org
> >> Cc: "Mathieu Desnoyers"<mathieu.desnoyers@efficios.com>, "Paul E.
> >> McKenney"<paulmck@linux.vnet.ibm.com>
> >> Sent: Friday, April 18, 2014 4:12:49 PM
> >> Subject: [PATCH liburcu] Fix lifetime of rcu_barrier()'s completion
> >> structure
> >>
> >> [Another attachment, I must sort out to integrate with git send-mail]
> >>
> >> rcu_barrier() can return as soon as completion.barrier_count==0, which
> >> frees the completion struct along with the rest of its stack frame. But
> >> its call_rcu callbacks may yet try to read and write completion.futex
> >> via the wake_up function.
> >>
> >> Fix this by calloc()ing the completion struct and implementing a
> >> reference count to determine when it is eventually free()d.
> >>
> >> This also fixes bug #787, since calloc() initialises all fields of the
> >> structure to zero.
> >
> > I slightly edited your patch to use urcu_ref() and uatomic_sub_return()
> > (simple style fix). Please let me know if you are OK with the attached
> > patch.
>
> Yes that's better, in particular I don't know how I missed the existence
> of uatomic_sub_return(). Very happy with that, thanks.
No worries. As far as I am concerned, I'm still wondering how I missed the
uninitialized variable and use-after-free in rcu_barrier(). ;-)
It's now merged into master and stable-0.8.
I opened the following bug tracker entry to track this issue:
https://bugs.lttng.org/issues/788
Thanks,
Mathieu
>
> -- Keir
>
> > Thanks!
> >
> > Mathieu
> >
>
--
Mathieu Desnoyers
EfficiOS Inc.
http://www.efficios.com
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH liburcu] Fix lifetime of rcu_barrier()'s completion structure
[not found] ` <2068111068.4340.1397937819635.JavaMail.zimbra@efficios.com>
@ 2014-04-20 5:35 ` Keir Fraser
[not found] ` <53535CB2.2030407@cohodata.com>
1 sibling, 0 replies; 4+ messages in thread
From: Keir Fraser @ 2014-04-20 5:35 UTC (permalink / raw)
To: Mathieu Desnoyers; +Cc: lttng-dev, Paul E. McKenney
Mathieu Desnoyers wrote:
> ----- Original Message -----
>> From: "Keir Fraser"<keir@cohodata.com>
>> To: lttng-dev@lists.lttng.org
>> Cc: "Mathieu Desnoyers"<mathieu.desnoyers@efficios.com>, "Paul E. McKenney"<paulmck@linux.vnet.ibm.com>
>> Sent: Friday, April 18, 2014 4:12:49 PM
>> Subject: [PATCH liburcu] Fix lifetime of rcu_barrier()'s completion structure
>>
>> [Another attachment, I must sort out to integrate with git send-mail]
>>
>> rcu_barrier() can return as soon as completion.barrier_count==0, which
>> frees the completion struct along with the rest of its stack frame. But
>> its call_rcu callbacks may yet try to read and write completion.futex
>> via the wake_up function.
>>
>> Fix this by calloc()ing the completion struct and implementing a
>> reference count to determine when it is eventually free()d.
>>
>> This also fixes bug #787, since calloc() initialises all fields of the
>> structure to zero.
>
> I slightly edited your patch to use urcu_ref() and uatomic_sub_return()
> (simple style fix). Please let me know if you are OK with the attached
> patch.
Yes that's better, in particular I don't know how I missed the existence
of uatomic_sub_return(). Very happy with that, thanks.
-- Keir
> Thanks!
>
> Mathieu
>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH liburcu] Fix lifetime of rcu_barrier()'s completion structure
[not found] <53518741.3060906@cohodata.com>
@ 2014-04-19 20:03 ` Mathieu Desnoyers
[not found] ` <2068111068.4340.1397937819635.JavaMail.zimbra@efficios.com>
1 sibling, 0 replies; 4+ messages in thread
From: Mathieu Desnoyers @ 2014-04-19 20:03 UTC (permalink / raw)
To: Keir Fraser; +Cc: lttng-dev, Paul E. McKenney
[-- Attachment #1: Type: text/plain, Size: 1117 bytes --]
----- Original Message -----
> From: "Keir Fraser" <keir@cohodata.com>
> To: lttng-dev@lists.lttng.org
> Cc: "Mathieu Desnoyers" <mathieu.desnoyers@efficios.com>, "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
> Sent: Friday, April 18, 2014 4:12:49 PM
> Subject: [PATCH liburcu] Fix lifetime of rcu_barrier()'s completion structure
>
> [Another attachment, I must sort out to integrate with git send-mail]
>
> rcu_barrier() can return as soon as completion.barrier_count==0, which
> frees the completion struct along with the rest of its stack frame. But
> its call_rcu callbacks may yet try to read and write completion.futex
> via the wake_up function.
>
> Fix this by calloc()ing the completion struct and implementing a
> reference count to determine when it is eventually free()d.
>
> This also fixes bug #787, since calloc() initialises all fields of the
> structure to zero.
I slightly edited your patch to use urcu_ref() and uatomic_sub_return()
(simple style fix). Please let me know if you are OK with the attached
patch.
Thanks!
Mathieu
--
Mathieu Desnoyers
EfficiOS Inc.
http://www.efficios.com
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: fix-rcu-barrier.patch --]
[-- Type: text/x-patch; name=fix-rcu-barrier.patch, Size: 3662 bytes --]
commit 66de1f4e8e636e3ff326b4447b72c82ca78cf98a
Author: Keir Fraser <keir@cohodata.com>
Date: Sat Apr 19 15:59:01 2014 -0400
Fix: Do not free the rcu_barrier() completion struct until all threads are done with it
It cannot reside on the waiter's stack as rcu_barrier() may return
before the call_rcu handlers have finished checking whether it needs a
futex wakeup. Instead we dynamically allocate the structure and
determine its lifetime with a reference count.
Signed-off-by: Keir Fraser <keir@cohodata.com>
[ Edit by Mathieu Desnoyers: use urcu/ref.h. Cleanup: use
uatomic_sub_return() rather than uatomic_add_return() with negative
value. ]
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
diff --git a/urcu-call-rcu-impl.h b/urcu-call-rcu-impl.h
index f0751f5..a55ac08 100644
--- a/urcu-call-rcu-impl.h
+++ b/urcu-call-rcu-impl.h
@@ -42,6 +42,7 @@
#include "urcu/list.h"
#include "urcu/futex.h"
#include "urcu/tls-compat.h"
+#include "urcu/ref.h"
#include "urcu-die.h"
/* Data structure that identifies a call_rcu thread. */
@@ -67,6 +68,7 @@ struct call_rcu_data {
struct call_rcu_completion {
int barrier_count;
int32_t futex;
+ struct urcu_ref ref;
};
struct call_rcu_completion_work {
@@ -769,6 +771,15 @@ void free_all_cpu_call_rcu_data(void)
}
static
+void free_completion(struct urcu_ref *ref)
+{
+ struct call_rcu_completion *completion;
+
+ completion = caa_container_of(ref, struct call_rcu_completion, ref);
+ free(completion);
+}
+
+static
void _rcu_barrier_complete(struct rcu_head *head)
{
struct call_rcu_completion_work *work;
@@ -776,8 +787,9 @@ void _rcu_barrier_complete(struct rcu_head *head)
work = caa_container_of(head, struct call_rcu_completion_work, head);
completion = work->completion;
- uatomic_dec(&completion->barrier_count);
- call_rcu_completion_wake_up(completion);
+ if (!uatomic_sub_return(&completion->barrier_count, 1))
+ call_rcu_completion_wake_up(completion);
+ urcu_ref_put(&completion->ref, free_completion);
free(work);
}
@@ -787,7 +799,7 @@ void _rcu_barrier_complete(struct rcu_head *head)
void rcu_barrier(void)
{
struct call_rcu_data *crdp;
- struct call_rcu_completion completion;
+ struct call_rcu_completion *completion;
int count = 0;
int was_online;
@@ -809,12 +821,17 @@ void rcu_barrier(void)
goto online;
}
+ completion = calloc(sizeof(*completion), 1);
+ if (!completion)
+ urcu_die(errno);
+
call_rcu_lock(&call_rcu_mutex);
cds_list_for_each_entry(crdp, &call_rcu_data_list, list)
count++;
- completion.barrier_count = count;
- completion.futex = 0;
+ /* Referenced by rcu_barrier() and each call_rcu thread. */
+ urcu_ref_set(&completion->ref, count + 1);
+ completion->barrier_count = count;
cds_list_for_each_entry(crdp, &call_rcu_data_list, list) {
struct call_rcu_completion_work *work;
@@ -822,20 +839,23 @@ void rcu_barrier(void)
work = calloc(sizeof(*work), 1);
if (!work)
urcu_die(errno);
- work->completion = &completion;
+ work->completion = completion;
_call_rcu(&work->head, _rcu_barrier_complete, crdp);
}
call_rcu_unlock(&call_rcu_mutex);
/* Wait for them */
for (;;) {
- uatomic_dec(&completion.futex);
+ uatomic_dec(&completion->futex);
/* Decrement futex before reading barrier_count */
cmm_smp_mb();
- if (!uatomic_read(&completion.barrier_count))
+ if (!uatomic_read(&completion->barrier_count))
break;
- call_rcu_completion_wait(&completion);
+ call_rcu_completion_wait(completion);
}
+
+ urcu_ref_put(&completion->ref, free_completion);
+
online:
if (was_online)
rcu_thread_online();
[-- Attachment #3: Type: text/plain, Size: 155 bytes --]
_______________________________________________
lttng-dev mailing list
lttng-dev@lists.lttng.org
http://lists.lttng.org/cgi-bin/mailman/listinfo/lttng-dev
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2014-04-20 16:26 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-04-18 20:12 [PATCH liburcu] Fix lifetime of rcu_barrier()'s completion structure Keir Fraser
[not found] <53518741.3060906@cohodata.com>
2014-04-19 20:03 ` Mathieu Desnoyers
[not found] ` <2068111068.4340.1397937819635.JavaMail.zimbra@efficios.com>
2014-04-20 5:35 ` Keir Fraser
[not found] ` <53535CB2.2030407@cohodata.com>
2014-04-20 16:26 ` Mathieu Desnoyers
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.