From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754437Ab3ACWUd (ORCPT ); Thu, 3 Jan 2013 17:20:33 -0500 Received: from mail.linuxfoundation.org ([140.211.169.12]:34063 "EHLO mail.linuxfoundation.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754329Ab3ACWUb (ORCPT ); Thu, 3 Jan 2013 17:20:31 -0500 Date: Thu, 3 Jan 2013 14:20:29 -0800 From: Andrew Morton To: Dave Jones Cc: Linux Kernel , Ingo Molnar , Peter Zijlstra , Al Viro , Eric Paris Subject: Re: schedule_timeout: wrong timeout value fffffffffffffff0 Message-Id: <20130103142029.705d2538.akpm@linux-foundation.org> In-Reply-To: <20130102152727.GA25940@redhat.com> References: <20130102152727.GA25940@redhat.com> X-Mailer: Sylpheed 3.0.2 (GTK+ 2.20.1; x86_64-pc-linux-gnu) Mime-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Wed, 2 Jan 2013 10:27:27 -0500 Dave Jones wrote: > This happened to a box I left running fuzz tests over the holidays. > > > schedule_timeout: wrong timeout value fffffffffffffff0 > Pid: 6606, comm: trinity-child1 Not tainted 3.8.0-rc1+ #43 > Call Trace: > [] schedule_timeout+0x305/0x340 > [] ? preempt_schedule+0x42/0x60 > [] ? _raw_spin_unlock_irqrestore+0x75/0x80 > [] audit_log_start+0x311/0x470 > [] ? __rcu_read_unlock+0x5c/0xa0 > [] ? try_to_wake_up+0x320/0x320 > [] audit_log_exit+0x4b/0xfb0 > [] ? vm_mmap_pgoff+0x8b/0xb0 > [] ? get_lock_stats+0x22/0x70 > [] ? put_lock_stats.isra.23+0xe/0x40 > [] ? lock_release_holdtime.part.24+0xcb/0x130 > [] ? up_write+0x23/0x40 > [] ? vm_mmap_pgoff+0x8b/0xb0 > [] ? sysret_signal+0x5/0x47 > [] __audit_syscall_exit+0x25f/0x2c0 > [] sysret_audit+0x17/0x21 > ooh, I can fix that. Please review. From: Andrew Morton Subject: kernel/audit.c: avoid negative sleep durations audit_log_start() performs the same jiffies comparison in two places. If sufficient time has elapsed between the two comparisons, the second one produces a negative sleep duration: schedule_timeout: wrong timeout value fffffffffffffff0 Pid: 6606, comm: trinity-child1 Not tainted 3.8.0-rc1+ #43 Call Trace: [] schedule_timeout+0x305/0x340 [] ? preempt_schedule+0x42/0x60 [] ? _raw_spin_unlock_irqrestore+0x75/0x80 [] audit_log_start+0x311/0x470 [] ? __rcu_read_unlock+0x5c/0xa0 [] ? try_to_wake_up+0x320/0x320 [] audit_log_exit+0x4b/0xfb0 [] ? vm_mmap_pgoff+0x8b/0xb0 [] ? get_lock_stats+0x22/0x70 [] ? put_lock_stats.isra.23+0xe/0x40 [] ? lock_release_holdtime.part.24+0xcb/0x130 [] ? up_write+0x23/0x40 [] ? vm_mmap_pgoff+0x8b/0xb0 [] ? sysret_signal+0x5/0x47 [] __audit_syscall_exit+0x25f/0x2c0 [] sysret_audit+0x17/0x21 Fix it by performing the comparison a single time. Reported-by: Dave Jones Cc: Al Viro Cc: Eric Paris Signed-off-by: Andrew Morton --- kernel/audit.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff -puN kernel/audit.c~a kernel/audit.c --- a/kernel/audit.c~a +++ a/kernel/audit.c @@ -1101,6 +1101,23 @@ static inline void audit_get_stamp(struc } } +/* + * Wait for auditd to drain the queue a little + */ +static void wait_for_auditd(unsigned long sleep_time) +{ + DECLARE_WAITQUEUE(wait, current); + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&audit_backlog_wait, &wait); + + if (audit_backlog_limit && + skb_queue_len(&audit_skb_queue) > audit_backlog_limit) + schedule_timeout(sleep_time); + + __set_current_state(TASK_RUNNING); + remove_wait_queue(&audit_backlog_wait, &wait); +} + /* Obtain an audit buffer. This routine does locking to obtain the * audit buffer, but then no locking is required for calls to * audit_log_*format. If the tsk is a task that is currently in a @@ -1146,20 +1163,13 @@ struct audit_buffer *audit_log_start(str while (audit_backlog_limit && skb_queue_len(&audit_skb_queue) > audit_backlog_limit + reserve) { - if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time - && time_before(jiffies, timeout_start + audit_backlog_wait_time)) { - - /* Wait for auditd to drain the queue a little */ - DECLARE_WAITQUEUE(wait, current); - set_current_state(TASK_INTERRUPTIBLE); - add_wait_queue(&audit_backlog_wait, &wait); - - if (audit_backlog_limit && - skb_queue_len(&audit_skb_queue) > audit_backlog_limit) - schedule_timeout(timeout_start + audit_backlog_wait_time - jiffies); + if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time) { + unsigned long sleep_time; - __set_current_state(TASK_RUNNING); - remove_wait_queue(&audit_backlog_wait, &wait); + sleep_time = timeout_start + audit_backlog_wait_time - + jiffies; + if ((long)sleep_time > 0) + wait_for_auditd(sleep_time); continue; } if (audit_rate_check() && printk_ratelimit()) _