2006-03-27 13:16:24 +04:00
/*
* linux / kernel / futex_compat . c
*
* Futex compatibililty routines .
*
* Copyright 2006 , Red Hat , Inc . , Ingo Molnar
*/
# include <linux/linkage.h>
# include <linux/compat.h>
2007-10-19 10:40:14 +04:00
# include <linux/nsproxy.h>
2006-03-27 13:16:24 +04:00
# include <linux/futex.h>
2012-03-20 03:12:53 +04:00
# include <linux/ptrace.h>
2012-12-26 08:16:10 +04:00
# include <linux/syscalls.h>
2006-03-27 13:16:24 +04:00
2016-12-24 22:46:01 +03:00
# include <linux/uaccess.h>
2006-03-27 13:16:24 +04:00
2006-07-29 07:17:57 +04:00
/*
* Fetch a robust - list pointer . Bit 0 signals PI futexes :
*/
static inline int
fetch_robust_entry ( compat_uptr_t * uentry , struct robust_list __user * * entry ,
2010-09-14 16:43:46 +04:00
compat_uptr_t __user * head , unsigned int * pi )
2006-07-29 07:17:57 +04:00
{
if ( get_user ( * uentry , head ) )
return - EFAULT ;
* entry = compat_ptr ( ( * uentry ) & ~ 1 ) ;
* pi = ( unsigned int ) ( * uentry ) & 1 ;
return 0 ;
}
2008-03-29 06:07:58 +03:00
static void __user * futex_uaddr ( struct robust_list __user * entry ,
2007-11-07 08:13:56 +03:00
compat_long_t futex_offset )
{
compat_uptr_t base = ptr_to_compat ( entry ) ;
void __user * uaddr = compat_ptr ( base + futex_offset ) ;
return uaddr ;
}
2006-03-27 13:16:24 +04:00
/*
* Walk curr - > robust_list ( very carefully , it ' s a userspace list ! )
* and mark any locks found there dead , and notify any waiters .
*
* We silently return on any sign of list - walking problem .
*/
void compat_exit_robust_list ( struct task_struct * curr )
{
struct compat_robust_list_head __user * head = curr - > compat_robust_list ;
2007-10-01 12:20:13 +04:00
struct robust_list __user * entry , * next_entry , * pending ;
2010-11-04 22:00:00 +03:00
unsigned int limit = ROBUST_LIST_LIMIT , pi , pip ;
unsigned int uninitialized_var ( next_pi ) ;
2007-10-01 12:20:13 +04:00
compat_uptr_t uentry , next_uentry , upending ;
2006-03-27 13:16:24 +04:00
compat_long_t futex_offset ;
2007-10-01 12:20:13 +04:00
int rc ;
2006-03-27 13:16:24 +04:00
2008-02-24 02:23:57 +03:00
if ( ! futex_cmpxchg_enabled )
return ;
2006-03-27 13:16:24 +04:00
/*
* Fetch the list head ( which was registered earlier , via
* sys_set_robust_list ( ) ) :
*/
2006-07-29 07:17:57 +04:00
if ( fetch_robust_entry ( & uentry , & entry , & head - > list . next , & pi ) )
2006-03-27 13:16:24 +04:00
return ;
/*
* Fetch the relative futex offset :
*/
if ( get_user ( futex_offset , & head - > futex_offset ) )
return ;
/*
* Fetch any possibly pending lock - add first , and handle it
* if it exists :
*/
2006-07-29 07:17:57 +04:00
if ( fetch_robust_entry ( & upending , & pending ,
2006-08-05 23:15:15 +04:00
& head - > list_op_pending , & pip ) )
2006-03-27 13:16:24 +04:00
return ;
2007-10-01 12:20:13 +04:00
next_entry = NULL ; /* avoid warning with gcc */
2007-09-12 02:23:49 +04:00
while ( entry ! = ( struct robust_list __user * ) & head - > list ) {
2007-10-01 12:20:13 +04:00
/*
* Fetch the next entry in the list before calling
* handle_futex_death :
*/
rc = fetch_robust_entry ( & next_uentry , & next_entry ,
( compat_uptr_t __user * ) & entry - > next , & next_pi ) ;
2006-03-27 13:16:24 +04:00
/*
* A pending lock might already be on the list , so
* dont process it twice :
*/
2007-11-07 08:13:56 +03:00
if ( entry ! = pending ) {
void __user * uaddr = futex_uaddr ( entry , futex_offset ) ;
2006-03-27 13:16:24 +04:00
2007-11-07 08:13:56 +03:00
if ( handle_futex_death ( uaddr , curr , pi ) )
return ;
}
2007-10-01 12:20:13 +04:00
if ( rc )
2006-03-27 13:16:24 +04:00
return ;
2007-10-01 12:20:13 +04:00
uentry = next_uentry ;
entry = next_entry ;
pi = next_pi ;
2006-03-27 13:16:24 +04:00
/*
* Avoid excessively long or circular lists :
*/
if ( ! - - limit )
break ;
cond_resched ( ) ;
}
2007-11-07 08:13:56 +03:00
if ( pending ) {
void __user * uaddr = futex_uaddr ( pending , futex_offset ) ;
handle_futex_death ( uaddr , curr , pip ) ;
}
2006-03-27 13:16:24 +04:00
}
2012-12-26 08:16:10 +04:00
COMPAT_SYSCALL_DEFINE2 ( set_robust_list ,
struct compat_robust_list_head __user * , head ,
compat_size_t , len )
2006-03-27 13:16:24 +04:00
{
2008-02-24 02:23:57 +03:00
if ( ! futex_cmpxchg_enabled )
return - ENOSYS ;
2006-03-27 13:16:24 +04:00
if ( unlikely ( len ! = sizeof ( * head ) ) )
return - EINVAL ;
current - > compat_robust_list = head ;
return 0 ;
}
2012-12-26 08:16:10 +04:00
COMPAT_SYSCALL_DEFINE3 ( get_robust_list , int , pid ,
compat_uptr_t __user * , head_ptr ,
compat_size_t __user * , len_ptr )
2006-03-27 13:16:24 +04:00
{
2006-10-11 01:46:07 +04:00
struct compat_robust_list_head __user * head ;
2006-03-27 13:16:24 +04:00
unsigned long ret ;
2012-03-20 03:12:53 +04:00
struct task_struct * p ;
2006-03-27 13:16:24 +04:00
2008-02-24 02:23:57 +03:00
if ( ! futex_cmpxchg_enabled )
return - ENOSYS ;
2012-03-20 03:12:53 +04:00
rcu_read_lock ( ) ;
ret = - ESRCH ;
2006-03-27 13:16:24 +04:00
if ( ! pid )
2012-03-20 03:12:53 +04:00
p = current ;
2006-03-27 13:16:24 +04:00
else {
2007-10-19 10:40:16 +04:00
p = find_task_by_vpid ( pid ) ;
2006-03-27 13:16:24 +04:00
if ( ! p )
goto err_unlock ;
}
2012-03-20 03:12:53 +04:00
ret = - EPERM ;
ptrace: use fsuid, fsgid, effective creds for fs access checks
By checking the effective credentials instead of the real UID / permitted
capabilities, ensure that the calling process actually intended to use its
credentials.
To ensure that all ptrace checks use the correct caller credentials (e.g.
in case out-of-tree code or newly added code omits the PTRACE_MODE_*CREDS
flag), use two new flags and require one of them to be set.
The problem was that when a privileged task had temporarily dropped its
privileges, e.g. by calling setreuid(0, user_uid), with the intent to
perform following syscalls with the credentials of a user, it still passed
ptrace access checks that the user would not be able to pass.
While an attacker should not be able to convince the privileged task to
perform a ptrace() syscall, this is a problem because the ptrace access
check is reused for things in procfs.
In particular, the following somewhat interesting procfs entries only rely
on ptrace access checks:
/proc/$pid/stat - uses the check for determining whether pointers
should be visible, useful for bypassing ASLR
/proc/$pid/maps - also useful for bypassing ASLR
/proc/$pid/cwd - useful for gaining access to restricted
directories that contain files with lax permissions, e.g. in
this scenario:
lrwxrwxrwx root root /proc/13020/cwd -> /root/foobar
drwx------ root root /root
drwxr-xr-x root root /root/foobar
-rw-r--r-- root root /root/foobar/secret
Therefore, on a system where a root-owned mode 6755 binary changes its
effective credentials as described and then dumps a user-specified file,
this could be used by an attacker to reveal the memory layout of root's
processes or reveal the contents of files he is not allowed to access
(through /proc/$pid/cwd).
[akpm@linux-foundation.org: fix warning]
Signed-off-by: Jann Horn <jann@thejh.net>
Acked-by: Kees Cook <keescook@chromium.org>
Cc: Casey Schaufler <casey@schaufler-ca.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Morris <james.l.morris@oracle.com>
Cc: "Serge E. Hallyn" <serge.hallyn@ubuntu.com>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Willy Tarreau <w@1wt.eu>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2016-01-21 02:00:04 +03:00
if ( ! ptrace_may_access ( p , PTRACE_MODE_READ_REALCREDS ) )
2012-03-20 03:12:53 +04:00
goto err_unlock ;
head = p - > compat_robust_list ;
rcu_read_unlock ( ) ;
2006-03-27 13:16:24 +04:00
if ( put_user ( sizeof ( * head ) , len_ptr ) )
return - EFAULT ;
return put_user ( ptr_to_compat ( head ) , head_ptr ) ;
err_unlock :
2009-12-01 16:02:00 +03:00
rcu_read_unlock ( ) ;
2006-03-27 13:16:24 +04:00
return ret ;
}
2012-12-26 08:16:10 +04:00
COMPAT_SYSCALL_DEFINE6 ( futex , u32 __user * , uaddr , int , op , u32 , val ,
struct compat_timespec __user * , utime , u32 __user * , uaddr2 ,
u32 , val3 )
2006-03-27 13:16:24 +04:00
{
2007-05-09 13:35:02 +04:00
struct timespec ts ;
ktime_t t , * tp = NULL ;
2006-03-27 13:16:24 +04:00
int val2 = 0 ;
2007-06-01 11:46:41 +04:00
int cmd = op & FUTEX_CMD_MASK ;
2006-03-27 13:16:24 +04:00
2008-02-01 19:45:14 +03:00
if ( utime & & ( cmd = = FUTEX_WAIT | | cmd = = FUTEX_LOCK_PI | |
2009-08-10 17:01:42 +04:00
cmd = = FUTEX_WAIT_BITSET | |
cmd = = FUTEX_WAIT_REQUEUE_PI ) ) {
2014-02-02 06:54:11 +04:00
if ( compat_get_timespec ( & ts , utime ) )
2006-03-27 13:16:24 +04:00
return - EFAULT ;
2007-05-09 13:35:02 +04:00
if ( ! timespec_valid ( & ts ) )
2006-03-31 14:31:32 +04:00
return - EINVAL ;
2007-05-09 13:35:02 +04:00
t = timespec_to_ktime ( ts ) ;
2007-06-01 11:46:41 +04:00
if ( cmd = = FUTEX_WAIT )
2008-02-13 11:20:43 +03:00
t = ktime_add_safe ( ktime_get ( ) , t ) ;
2007-05-09 13:35:02 +04:00
tp = & t ;
2006-03-27 13:16:24 +04:00
}
2009-08-10 17:01:42 +04:00
if ( cmd = = FUTEX_REQUEUE | | cmd = = FUTEX_CMP_REQUEUE | |
cmd = = FUTEX_CMP_REQUEUE_PI | | cmd = = FUTEX_WAKE_OP )
2006-03-27 13:16:24 +04:00
val2 = ( int ) ( unsigned long ) utime ;
2007-05-09 13:35:02 +04:00
return do_futex ( uaddr , op , val , tp , uaddr2 , val2 , val3 ) ;
2006-03-27 13:16:24 +04:00
}