lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <1352841428.18025.45.camel@gandalf.local.home>
Date:	Tue, 13 Nov 2012 16:17:08 -0500
From:	Steven Rostedt <rostedt@...dmis.org>
To:	Frederic Weisbecker <fweisbec@...il.com>
Cc:	Ingo Molnar <mingo@...nel.org>,
	Peter Zijlstra <peterz@...radead.org>,
	LKML <linux-kernel@...r.kernel.org>,
	Thomas Gleixner <tglx@...utronix.de>,
	Andrew Morton <akpm@...ux-foundation.org>,
	Paul Gortmaker <paul.gortmaker@...driver.com>,
	Anish Kumar <anish198519851985@...il.com>
Subject: Re: [PATCH 2/2] irq_work: Fix racy check on work pending flag

On Fri, 2012-11-02 at 18:35 +0100, Frederic Weisbecker wrote:
> Work claiming wants to be SMP-safe.
> 
> And by the time we try to claim a work, if it is already executing
> concurrently on another CPU, we want to succeed the claiming and queue
> the work again because the other CPU may have missed the data we wanted
> to handle in our work if it's about to complete there.
> 
> This scenario is summarized below:
> 
>         CPU 1                                   CPU 2
>         -----                                   -----
>         (flags = 0)
>         cmpxchg(flags, 0, IRQ_WORK_FLAGS)
>         (flags = 3)
>         [...]
>         xchg(flags, IRQ_WORK_BUSY)
>         (flags = 2)
>         func()
>                                                 if (flags & IRQ_WORK_PENDING)
>                                                         (not true)
>                                                 cmpxchg(flags, flags, IRQ_WORK_FLAGS)
>                                                 (flags = 3)
>                                                 [...]
>         cmpxchg(flags, IRQ_WORK_BUSY, 0);
>         (fail, pending on CPU 2)
> 
> This state machine is synchronized using [cmp]xchg() on the flags.
> As such, the early IRQ_WORK_PENDING check in CPU 2 above is racy.
> By the time we check it, we may be dealing with a stale value because
> we aren't using an atomic accessor. As a result, CPU 2 may "see"
> that the work is still pending on another CPU while it may be
> actually completing the work function exection already, leaving
> our data unprocessed.
> 
> To fix this, we start by speculating about the value we wish to be
> in the work->flags but we only make any conclusion after the value
> returned by the cmpxchg() call that either claims the work or let
> the current owner handle the pending work for us.
> 
> Changelog-heavily-inspired-by: Steven Rostedt <rostedt@...dmis.org>
> Signed-off-by: Frederic Weisbecker <fweisbec@...il.com>
> Cc: Peter Zijlstra <peterz@...radead.org>
> Cc: Ingo Molnar <mingo@...nel.org>
> Cc: Thomas Gleixner <tglx@...utronix.de>
> Cc: Andrew Morton <akpm@...ux-foundation.org>

Acked-by: Steven Rostedt <rostedt@...dmis.org>

-- Steve

> Cc: Paul Gortmaker <paul.gortmaker@...driver.com>
> Cc: Anish Kumar <anish198519851985@...il.com>
> ---
>  kernel/irq_work.c |   16 +++++++++++-----
>  1 files changed, 11 insertions(+), 5 deletions(-)
> 
> diff --git a/kernel/irq_work.c b/kernel/irq_work.c
> index 57be1a6..64eddd5 100644
> --- a/kernel/irq_work.c
> +++ b/kernel/irq_work.c
> @@ -34,15 +34,21 @@ static DEFINE_PER_CPU(struct llist_head, irq_work_list);
>   */
>  static bool irq_work_claim(struct irq_work *work)
>  {
> -	unsigned long flags, nflags;
> +	unsigned long flags, oflags, nflags;
>  
> +	/*
> +	 * Start with our best wish as a premise but only trust any
> +	 * flag value after cmpxchg() result.
> +	 */
> +	flags = work->flags & ~IRQ_WORK_PENDING;
>  	for (;;) {
> -		flags = work->flags;
> -		if (flags & IRQ_WORK_PENDING)
> -			return false;
>  		nflags = flags | IRQ_WORK_FLAGS;
> -		if (cmpxchg(&work->flags, flags, nflags) == flags)
> +		oflags = cmpxchg(&work->flags, flags, nflags);
> +		if (oflags == flags)
>  			break;
> +		if (oflags & IRQ_WORK_PENDING)
> +			return false;
> +		flags = oflags;
>  		cpu_relax();
>  	}
>  


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ