2017-11-06 20:11:51 +03:00
// SPDX-License-Identifier: GPL-2.0
2017-04-13 01:37:16 +03:00
/*
* Copyright ( C ) 1991 , 1992 Linus Torvalds
*/
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/signal.h>
# include <linux/sched/signal.h>
# include <linux/sched/task.h>
# include <linux/tty.h>
# include <linux/fcntl.h>
# include <linux/uaccess.h>
static int is_ignored ( int sig )
{
return ( sigismember ( & current - > blocked , sig ) | |
current - > sighand - > action [ sig - 1 ] . sa . sa_handler = = SIG_IGN ) ;
}
/**
* tty_check_change - check for POSIX terminal changes
* @ tty : tty to check
2020-11-04 22:35:20 +03:00
* @ sig : signal to send
2017-04-13 01:37:16 +03:00
*
* If we try to write to , or set the state of , a terminal and we ' re
* not in the foreground , send a SIGTTOU . If the signal is blocked or
* ignored , go ahead and perform the operation . ( POSIX 7.2 )
*
* Locking : ctrl_lock
*/
int __tty_check_change ( struct tty_struct * tty , int sig )
{
unsigned long flags ;
struct pid * pgrp , * tty_pgrp ;
int ret = 0 ;
if ( current - > signal - > tty ! = tty )
return 0 ;
rcu_read_lock ( ) ;
pgrp = task_pgrp ( current ) ;
spin_lock_irqsave ( & tty - > ctrl_lock , flags ) ;
tty_pgrp = tty - > pgrp ;
spin_unlock_irqrestore ( & tty - > ctrl_lock , flags ) ;
2019-03-11 00:43:47 +03:00
if ( tty_pgrp & & pgrp ! = tty_pgrp ) {
2017-04-13 01:37:16 +03:00
if ( is_ignored ( sig ) ) {
if ( sig = = SIGTTIN )
ret = - EIO ;
} else if ( is_current_pgrp_orphaned ( ) )
ret = - EIO ;
else {
kill_pgrp ( pgrp , sig , 1 ) ;
set_thread_flag ( TIF_SIGPENDING ) ;
ret = - ERESTARTSYS ;
}
}
rcu_read_unlock ( ) ;
if ( ! tty_pgrp )
tty_warn ( tty , " sig=%d, tty->pgrp == NULL! \n " , sig ) ;
return ret ;
}
int tty_check_change ( struct tty_struct * tty )
{
return __tty_check_change ( tty , SIGTTOU ) ;
}
EXPORT_SYMBOL ( tty_check_change ) ;
void proc_clear_tty ( struct task_struct * p )
{
unsigned long flags ;
struct tty_struct * tty ;
spin_lock_irqsave ( & p - > sighand - > siglock , flags ) ;
tty = p - > signal - > tty ;
p - > signal - > tty = NULL ;
spin_unlock_irqrestore ( & p - > sighand - > siglock , flags ) ;
tty_kref_put ( tty ) ;
}
/**
* proc_set_tty - set the controlling terminal
2020-11-04 22:35:20 +03:00
* @ tty : tty structure
2017-04-13 01:37:16 +03:00
*
* Only callable by the session leader and only if it does not already have
* a controlling terminal .
*
* Caller must hold : tty_lock ( )
* a readlock on tasklist_lock
* sighand lock
*/
static void __proc_set_tty ( struct tty_struct * tty )
{
unsigned long flags ;
spin_lock_irqsave ( & tty - > ctrl_lock , flags ) ;
/*
* The session and fg pgrp references will be non - NULL if
* tiocsctty ( ) is stealing the controlling tty
*/
put_pid ( tty - > session ) ;
put_pid ( tty - > pgrp ) ;
tty - > pgrp = get_pid ( task_pgrp ( current ) ) ;
tty - > session = get_pid ( task_session ( current ) ) ;
tty: Fix ->session locking
Currently, locking of ->session is very inconsistent; most places
protect it using the legacy tty mutex, but disassociate_ctty(),
__do_SAK(), tiocspgrp() and tiocgsid() don't.
Two of the writers hold the ctrl_lock (because they already need it for
->pgrp), but __proc_set_tty() doesn't do that yet.
On a PREEMPT=y system, an unprivileged user can theoretically abuse
this broken locking to read 4 bytes of freed memory via TIOCGSID if
tiocgsid() is preempted long enough at the right point. (Other things
might also go wrong, especially if root-only ioctls are involved; I'm
not sure about that.)
Change the locking on ->session such that:
- tty_lock() is held by all writers: By making disassociate_ctty()
hold it. This should be fine because the same lock can already be
taken through the call to tty_vhangup_session().
The tricky part is that we need to shorten the area covered by
siglock to be able to take tty_lock() without ugly retry logic; as
far as I can tell, this should be fine, since nothing in the
signal_struct is touched in the `if (tty)` branch.
- ctrl_lock is held by all writers: By changing __proc_set_tty() to
hold the lock a little longer.
- All readers that aren't holding tty_lock() hold ctrl_lock: By
adding locking to tiocgsid() and __do_SAK(), and expanding the area
covered by ctrl_lock in tiocspgrp().
Cc: stable@kernel.org
Signed-off-by: Jann Horn <jannh@google.com>
Reviewed-by: Jiri Slaby <jirislaby@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-12-03 04:25:05 +03:00
spin_unlock_irqrestore ( & tty - > ctrl_lock , flags ) ;
2017-04-13 01:37:16 +03:00
if ( current - > signal - > tty ) {
tty_debug ( tty , " current tty %s not NULL!! \n " ,
current - > signal - > tty - > name ) ;
tty_kref_put ( current - > signal - > tty ) ;
}
put_pid ( current - > signal - > tty_old_pgrp ) ;
current - > signal - > tty = tty_kref_get ( tty ) ;
current - > signal - > tty_old_pgrp = NULL ;
}
static void proc_set_tty ( struct tty_struct * tty )
{
spin_lock_irq ( & current - > sighand - > siglock ) ;
__proc_set_tty ( tty ) ;
spin_unlock_irq ( & current - > sighand - > siglock ) ;
}
/*
* Called by tty_open ( ) to set the controlling tty if applicable .
*/
void tty_open_proc_set_tty ( struct file * filp , struct tty_struct * tty )
{
read_lock ( & tasklist_lock ) ;
spin_lock_irq ( & current - > sighand - > siglock ) ;
if ( current - > signal - > leader & &
! current - > signal - > tty & &
tty - > session = = NULL ) {
/*
* Don ' t let a process that only has write access to the tty
* obtain the privileges associated with having a tty as
* controlling terminal ( being able to reopen it with full
* access through / dev / tty , being able to perform pushback ) .
* Many distributions set the group of all ttys to " tty " and
* grant write - only access to all terminals for setgid tty
* binaries , which should not imply full privileges on all ttys .
*
* This could theoretically break old code that performs open ( )
* on a write - only file descriptor . In that case , it might be
* necessary to also permit this if
* inode_permission ( inode , MAY_READ ) = = 0.
*/
if ( filp - > f_mode & FMODE_READ )
__proc_set_tty ( tty ) ;
}
spin_unlock_irq ( & current - > sighand - > siglock ) ;
read_unlock ( & tasklist_lock ) ;
}
struct tty_struct * get_current_tty ( void )
{
struct tty_struct * tty ;
unsigned long flags ;
spin_lock_irqsave ( & current - > sighand - > siglock , flags ) ;
tty = tty_kref_get ( current - > signal - > tty ) ;
spin_unlock_irqrestore ( & current - > sighand - > siglock , flags ) ;
return tty ;
}
EXPORT_SYMBOL_GPL ( get_current_tty ) ;
/*
* Called from tty_release ( ) .
*/
void session_clear_tty ( struct pid * session )
{
struct task_struct * p ;
do_each_pid_task ( session , PIDTYPE_SID , p ) {
proc_clear_tty ( p ) ;
} while_each_pid_task ( session , PIDTYPE_SID , p ) ;
}
/**
* tty_signal_session_leader - sends SIGHUP to session leader
2020-08-18 11:56:51 +03:00
* @ tty : controlling tty
* @ exit_session : if non - zero , signal all foreground group processes
2017-04-13 01:37:16 +03:00
*
* Send SIGHUP and SIGCONT to the session leader and its process group .
* Optionally , signal all processes in the foreground process group .
*
* Returns the number of processes in the session with this tty
* as their controlling terminal . This value is used to drop
* tty references for those processes .
*/
int tty_signal_session_leader ( struct tty_struct * tty , int exit_session )
{
struct task_struct * p ;
int refs = 0 ;
struct pid * tty_pgrp = NULL ;
read_lock ( & tasklist_lock ) ;
if ( tty - > session ) {
do_each_pid_task ( tty - > session , PIDTYPE_SID , p ) {
spin_lock_irq ( & p - > sighand - > siglock ) ;
if ( p - > signal - > tty = = tty ) {
p - > signal - > tty = NULL ;
/* We defer the dereferences outside fo
the tasklist lock */
refs + + ;
}
if ( ! p - > signal - > leader ) {
spin_unlock_irq ( & p - > sighand - > siglock ) ;
continue ;
}
__group_send_sig_info ( SIGHUP , SEND_SIG_PRIV , p ) ;
__group_send_sig_info ( SIGCONT , SEND_SIG_PRIV , p ) ;
put_pid ( p - > signal - > tty_old_pgrp ) ; /* A noop */
spin_lock ( & tty - > ctrl_lock ) ;
tty_pgrp = get_pid ( tty - > pgrp ) ;
if ( tty - > pgrp )
p - > signal - > tty_old_pgrp = get_pid ( tty - > pgrp ) ;
spin_unlock ( & tty - > ctrl_lock ) ;
spin_unlock_irq ( & p - > sighand - > siglock ) ;
} while_each_pid_task ( tty - > session , PIDTYPE_SID , p ) ;
}
read_unlock ( & tasklist_lock ) ;
if ( tty_pgrp ) {
if ( exit_session )
kill_pgrp ( tty_pgrp , SIGHUP , exit_session ) ;
put_pid ( tty_pgrp ) ;
}
return refs ;
}
/**
* disassociate_ctty - disconnect controlling tty
* @ on_exit : true if exiting so need to " hang up " the session
*
* This function is typically called only by the session leader , when
* it wants to disassociate itself from its controlling tty .
*
* It performs the following functions :
* ( 1 ) Sends a SIGHUP and SIGCONT to the foreground process group
* ( 2 ) Clears the tty from being controlling the session
* ( 3 ) Clears the controlling tty for all processes in the
* session group .
*
* The argument on_exit is set to 1 if called when a process is
* exiting ; it is 0 if called by the ioctl TIOCNOTTY .
*
* Locking :
* BTM is taken for hysterical raisons , and held when
* called from no_tty ( ) .
* tty_mutex is taken to protect tty
* - > siglock is taken to protect - > signal / - > sighand
* tasklist_lock is taken to walk process list for sessions
* - > siglock is taken to protect - > signal / - > sighand
*/
void disassociate_ctty ( int on_exit )
{
struct tty_struct * tty ;
if ( ! current - > signal - > leader )
return ;
tty = get_current_tty ( ) ;
if ( tty ) {
if ( on_exit & & tty - > driver - > type ! = TTY_DRIVER_TYPE_PTY ) {
tty_vhangup_session ( tty ) ;
} else {
struct pid * tty_pgrp = tty_get_pgrp ( tty ) ;
if ( tty_pgrp ) {
kill_pgrp ( tty_pgrp , SIGHUP , on_exit ) ;
if ( ! on_exit )
kill_pgrp ( tty_pgrp , SIGCONT , on_exit ) ;
put_pid ( tty_pgrp ) ;
}
}
tty_kref_put ( tty ) ;
} else if ( on_exit ) {
struct pid * old_pgrp ;
spin_lock_irq ( & current - > sighand - > siglock ) ;
old_pgrp = current - > signal - > tty_old_pgrp ;
current - > signal - > tty_old_pgrp = NULL ;
spin_unlock_irq ( & current - > sighand - > siglock ) ;
if ( old_pgrp ) {
kill_pgrp ( old_pgrp , SIGHUP , on_exit ) ;
kill_pgrp ( old_pgrp , SIGCONT , on_exit ) ;
put_pid ( old_pgrp ) ;
}
return ;
}
spin_lock_irq ( & current - > sighand - > siglock ) ;
put_pid ( current - > signal - > tty_old_pgrp ) ;
current - > signal - > tty_old_pgrp = NULL ;
tty = tty_kref_get ( current - > signal - > tty ) ;
tty: Fix ->session locking
Currently, locking of ->session is very inconsistent; most places
protect it using the legacy tty mutex, but disassociate_ctty(),
__do_SAK(), tiocspgrp() and tiocgsid() don't.
Two of the writers hold the ctrl_lock (because they already need it for
->pgrp), but __proc_set_tty() doesn't do that yet.
On a PREEMPT=y system, an unprivileged user can theoretically abuse
this broken locking to read 4 bytes of freed memory via TIOCGSID if
tiocgsid() is preempted long enough at the right point. (Other things
might also go wrong, especially if root-only ioctls are involved; I'm
not sure about that.)
Change the locking on ->session such that:
- tty_lock() is held by all writers: By making disassociate_ctty()
hold it. This should be fine because the same lock can already be
taken through the call to tty_vhangup_session().
The tricky part is that we need to shorten the area covered by
siglock to be able to take tty_lock() without ugly retry logic; as
far as I can tell, this should be fine, since nothing in the
signal_struct is touched in the `if (tty)` branch.
- ctrl_lock is held by all writers: By changing __proc_set_tty() to
hold the lock a little longer.
- All readers that aren't holding tty_lock() hold ctrl_lock: By
adding locking to tiocgsid() and __do_SAK(), and expanding the area
covered by ctrl_lock in tiocspgrp().
Cc: stable@kernel.org
Signed-off-by: Jann Horn <jannh@google.com>
Reviewed-by: Jiri Slaby <jirislaby@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-12-03 04:25:05 +03:00
spin_unlock_irq ( & current - > sighand - > siglock ) ;
2017-04-13 01:37:16 +03:00
if ( tty ) {
unsigned long flags ;
tty: Fix ->session locking
Currently, locking of ->session is very inconsistent; most places
protect it using the legacy tty mutex, but disassociate_ctty(),
__do_SAK(), tiocspgrp() and tiocgsid() don't.
Two of the writers hold the ctrl_lock (because they already need it for
->pgrp), but __proc_set_tty() doesn't do that yet.
On a PREEMPT=y system, an unprivileged user can theoretically abuse
this broken locking to read 4 bytes of freed memory via TIOCGSID if
tiocgsid() is preempted long enough at the right point. (Other things
might also go wrong, especially if root-only ioctls are involved; I'm
not sure about that.)
Change the locking on ->session such that:
- tty_lock() is held by all writers: By making disassociate_ctty()
hold it. This should be fine because the same lock can already be
taken through the call to tty_vhangup_session().
The tricky part is that we need to shorten the area covered by
siglock to be able to take tty_lock() without ugly retry logic; as
far as I can tell, this should be fine, since nothing in the
signal_struct is touched in the `if (tty)` branch.
- ctrl_lock is held by all writers: By changing __proc_set_tty() to
hold the lock a little longer.
- All readers that aren't holding tty_lock() hold ctrl_lock: By
adding locking to tiocgsid() and __do_SAK(), and expanding the area
covered by ctrl_lock in tiocspgrp().
Cc: stable@kernel.org
Signed-off-by: Jann Horn <jannh@google.com>
Reviewed-by: Jiri Slaby <jirislaby@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-12-03 04:25:05 +03:00
tty_lock ( tty ) ;
2017-04-13 01:37:16 +03:00
spin_lock_irqsave ( & tty - > ctrl_lock , flags ) ;
put_pid ( tty - > session ) ;
put_pid ( tty - > pgrp ) ;
tty - > session = NULL ;
tty - > pgrp = NULL ;
spin_unlock_irqrestore ( & tty - > ctrl_lock , flags ) ;
tty: Fix ->session locking
Currently, locking of ->session is very inconsistent; most places
protect it using the legacy tty mutex, but disassociate_ctty(),
__do_SAK(), tiocspgrp() and tiocgsid() don't.
Two of the writers hold the ctrl_lock (because they already need it for
->pgrp), but __proc_set_tty() doesn't do that yet.
On a PREEMPT=y system, an unprivileged user can theoretically abuse
this broken locking to read 4 bytes of freed memory via TIOCGSID if
tiocgsid() is preempted long enough at the right point. (Other things
might also go wrong, especially if root-only ioctls are involved; I'm
not sure about that.)
Change the locking on ->session such that:
- tty_lock() is held by all writers: By making disassociate_ctty()
hold it. This should be fine because the same lock can already be
taken through the call to tty_vhangup_session().
The tricky part is that we need to shorten the area covered by
siglock to be able to take tty_lock() without ugly retry logic; as
far as I can tell, this should be fine, since nothing in the
signal_struct is touched in the `if (tty)` branch.
- ctrl_lock is held by all writers: By changing __proc_set_tty() to
hold the lock a little longer.
- All readers that aren't holding tty_lock() hold ctrl_lock: By
adding locking to tiocgsid() and __do_SAK(), and expanding the area
covered by ctrl_lock in tiocspgrp().
Cc: stable@kernel.org
Signed-off-by: Jann Horn <jannh@google.com>
Reviewed-by: Jiri Slaby <jirislaby@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-12-03 04:25:05 +03:00
tty_unlock ( tty ) ;
2017-04-13 01:37:16 +03:00
tty_kref_put ( tty ) ;
}
/* Now clear signal->tty under the lock */
read_lock ( & tasklist_lock ) ;
session_clear_tty ( task_session ( current ) ) ;
read_unlock ( & tasklist_lock ) ;
}
2019-03-12 14:49:03 +03:00
/*
2017-04-13 01:37:16 +03:00
*
* no_tty - Ensure the current process does not have a controlling tty
*/
void no_tty ( void )
{
/* FIXME: Review locking here. The tty_lock never covered any race
between a new association and proc_clear_tty but possible we need
to protect against this anyway */
struct task_struct * tsk = current ;
disassociate_ctty ( 0 ) ;
proc_clear_tty ( tsk ) ;
}
/**
* tiocsctty - set controlling tty
* @ tty : tty structure
2020-11-04 22:35:20 +03:00
* @ file : file structure used to check permissions
2017-04-13 01:37:16 +03:00
* @ arg : user argument
*
* This ioctl is used to manage job control . It permits a session
* leader to set this tty as the controlling tty for the session .
*
* Locking :
* Takes tty_lock ( ) to serialize proc_set_tty ( ) for this tty
* Takes tasklist_lock internally to walk sessions
* Takes - > siglock ( ) when updating signal - > tty
*/
static int tiocsctty ( struct tty_struct * tty , struct file * file , int arg )
{
int ret = 0 ;
tty_lock ( tty ) ;
read_lock ( & tasklist_lock ) ;
if ( current - > signal - > leader & & ( task_session ( current ) = = tty - > session ) )
goto unlock ;
/*
* The process must be a session leader and
* not have a controlling tty already .
*/
if ( ! current - > signal - > leader | | current - > signal - > tty ) {
ret = - EPERM ;
goto unlock ;
}
if ( tty - > session ) {
/*
* This tty is already the controlling
* tty for another session group !
*/
if ( arg = = 1 & & capable ( CAP_SYS_ADMIN ) ) {
/*
* Steal it away
*/
session_clear_tty ( tty - > session ) ;
} else {
ret = - EPERM ;
goto unlock ;
}
}
/* See the comment in tty_open_proc_set_tty(). */
if ( ( file - > f_mode & FMODE_READ ) = = 0 & & ! capable ( CAP_SYS_ADMIN ) ) {
ret = - EPERM ;
goto unlock ;
}
proc_set_tty ( tty ) ;
unlock :
read_unlock ( & tasklist_lock ) ;
tty_unlock ( tty ) ;
return ret ;
}
/**
* tty_get_pgrp - return a ref counted pgrp pid
* @ tty : tty to read
*
* Returns a refcounted instance of the pid struct for the process
* group controlling the tty .
*/
struct pid * tty_get_pgrp ( struct tty_struct * tty )
{
unsigned long flags ;
struct pid * pgrp ;
spin_lock_irqsave ( & tty - > ctrl_lock , flags ) ;
pgrp = get_pid ( tty - > pgrp ) ;
spin_unlock_irqrestore ( & tty - > ctrl_lock , flags ) ;
return pgrp ;
}
EXPORT_SYMBOL_GPL ( tty_get_pgrp ) ;
/*
* This checks not only the pgrp , but falls back on the pid if no
* satisfactory pgrp is found . I dunno - gdb doesn ' t work correctly
* without this . . .
*
* The caller must hold rcu lock or the tasklist lock .
*/
static struct pid * session_of_pgrp ( struct pid * pgrp )
{
struct task_struct * p ;
struct pid * sid = NULL ;
p = pid_task ( pgrp , PIDTYPE_PGID ) ;
if ( p = = NULL )
p = pid_task ( pgrp , PIDTYPE_PID ) ;
if ( p ! = NULL )
sid = task_session ( p ) ;
return sid ;
}
/**
* tiocgpgrp - get process group
* @ tty : tty passed by user
* @ real_tty : tty side of the tty passed by the user if a pty else the tty
* @ p : returned pid
*
* Obtain the process group of the tty . If there is no process group
* return an error .
*
* Locking : none . Reference to current - > signal - > tty is safe .
*/
static int tiocgpgrp ( struct tty_struct * tty , struct tty_struct * real_tty , pid_t __user * p )
{
struct pid * pid ;
int ret ;
/*
* ( tty = = real_tty ) is a cheap way of
* testing if the tty is NOT a master pty .
*/
if ( tty = = real_tty & & current - > signal - > tty ! = real_tty )
return - ENOTTY ;
pid = tty_get_pgrp ( real_tty ) ;
ret = put_user ( pid_vnr ( pid ) , p ) ;
put_pid ( pid ) ;
return ret ;
}
/**
* tiocspgrp - attempt to set process group
* @ tty : tty passed by user
* @ real_tty : tty side device matching tty passed by user
* @ p : pid pointer
*
* Set the process group of the tty to the session passed . Only
* permitted where the tty session is our session .
*
* Locking : RCU , ctrl lock
*/
static int tiocspgrp ( struct tty_struct * tty , struct tty_struct * real_tty , pid_t __user * p )
{
struct pid * pgrp ;
pid_t pgrp_nr ;
int retval = tty_check_change ( real_tty ) ;
if ( retval = = - EIO )
return - ENOTTY ;
if ( retval )
return retval ;
tty: Fix ->session locking
Currently, locking of ->session is very inconsistent; most places
protect it using the legacy tty mutex, but disassociate_ctty(),
__do_SAK(), tiocspgrp() and tiocgsid() don't.
Two of the writers hold the ctrl_lock (because they already need it for
->pgrp), but __proc_set_tty() doesn't do that yet.
On a PREEMPT=y system, an unprivileged user can theoretically abuse
this broken locking to read 4 bytes of freed memory via TIOCGSID if
tiocgsid() is preempted long enough at the right point. (Other things
might also go wrong, especially if root-only ioctls are involved; I'm
not sure about that.)
Change the locking on ->session such that:
- tty_lock() is held by all writers: By making disassociate_ctty()
hold it. This should be fine because the same lock can already be
taken through the call to tty_vhangup_session().
The tricky part is that we need to shorten the area covered by
siglock to be able to take tty_lock() without ugly retry logic; as
far as I can tell, this should be fine, since nothing in the
signal_struct is touched in the `if (tty)` branch.
- ctrl_lock is held by all writers: By changing __proc_set_tty() to
hold the lock a little longer.
- All readers that aren't holding tty_lock() hold ctrl_lock: By
adding locking to tiocgsid() and __do_SAK(), and expanding the area
covered by ctrl_lock in tiocspgrp().
Cc: stable@kernel.org
Signed-off-by: Jann Horn <jannh@google.com>
Reviewed-by: Jiri Slaby <jirislaby@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-12-03 04:25:05 +03:00
2017-04-13 01:37:16 +03:00
if ( get_user ( pgrp_nr , p ) )
return - EFAULT ;
if ( pgrp_nr < 0 )
return - EINVAL ;
tty: Fix ->session locking
Currently, locking of ->session is very inconsistent; most places
protect it using the legacy tty mutex, but disassociate_ctty(),
__do_SAK(), tiocspgrp() and tiocgsid() don't.
Two of the writers hold the ctrl_lock (because they already need it for
->pgrp), but __proc_set_tty() doesn't do that yet.
On a PREEMPT=y system, an unprivileged user can theoretically abuse
this broken locking to read 4 bytes of freed memory via TIOCGSID if
tiocgsid() is preempted long enough at the right point. (Other things
might also go wrong, especially if root-only ioctls are involved; I'm
not sure about that.)
Change the locking on ->session such that:
- tty_lock() is held by all writers: By making disassociate_ctty()
hold it. This should be fine because the same lock can already be
taken through the call to tty_vhangup_session().
The tricky part is that we need to shorten the area covered by
siglock to be able to take tty_lock() without ugly retry logic; as
far as I can tell, this should be fine, since nothing in the
signal_struct is touched in the `if (tty)` branch.
- ctrl_lock is held by all writers: By changing __proc_set_tty() to
hold the lock a little longer.
- All readers that aren't holding tty_lock() hold ctrl_lock: By
adding locking to tiocgsid() and __do_SAK(), and expanding the area
covered by ctrl_lock in tiocspgrp().
Cc: stable@kernel.org
Signed-off-by: Jann Horn <jannh@google.com>
Reviewed-by: Jiri Slaby <jirislaby@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-12-03 04:25:05 +03:00
spin_lock_irq ( & real_tty - > ctrl_lock ) ;
if ( ! current - > signal - > tty | |
( current - > signal - > tty ! = real_tty ) | |
( real_tty - > session ! = task_session ( current ) ) ) {
retval = - ENOTTY ;
goto out_unlock_ctrl ;
}
2017-04-13 01:37:16 +03:00
rcu_read_lock ( ) ;
pgrp = find_vpid ( pgrp_nr ) ;
retval = - ESRCH ;
if ( ! pgrp )
goto out_unlock ;
retval = - EPERM ;
if ( session_of_pgrp ( pgrp ) ! = task_session ( current ) )
goto out_unlock ;
retval = 0 ;
put_pid ( real_tty - > pgrp ) ;
real_tty - > pgrp = get_pid ( pgrp ) ;
out_unlock :
rcu_read_unlock ( ) ;
tty: Fix ->session locking
Currently, locking of ->session is very inconsistent; most places
protect it using the legacy tty mutex, but disassociate_ctty(),
__do_SAK(), tiocspgrp() and tiocgsid() don't.
Two of the writers hold the ctrl_lock (because they already need it for
->pgrp), but __proc_set_tty() doesn't do that yet.
On a PREEMPT=y system, an unprivileged user can theoretically abuse
this broken locking to read 4 bytes of freed memory via TIOCGSID if
tiocgsid() is preempted long enough at the right point. (Other things
might also go wrong, especially if root-only ioctls are involved; I'm
not sure about that.)
Change the locking on ->session such that:
- tty_lock() is held by all writers: By making disassociate_ctty()
hold it. This should be fine because the same lock can already be
taken through the call to tty_vhangup_session().
The tricky part is that we need to shorten the area covered by
siglock to be able to take tty_lock() without ugly retry logic; as
far as I can tell, this should be fine, since nothing in the
signal_struct is touched in the `if (tty)` branch.
- ctrl_lock is held by all writers: By changing __proc_set_tty() to
hold the lock a little longer.
- All readers that aren't holding tty_lock() hold ctrl_lock: By
adding locking to tiocgsid() and __do_SAK(), and expanding the area
covered by ctrl_lock in tiocspgrp().
Cc: stable@kernel.org
Signed-off-by: Jann Horn <jannh@google.com>
Reviewed-by: Jiri Slaby <jirislaby@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-12-03 04:25:05 +03:00
out_unlock_ctrl :
spin_unlock_irq ( & real_tty - > ctrl_lock ) ;
2017-04-13 01:37:16 +03:00
return retval ;
}
/**
* tiocgsid - get session id
* @ tty : tty passed by user
* @ real_tty : tty side of the tty passed by the user if a pty else the tty
* @ p : pointer to returned session id
*
* Obtain the session id of the tty . If there is no session
* return an error .
*/
static int tiocgsid ( struct tty_struct * tty , struct tty_struct * real_tty , pid_t __user * p )
{
tty: Fix ->session locking
Currently, locking of ->session is very inconsistent; most places
protect it using the legacy tty mutex, but disassociate_ctty(),
__do_SAK(), tiocspgrp() and tiocgsid() don't.
Two of the writers hold the ctrl_lock (because they already need it for
->pgrp), but __proc_set_tty() doesn't do that yet.
On a PREEMPT=y system, an unprivileged user can theoretically abuse
this broken locking to read 4 bytes of freed memory via TIOCGSID if
tiocgsid() is preempted long enough at the right point. (Other things
might also go wrong, especially if root-only ioctls are involved; I'm
not sure about that.)
Change the locking on ->session such that:
- tty_lock() is held by all writers: By making disassociate_ctty()
hold it. This should be fine because the same lock can already be
taken through the call to tty_vhangup_session().
The tricky part is that we need to shorten the area covered by
siglock to be able to take tty_lock() without ugly retry logic; as
far as I can tell, this should be fine, since nothing in the
signal_struct is touched in the `if (tty)` branch.
- ctrl_lock is held by all writers: By changing __proc_set_tty() to
hold the lock a little longer.
- All readers that aren't holding tty_lock() hold ctrl_lock: By
adding locking to tiocgsid() and __do_SAK(), and expanding the area
covered by ctrl_lock in tiocspgrp().
Cc: stable@kernel.org
Signed-off-by: Jann Horn <jannh@google.com>
Reviewed-by: Jiri Slaby <jirislaby@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-12-03 04:25:05 +03:00
unsigned long flags ;
pid_t sid ;
2017-04-13 01:37:16 +03:00
/*
* ( tty = = real_tty ) is a cheap way of
* testing if the tty is NOT a master pty .
*/
if ( tty = = real_tty & & current - > signal - > tty ! = real_tty )
return - ENOTTY ;
tty: Fix ->session locking
Currently, locking of ->session is very inconsistent; most places
protect it using the legacy tty mutex, but disassociate_ctty(),
__do_SAK(), tiocspgrp() and tiocgsid() don't.
Two of the writers hold the ctrl_lock (because they already need it for
->pgrp), but __proc_set_tty() doesn't do that yet.
On a PREEMPT=y system, an unprivileged user can theoretically abuse
this broken locking to read 4 bytes of freed memory via TIOCGSID if
tiocgsid() is preempted long enough at the right point. (Other things
might also go wrong, especially if root-only ioctls are involved; I'm
not sure about that.)
Change the locking on ->session such that:
- tty_lock() is held by all writers: By making disassociate_ctty()
hold it. This should be fine because the same lock can already be
taken through the call to tty_vhangup_session().
The tricky part is that we need to shorten the area covered by
siglock to be able to take tty_lock() without ugly retry logic; as
far as I can tell, this should be fine, since nothing in the
signal_struct is touched in the `if (tty)` branch.
- ctrl_lock is held by all writers: By changing __proc_set_tty() to
hold the lock a little longer.
- All readers that aren't holding tty_lock() hold ctrl_lock: By
adding locking to tiocgsid() and __do_SAK(), and expanding the area
covered by ctrl_lock in tiocspgrp().
Cc: stable@kernel.org
Signed-off-by: Jann Horn <jannh@google.com>
Reviewed-by: Jiri Slaby <jirislaby@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-12-03 04:25:05 +03:00
spin_lock_irqsave ( & real_tty - > ctrl_lock , flags ) ;
2017-04-13 01:37:16 +03:00
if ( ! real_tty - > session )
tty: Fix ->session locking
Currently, locking of ->session is very inconsistent; most places
protect it using the legacy tty mutex, but disassociate_ctty(),
__do_SAK(), tiocspgrp() and tiocgsid() don't.
Two of the writers hold the ctrl_lock (because they already need it for
->pgrp), but __proc_set_tty() doesn't do that yet.
On a PREEMPT=y system, an unprivileged user can theoretically abuse
this broken locking to read 4 bytes of freed memory via TIOCGSID if
tiocgsid() is preempted long enough at the right point. (Other things
might also go wrong, especially if root-only ioctls are involved; I'm
not sure about that.)
Change the locking on ->session such that:
- tty_lock() is held by all writers: By making disassociate_ctty()
hold it. This should be fine because the same lock can already be
taken through the call to tty_vhangup_session().
The tricky part is that we need to shorten the area covered by
siglock to be able to take tty_lock() without ugly retry logic; as
far as I can tell, this should be fine, since nothing in the
signal_struct is touched in the `if (tty)` branch.
- ctrl_lock is held by all writers: By changing __proc_set_tty() to
hold the lock a little longer.
- All readers that aren't holding tty_lock() hold ctrl_lock: By
adding locking to tiocgsid() and __do_SAK(), and expanding the area
covered by ctrl_lock in tiocspgrp().
Cc: stable@kernel.org
Signed-off-by: Jann Horn <jannh@google.com>
Reviewed-by: Jiri Slaby <jirislaby@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2020-12-03 04:25:05 +03:00
goto err ;
sid = pid_vnr ( real_tty - > session ) ;
spin_unlock_irqrestore ( & real_tty - > ctrl_lock , flags ) ;
return put_user ( sid , p ) ;
err :
spin_unlock_irqrestore ( & real_tty - > ctrl_lock , flags ) ;
return - ENOTTY ;
2017-04-13 01:37:16 +03:00
}
/*
* Called from tty_ioctl ( ) . If tty is a pty then real_tty is the slave side ,
* if not then tty = = real_tty .
*/
long tty_jobctrl_ioctl ( struct tty_struct * tty , struct tty_struct * real_tty ,
struct file * file , unsigned int cmd , unsigned long arg )
{
void __user * p = ( void __user * ) arg ;
switch ( cmd ) {
case TIOCNOTTY :
if ( current - > signal - > tty ! = tty )
return - ENOTTY ;
no_tty ( ) ;
return 0 ;
case TIOCSCTTY :
return tiocsctty ( real_tty , file , arg ) ;
case TIOCGPGRP :
return tiocgpgrp ( tty , real_tty , p ) ;
case TIOCSPGRP :
return tiocspgrp ( tty , real_tty , p ) ;
case TIOCGSID :
return tiocgsid ( tty , real_tty , p ) ;
}
return - ENOIOCTLCMD ;
}