diff options
author | Andrew Morton <akpm@linux-foundation.org> | 2013-01-11 17:32:11 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-01-11 17:54:56 -0500 |
commit | 829199197a430dade2519d54f5545c4a094393b8 (patch) | |
tree | af50ca9af09b83fd2a5c76cad35bd0603eb33391 | |
parent | 0644ec0cc8a33fb654e348897ad7684e22a4b5d8 (diff) |
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>
-rw-r--r-- | kernel/audit.c | 36 |
1 files changed, 23 insertions, 13 deletions
diff --git a/kernel/audit.c b/kernel/audit.c index a219998aecc1..d596e5355f15 100644 --- a/kernel/audit.c +++ b/kernel/audit.c | |||
@@ -1101,6 +1101,23 @@ static inline void audit_get_stamp(struct audit_context *ctx, | |||
1101 | } | 1101 | } |
1102 | } | 1102 | } |
1103 | 1103 | ||
1104 | /* | ||
1105 | * Wait for auditd to drain the queue a little | ||
1106 | */ | ||
1107 | static void wait_for_auditd(unsigned long sleep_time) | ||
1108 | { | ||
1109 | DECLARE_WAITQUEUE(wait, current); | ||
1110 | set_current_state(TASK_INTERRUPTIBLE); | ||
1111 | add_wait_queue(&audit_backlog_wait, &wait); | ||
1112 | |||
1113 | if (audit_backlog_limit && | ||
1114 | skb_queue_len(&audit_skb_queue) > audit_backlog_limit) | ||
1115 | schedule_timeout(sleep_time); | ||
1116 | |||
1117 | __set_current_state(TASK_RUNNING); | ||
1118 | remove_wait_queue(&audit_backlog_wait, &wait); | ||
1119 | } | ||
1120 | |||
1104 | /* Obtain an audit buffer. This routine does locking to obtain the | 1121 | /* Obtain an audit buffer. This routine does locking to obtain the |
1105 | * audit buffer, but then no locking is required for calls to | 1122 | * audit buffer, but then no locking is required for calls to |
1106 | * audit_log_*format. If the tsk is a task that is currently in a | 1123 | * 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, | |||
1146 | 1163 | ||
1147 | while (audit_backlog_limit | 1164 | while (audit_backlog_limit |
1148 | && skb_queue_len(&audit_skb_queue) > audit_backlog_limit + reserve) { | 1165 | && skb_queue_len(&audit_skb_queue) > audit_backlog_limit + reserve) { |
1149 | if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time | 1166 | if (gfp_mask & __GFP_WAIT && audit_backlog_wait_time) { |
1150 | && time_before(jiffies, timeout_start + audit_backlog_wait_time)) { | 1167 | unsigned long sleep_time; |
1151 | |||
1152 | /* Wait for auditd to drain the queue a little */ | ||
1153 | DECLARE_WAITQUEUE(wait, current); | ||
1154 | set_current_state(TASK_INTERRUPTIBLE); | ||
1155 | add_wait_queue(&audit_backlog_wait, &wait); | ||
1156 | |||
1157 | if (audit_backlog_limit && | ||
1158 | skb_queue_len(&audit_skb_queue) > audit_backlog_limit) | ||
1159 | schedule_timeout(timeout_start + audit_backlog_wait_time - jiffies); | ||
1160 | 1168 | ||
1161 | __set_current_state(TASK_RUNNING); | 1169 | sleep_time = timeout_start + audit_backlog_wait_time - |
1162 | remove_wait_queue(&audit_backlog_wait, &wait); | 1170 | jiffies; |
1171 | if ((long)sleep_time > 0) | ||
1172 | wait_for_auditd(sleep_time); | ||
1163 | continue; | 1173 | continue; |
1164 | } | 1174 | } |
1165 | if (audit_rate_check() && printk_ratelimit()) | 1175 | if (audit_rate_check() && printk_ratelimit()) |