From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751011AbdE3VAr (ORCPT ); Tue, 30 May 2017 17:00:47 -0400 Received: from Galois.linutronix.de ([146.0.238.70]:55569 "EHLO Galois.linutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750841AbdE3VAq (ORCPT ); Tue, 30 May 2017 17:00:46 -0400 Date: Tue, 30 May 2017 23:00:38 +0200 (CEST) From: Thomas Gleixner To: Linus Torvalds cc: LKML , Oleg Nesterov , Peter Zijlstra , Ingo Molnar , Michael Kerrisk , linux-man@vger.kernel.org, libc-alpha Subject: Re: signals: Bug or manpage inconsistency? In-Reply-To: Message-ID: References: User-Agent: Alpine 2.20 (DEB 67 2015-01-07) MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Tue, 30 May 2017, Linus Torvalds wrote: > On Tue, May 30, 2017 at 12:35 PM, Thomas Gleixner wrote: > > The reason why I'm looking into that is the silly case with posix interval > > timers dealing with ignored signals. We have to keep these timers self > > rearming because nothing rearms them when SIG_IGN is lifted. > > Why not do SIG_IGN specially at signal generation time, the way > SIGCHLD does. If SIG_IGN is set, you re-arm immediately but do not > actually deliver the signal. It will automatically be re-delivered > next time around (assuming people have by then installed a real signal > handler). The rearming is exactly the issue. Assume the following: t.interval.tv_nsec = 1; t.interval.tv_sec = 0; timer_set(timerid, 0, &t, NULL); This creates a 1 nanosecond periodic timer, which is silly to begin with, but allowed. In the normal case this is automatically rate limited by: expire -> queue signal -> wakeup task -> dequeue signal -> rearm So the scheduler controls how much CPU this task gets because the signal must be dequeued to rearm the timer. If it's the only task on the system it will still hog the CPU, but that's the same as if you do while(1). In the SIG_IGN case it is not, because we need to rearm automatically. So with that 1 nsec interval you created a DOS attack because the system is busy expiring and rearming the timer. The mitigation we have in place is to rate limit that rearming to one jiffie, so the DOS won't happen. But that's just a stupid hack. The proper solution would be to rearm the timer at the point where the SIG_IGN is replaced or for that matter the ignored signal is blocked. I'm fine with the current behaviour vs. blocking the ignored signal, I still think it's inconsistent, but that could be debated forever. Though it needs to be documented somewhere proper and the man page of sigpending() needs to be fixed so the next person looking into this starts scratching his head and asks the same questions again. So with the existing blocking ignored signal semantics the DOS is mitigated as well, because the signal is queued and the rearming happens when the signal is dequeued as in the normal case above, through unblocking or sigwait(). Now there is a subtle issue with this. The following code sequence will stop the timer forever: block(sig); timer expires -> signal is queued -> timer is stopped ignore(sig); pending signal is discarded install_handler(sig); unblock(sig); Neither the handler install nor the unblocking will restart the timer. I think I have an idea how to handle both cases proper but I certainly wanted to have clarity about the semantics before starting. Thanks, tglx From mboxrd@z Thu Jan 1 00:00:00 1970 From: Thomas Gleixner Subject: Re: signals: Bug or manpage inconsistency? Date: Tue, 30 May 2017 23:00:38 +0200 (CEST) Message-ID: References: Mime-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Return-path: In-Reply-To: Sender: linux-man-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org To: Linus Torvalds Cc: LKML , Oleg Nesterov , Peter Zijlstra , Ingo Molnar , Michael Kerrisk , linux-man-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, libc-alpha List-Id: linux-man@vger.kernel.org On Tue, 30 May 2017, Linus Torvalds wrote: > On Tue, May 30, 2017 at 12:35 PM, Thomas Gleixner wrote: > > The reason why I'm looking into that is the silly case with posix interval > > timers dealing with ignored signals. We have to keep these timers self > > rearming because nothing rearms them when SIG_IGN is lifted. > > Why not do SIG_IGN specially at signal generation time, the way > SIGCHLD does. If SIG_IGN is set, you re-arm immediately but do not > actually deliver the signal. It will automatically be re-delivered > next time around (assuming people have by then installed a real signal > handler). The rearming is exactly the issue. Assume the following: t.interval.tv_nsec = 1; t.interval.tv_sec = 0; timer_set(timerid, 0, &t, NULL); This creates a 1 nanosecond periodic timer, which is silly to begin with, but allowed. In the normal case this is automatically rate limited by: expire -> queue signal -> wakeup task -> dequeue signal -> rearm So the scheduler controls how much CPU this task gets because the signal must be dequeued to rearm the timer. If it's the only task on the system it will still hog the CPU, but that's the same as if you do while(1). In the SIG_IGN case it is not, because we need to rearm automatically. So with that 1 nsec interval you created a DOS attack because the system is busy expiring and rearming the timer. The mitigation we have in place is to rate limit that rearming to one jiffie, so the DOS won't happen. But that's just a stupid hack. The proper solution would be to rearm the timer at the point where the SIG_IGN is replaced or for that matter the ignored signal is blocked. I'm fine with the current behaviour vs. blocking the ignored signal, I still think it's inconsistent, but that could be debated forever. Though it needs to be documented somewhere proper and the man page of sigpending() needs to be fixed so the next person looking into this starts scratching his head and asks the same questions again. So with the existing blocking ignored signal semantics the DOS is mitigated as well, because the signal is queued and the rearming happens when the signal is dequeued as in the normal case above, through unblocking or sigwait(). Now there is a subtle issue with this. The following code sequence will stop the timer forever: block(sig); timer expires -> signal is queued -> timer is stopped ignore(sig); pending signal is discarded install_handler(sig); unblock(sig); Neither the handler install nor the unblocking will restart the timer. I think I have an idea how to handle both cases proper but I certainly wanted to have clarity about the semantics before starting. Thanks, tglx -- To unsubscribe from this list: send the line "unsubscribe linux-man" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html