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>
# include <asm/uaccess.h>
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
}
asmlinkage long
compat_sys_set_robust_list ( struct compat_robust_list_head __user * head ,
compat_size_t len )
{
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 ;
}
asmlinkage long
2006-10-11 01:46:07 +04:00
compat_sys_get_robust_list ( int pid , compat_uptr_t __user * head_ptr ,
2006-03-27 13:16:24 +04:00
compat_size_t __user * len_ptr )
{
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 ;
2008-11-14 02:39:19 +03:00
const struct cred * cred = current_cred ( ) , * pcred ;
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 ( ! pid )
head = current - > compat_robust_list ;
else {
struct task_struct * p ;
ret = - ESRCH ;
2009-12-01 16:02:00 +03:00
rcu_read_lock ( ) ;
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 ;
ret = - EPERM ;
2008-11-14 02:39:19 +03:00
pcred = __task_cred ( p ) ;
2011-03-24 02:43:24 +03:00
/* If victim is in different user_ns, then uids are not
comparable , so we must have CAP_SYS_PTRACE */
if ( cred - > user - > user_ns ! = pcred - > user - > user_ns ) {
if ( ! ns_capable ( pcred - > user - > user_ns , CAP_SYS_PTRACE ) )
goto err_unlock ;
goto ok ;
}
/* If victim is in same user_ns, then uids are comparable */
2008-11-14 02:39:19 +03:00
if ( cred - > euid ! = pcred - > euid & &
cred - > euid ! = pcred - > uid & &
2011-03-24 02:43:24 +03:00
! ns_capable ( pcred - > user - > user_ns , CAP_SYS_PTRACE ) )
2006-03-27 13:16:24 +04:00
goto err_unlock ;
2011-03-24 02:43:24 +03:00
ok :
2006-03-27 13:16:24 +04:00
head = p - > compat_robust_list ;
2009-12-01 16:02:00 +03:00
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 ;
}
2006-03-27 13:16:27 +04:00
asmlinkage long compat_sys_futex ( u32 __user * uaddr , int op , u32 val ,
2006-03-27 13:16:24 +04:00
struct compat_timespec __user * utime , u32 __user * uaddr2 ,
2006-03-27 13:16:27 +04:00
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 ) ) {
2007-05-09 13:35:02 +04:00
if ( get_compat_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
}