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] [thread-next>] [day] [month] [year] [list]
Date:   Mon, 19 Aug 2019 16:31:48 +0200
From:   Thomas Gleixner <tglx@...utronix.de>
To:     LKML <linux-kernel@...r.kernel.org>
Cc:     Oleg Nesterov <oleg@...hat.com>, Ingo Molnar <mingo@...nel.org>,
        Peter Zijlstra <peterz@...radead.org>,
        John Stultz <john.stultz@...aro.org>,
        Frederic Weisbecker <fweisbec@...il.com>,
        Anna-Maria Behnsen <anna-maria@...utronix.de>
Subject: [patch 07/44] posix-cpu-timers: Simplify sighand locking in
 run_posix_cpu_timers()

run_posix_cpu_timers() is called from the timer interrupt. The posix timer
expiry always affects the current task which got interrupted.

sighand locking is only racy when done on a foreign task, which must use
lock_task_sighand(). But in case of run_posix_cpu_timers() that's
pointless.

sighand of a task can only be dropped or changed by the task itself. Drop
happens in do_exit(), changing sighand happens in execve().

So the timer interrupt can see one of the following states of the current
task which it interrupted:

   - executing:	sighand is set
   - exiting:	sighand is either set or NULL
   - execing:	sighand is either the original one or the new one

The 'check -> lock -> check again' logic in lock_task_sighand() is not
required here at all. The state cannot change as the interrupted task
context obviously is not executing instructions.

So the only interesting case here is to check whether current->sighand is
NULL or not.

Add the check for sighand == NULL right at the beginning and replace
lock_task_sighand() with a regular spin_lock() invocation along with proper
comments why this is safe.

Signed-off-by: Thomas Gleixner <tglx@...utronix.de>
---
 kernel/time/posix-cpu-timers.c |   26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

--- a/kernel/time/posix-cpu-timers.c
+++ b/kernel/time/posix-cpu-timers.c
@@ -1141,20 +1141,34 @@ void run_posix_cpu_timers(void)
 {
 	struct task_struct *tsk = current;
 	struct k_itimer *timer, *next;
-	unsigned long flags;
 	LIST_HEAD(firing);
 
 	lockdep_assert_irqs_disabled();
 
 	/*
-	 * The fast path checks that there are no expired thread or thread
-	 * group timers.  If that's so, just return.
+	 * Verify that sighand exits. If not, the task is exiting and has
+	 * dropped sighand already. Without sighand, timer expiry is
+	 * pointless.
 	 */
-	if (!fastpath_timer_check(tsk))
+	if (!tsk->sighand)
 		return;
 
-	if (!lock_task_sighand(tsk, &flags))
+	/*
+	 * Now do a fast check for expired task and group timers. If
+	 * no expired timers found, return.
+	 */
+	if(!fastpath_timer_check(tsk))
 		return;
+
+	/*
+	 * The timer interrupt hit current. It's verified that sighand
+	 * exists, so it cannot go away before the timer interrupt returns
+	 * as only current can remove or change its own sighand in exit()
+	 * or exec() with sighand lock held and interrupts disabled. Ergo
+	 * the interrupt can only observe a valid sighand or NULL.
+	 */
+	spin_lock(&tsk->sighand->siglock);
+
 	/*
 	 * Here we take off tsk->signal->cpu_timers[N] and
 	 * tsk->cpu_timers[N] all the timers that are firing, and
@@ -1172,7 +1186,7 @@ void run_posix_cpu_timers(void)
 	 * that gets the timer lock before we do will give it up and
 	 * spin until we've taken care of that timer below.
 	 */
-	unlock_task_sighand(tsk, &flags);
+	spin_unlock(&tsk->sighand->siglock);
 
 	/*
 	 * Now that all the timers on our list have the firing flag,


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ