diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2009-12-09 09:19:31 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2010-10-30 08:45:25 -0400 |
commit | 3c80fe4ac9cfb13b1bfa4edf1544e8b656716694 (patch) | |
tree | c605435b642323cd76eea9567a43d8c67b9c9db1 | |
parent | f7a998a9491f2da1d3e44d150aa611d10093da4f (diff) |
audit: Call tty_audit_push_task() outside preempt disabled
While auditing all tasklist_lock read_lock sites I stumbled over the
following call chain:
audit_prepare_user_tty()
read_lock(&tasklist_lock);
tty_audit_push_task();
mutex_lock(&buf->mutex);
--> buf->mutex is locked with preemption disabled.
Solve this by acquiring a reference to the task struct under
rcu_read_lock and call tty_audit_push_task outside of the preempt
disabled region.
Move all code which needs to be protected by sighand lock into
tty_audit_push_task() and use lock/unlock_sighand as we do not hold
tasklist_lock.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Eric Paris <eparis@redhat.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | drivers/char/tty_audit.c | 38 | ||||
-rw-r--r-- | include/linux/tty.h | 9 | ||||
-rw-r--r-- | kernel/audit.c | 25 |
3 files changed, 42 insertions, 30 deletions
diff --git a/drivers/char/tty_audit.c b/drivers/char/tty_audit.c index 1b8ee590b4ca..f64582b0f623 100644 --- a/drivers/char/tty_audit.c +++ b/drivers/char/tty_audit.c | |||
@@ -188,25 +188,43 @@ void tty_audit_tiocsti(struct tty_struct *tty, char ch) | |||
188 | } | 188 | } |
189 | 189 | ||
190 | /** | 190 | /** |
191 | * tty_audit_push_task - Flush task's pending audit data | 191 | * tty_audit_push_task - Flush task's pending audit data |
192 | * @tsk: task pointer | ||
193 | * @loginuid: sender login uid | ||
194 | * @sessionid: sender session id | ||
195 | * | ||
196 | * Called with a ref on @tsk held. Try to lock sighand and get a | ||
197 | * reference to the tty audit buffer if available. | ||
198 | * Flush the buffer or return an appropriate error code. | ||
192 | */ | 199 | */ |
193 | void tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid) | 200 | int tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid) |
194 | { | 201 | { |
195 | struct tty_audit_buf *buf; | 202 | struct tty_audit_buf *buf = ERR_PTR(-EPERM); |
203 | unsigned long flags; | ||
196 | 204 | ||
197 | spin_lock_irq(&tsk->sighand->siglock); | 205 | if (!lock_task_sighand(tsk, &flags)) |
198 | buf = tsk->signal->tty_audit_buf; | 206 | return -ESRCH; |
199 | if (buf) | 207 | |
200 | atomic_inc(&buf->count); | 208 | if (tsk->signal->audit_tty) { |
201 | spin_unlock_irq(&tsk->sighand->siglock); | 209 | buf = tsk->signal->tty_audit_buf; |
202 | if (!buf) | 210 | if (buf) |
203 | return; | 211 | atomic_inc(&buf->count); |
212 | } | ||
213 | unlock_task_sighand(tsk, &flags); | ||
214 | |||
215 | /* | ||
216 | * Return 0 when signal->audit_tty set | ||
217 | * but tsk->signal->tty_audit_buf == NULL. | ||
218 | */ | ||
219 | if (!buf || IS_ERR(buf)) | ||
220 | return PTR_ERR(buf); | ||
204 | 221 | ||
205 | mutex_lock(&buf->mutex); | 222 | mutex_lock(&buf->mutex); |
206 | tty_audit_buf_push(tsk, loginuid, sessionid, buf); | 223 | tty_audit_buf_push(tsk, loginuid, sessionid, buf); |
207 | mutex_unlock(&buf->mutex); | 224 | mutex_unlock(&buf->mutex); |
208 | 225 | ||
209 | tty_audit_buf_put(buf); | 226 | tty_audit_buf_put(buf); |
227 | return 0; | ||
210 | } | 228 | } |
211 | 229 | ||
212 | /** | 230 | /** |
diff --git a/include/linux/tty.h b/include/linux/tty.h index e500171c745f..2a754748dd5f 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h | |||
@@ -541,8 +541,8 @@ extern void tty_audit_exit(void); | |||
541 | extern void tty_audit_fork(struct signal_struct *sig); | 541 | extern void tty_audit_fork(struct signal_struct *sig); |
542 | extern void tty_audit_tiocsti(struct tty_struct *tty, char ch); | 542 | extern void tty_audit_tiocsti(struct tty_struct *tty, char ch); |
543 | extern void tty_audit_push(struct tty_struct *tty); | 543 | extern void tty_audit_push(struct tty_struct *tty); |
544 | extern void tty_audit_push_task(struct task_struct *tsk, | 544 | extern int tty_audit_push_task(struct task_struct *tsk, |
545 | uid_t loginuid, u32 sessionid); | 545 | uid_t loginuid, u32 sessionid); |
546 | #else | 546 | #else |
547 | static inline void tty_audit_add_data(struct tty_struct *tty, | 547 | static inline void tty_audit_add_data(struct tty_struct *tty, |
548 | unsigned char *data, size_t size) | 548 | unsigned char *data, size_t size) |
@@ -560,9 +560,10 @@ static inline void tty_audit_fork(struct signal_struct *sig) | |||
560 | static inline void tty_audit_push(struct tty_struct *tty) | 560 | static inline void tty_audit_push(struct tty_struct *tty) |
561 | { | 561 | { |
562 | } | 562 | } |
563 | static inline void tty_audit_push_task(struct task_struct *tsk, | 563 | static inline int tty_audit_push_task(struct task_struct *tsk, |
564 | uid_t loginuid, u32 sessionid) | 564 | uid_t loginuid, u32 sessionid) |
565 | { | 565 | { |
566 | return 0; | ||
566 | } | 567 | } |
567 | #endif | 568 | #endif |
568 | 569 | ||
diff --git a/kernel/audit.c b/kernel/audit.c index a300931fc45f..8429afea37bf 100644 --- a/kernel/audit.c +++ b/kernel/audit.c | |||
@@ -467,23 +467,16 @@ static int audit_prepare_user_tty(pid_t pid, uid_t loginuid, u32 sessionid) | |||
467 | struct task_struct *tsk; | 467 | struct task_struct *tsk; |
468 | int err; | 468 | int err; |
469 | 469 | ||
470 | read_lock(&tasklist_lock); | 470 | rcu_read_lock(); |
471 | tsk = find_task_by_vpid(pid); | 471 | tsk = find_task_by_vpid(pid); |
472 | err = -ESRCH; | 472 | if (!tsk) { |
473 | if (!tsk) | 473 | rcu_read_unlock(); |
474 | goto out; | 474 | return -ESRCH; |
475 | err = 0; | 475 | } |
476 | 476 | get_task_struct(tsk); | |
477 | spin_lock_irq(&tsk->sighand->siglock); | 477 | rcu_read_unlock(); |
478 | if (!tsk->signal->audit_tty) | 478 | err = tty_audit_push_task(tsk, loginuid, sessionid); |
479 | err = -EPERM; | 479 | put_task_struct(tsk); |
480 | spin_unlock_irq(&tsk->sighand->siglock); | ||
481 | if (err) | ||
482 | goto out; | ||
483 | |||
484 | tty_audit_push_task(tsk, loginuid, sessionid); | ||
485 | out: | ||
486 | read_unlock(&tasklist_lock); | ||
487 | return err; | 480 | return err; |
488 | } | 481 | } |
489 | 482 | ||