From 3c80fe4ac9cfb13b1bfa4edf1544e8b656716694 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 9 Dec 2009 14:19:31 +0000 Subject: 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 Cc: Al Viro Cc: Eric Paris Cc: Oleg Nesterov Signed-off-by: Al Viro --- drivers/char/tty_audit.c | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) (limited to 'drivers/char') 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; } /** -- cgit v1.2.3