summaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@linutronix.de>2009-12-09 14:19:31 +0000
committerAl Viro <viro@zeniv.linux.org.uk>2010-10-30 08:45:25 -0400
commit3c80fe4ac9cfb13b1bfa4edf1544e8b656716694 (patch)
treec605435b642323cd76eea9567a43d8c67b9c9db1 /drivers/char
parentf7a998a9491f2da1d3e44d150aa611d10093da4f (diff)
downloadlinux-3c80fe4ac9cfb13b1bfa4edf1544e8b656716694.tar.gz
linux-3c80fe4ac9cfb13b1bfa4edf1544e8b656716694.tar.bz2
linux-3c80fe4ac9cfb13b1bfa4edf1544e8b656716694.zip
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>
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/tty_audit.c38
1 files changed, 28 insertions, 10 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)
}
/**
- * tty_audit_push_task - Flush task's pending audit data
+ * tty_audit_push_task - Flush task's pending audit data
+ * @tsk: task pointer
+ * @loginuid: sender login uid
+ * @sessionid: sender session id
+ *
+ * Called with a ref on @tsk held. Try to lock sighand and get a
+ * reference to the tty audit buffer if available.
+ * Flush the buffer or return an appropriate error code.
*/
-void tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid)
+int tty_audit_push_task(struct task_struct *tsk, uid_t loginuid, u32 sessionid)
{
- struct tty_audit_buf *buf;
+ struct tty_audit_buf *buf = ERR_PTR(-EPERM);
+ unsigned long flags;
- spin_lock_irq(&tsk->sighand->siglock);
- buf = tsk->signal->tty_audit_buf;
- if (buf)
- atomic_inc(&buf->count);
- spin_unlock_irq(&tsk->sighand->siglock);
- if (!buf)
- return;
+ if (!lock_task_sighand(tsk, &flags))
+ return -ESRCH;
+
+ if (tsk->signal->audit_tty) {
+ buf = tsk->signal->tty_audit_buf;
+ if (buf)
+ atomic_inc(&buf->count);
+ }
+ unlock_task_sighand(tsk, &flags);
+
+ /*
+ * Return 0 when signal->audit_tty set
+ * but tsk->signal->tty_audit_buf == NULL.
+ */
+ if (!buf || IS_ERR(buf))
+ return PTR_ERR(buf);
mutex_lock(&buf->mutex);
tty_audit_buf_push(tsk, loginuid, sessionid, buf);
mutex_unlock(&buf->mutex);
tty_audit_buf_put(buf);
+ return 0;
}
/**