[ovs-dev] [PATCH v2] ovs rcu: update rcu pointer first

Linhaifeng haifeng.lin at huawei.com
Thu Jun 4 01:25:33 UTC 2020



> -----Original Message-----
> From: Ilya Maximets [mailto:i.maximets at ovn.org]
> Sent: Thursday, June 4, 2020 12:01 AM
> To: Ben Pfaff <blp at ovn.org>; Linhaifeng <haifeng.lin at huawei.com>
> Cc: dev at openvswitch.org; Lilijun (Jerry) <jerry.lilijun at huawei.com>; Lichunhe
> <lichunhe at huawei.com>; nd <nd at arm.com>; chenchanghu
> <chenchanghu at huawei.com>; i.maximets at ovn.org
> Subject: Re: [ovs-dev] [PATCH v2] ovs rcu: update rcu pointer first
> 
> On 6/3/20 7:26 AM, Ben Pfaff wrote:
> > On Wed, Jun 03, 2020 at 01:22:52AM +0000, Linhaifeng wrote:
> >>
> >>
> >> -----Original Message-----
> >> From: Ben Pfaff [mailto:blp at ovn.org]
> >> Sent: Wednesday, June 3, 2020 1:28 AM
> >> To: Linhaifeng <haifeng.lin at huawei.com>
> >> Cc: Yanqin Wei <Yanqin.Wei at arm.com>; dev at openvswitch.org; nd
> >> <nd at arm.com>; Lilijun (Jerry) <jerry.lilijun at huawei.com>; chenchanghu
> >> <chenchanghu at huawei.com>; Lichunhe <lichunhe at huawei.com>
> >> Subject: Re: [ovs-dev] [PATCH v2] ovs rcu: update rcu pointer first
> >>
> >> On Tue, Jun 02, 2020 at 07:27:59AM +0000, Linhaifeng wrote:
> >>> We should update rcu pointer first then use ovsrcu_postpone to free
> >>> otherwise maybe cause use-after-free.
> >>> e.g.,reader indicates momentary quiescent and access old pointer
> >>> after writer postpone free old pointer and before setting new pointer.
> >>>
> >>> Signed-off-by: Linhaifeng <haifeng.lin at huawei.com>
> >>
> >> I don't see how that's possible, since the writer hasn't quiesced.
> >>
> >> I think the logic is as follow, Could you help me find out where is incorrect?
> >>
> >> 1.1 -> 1.2 -> 3.1 -> 3.2 -> 2.1 -> 2.2 -> 2.3 -> 2.1 -> 1.3 -> 1.4 ->
> >> 3.3 -> 2.2(use after free)
> >>
> >> wirter:
> >> 1.1 use postone to free old pointer
> >> 1.2 flush cbsets to flushed_cbsets
> >> 1.3 update new pointer
> >> 1.4 quiesced
> >>
> >> Read:
> >> 2.1. read pointer
> >> 2.2. use pointer
> >> 2.3. quiesced
> >>
> >> Rcu:
> >> 3.1 pop flushed_cbsets
> >> 3.2 ovsrcu_synchronize
> >> 3.3 call all cb to free
> >
> > So you're saying this:
> >
> >     1.1 use postone to free old pointer (A)
> >     1.2 flush cbsets to flushed_cbsets
> >
> >     3.1 pop flushed_cbsets
> >     3.2 ovsrcu_synchronize
> >
> >     2.1. read pointer (A)
> >     2.2. use pointer (A)
> >     2.3. quiesced
> >
> >     2.1. read pointer (A)
> 
> IIUC, 'Read' thread should enter quiescent state here at least one more time in
> order to trigger the issue (or, maybe, some other thread could advance
> global_seqno before step 2.3 and after reading target_seqno by RCU thread on
> step 3.2).  Otherwise, target_seqno will be equal to thread->seqno of the
> 'Read' thread and RCU thread will wait for the next quiescent state before
> calling callbacks.
> 

Yes, you are right. The reader's thread->seqno should bigger than global_seqno so it maybe have run one more times before someone's thread->seqno bigger than global_seqno.

> >
> >     1.3 update new pointer (B)
> >     1.4 quiesced
> >
> >     3.3 call all cb to free (A)
> >
> >     2.2. use pointer (A)
> >
> > Wow, you are absolutely right.  This had never occurred to me.  Thank
> > you!  I'll review your patch.
> 
> Sharing my more detailed diagram of the execution flow that should trigger the
> issue here:
> 
>      ------------------  ------------------  -------------------
>      Thread 1            Thread 2            RCU Thread
>      ------------------  ------------------  -------------------
>      pointer = A
> 
>      ovsrcu_quiesce():
>       thread->seqno = 30
>       global_seqno = 31
>       quiesced
> 
>      read pointer A
>      postpone(free(A)):
>        flush cbset
>                                              pop flushed_cbsets
>                                              ovsrcu_synchronize:
>                                                target_seqno = 31
>                          ovsrcu_quiesce():
>                           thread->seqno = 31
>                           global_seqno = 32
>                           quiesced
> 
>                          read pointer A
>                          use pointer A
> 
>                          ovsrcu_quiesce():
>                           thread->seqno = 32
>                           global_seqno = 33
>                           quiesced
> 
>                          read pointer A
>      pointer = B
> 
>      ovsrcu_quiesce():
>       thread->seqno = 33
>       global_seqno = 34
>       quiesced
> 
>                                              target_seqno exceeded
>                                              by all threads
>                                              call cbs to free A
>                          use pointer A
>                          (use after free)
>      -----------------------------------------------------------
> 
> Best regards, Ilya Maximets.


More information about the dev mailing list