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
    audit_log_start+0x311/0x470
    audit_log_exit+0x4b/0xfb0
    __audit_syscall_exit+0x25f/0x2c0
    sysret_audit+0x17/0x21

Fix it by performing the comparison a single time.

Reported-by: Dave Jones <davej@redhat.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Eric Paris <eparis@redhat.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Andrew Morton 2013-01-11 14:32:11 -08:00 committed by Linus Torvalds
parent 0644ec0cc8
commit 829199197a

View File

@ -1101,6 +1101,23 @@ static inline void audit_get_stamp(struct audit_context *ctx,
} }
} }
/*
* 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 /* Obtain an audit buffer. This routine does locking to obtain the
* audit buffer, but then no locking is required for calls to * 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 * audit_log_*format. If the tsk is a task that is currently in a
@ -1146,20 +1163,13 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx, gfp_t gfp_mask,
while (audit_backlog_limit while (audit_backlog_limit
&& skb_queue_len(&audit_skb_queue) > audit_backlog_limit + reserve) { && skb_queue_len(&audit_skb_queue) > audit_backlog_limit + reserve) {
if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time) {
&& time_before(jiffies, timeout_start + audit_backlog_wait_time)) { unsigned long sleep_time;
/* Wait for auditd to drain the queue a little */ sleep_time = timeout_start + audit_backlog_wait_time -
DECLARE_WAITQUEUE(wait, current); jiffies;
set_current_state(TASK_INTERRUPTIBLE); if ((long)sleep_time > 0)
add_wait_queue(&audit_backlog_wait, &wait); wait_for_auditd(sleep_time);
if (audit_backlog_limit &&
skb_queue_len(&audit_skb_queue) > audit_backlog_limit)
schedule_timeout(timeout_start + audit_backlog_wait_time - jiffies);
__set_current_state(TASK_RUNNING);
remove_wait_queue(&audit_backlog_wait, &wait);
continue; continue;
} }
if (audit_rate_check() && printk_ratelimit()) if (audit_rate_check() && printk_ratelimit())