2005-04-17 02:20:36 +04:00
/*
* Handling of different ABIs ( personalities ) .
*
* We group personalities into execution domains which have their
* own handlers for kernel entry points , signal mapping , etc . . .
*
* 2001 - 05 - 06 Complete rewrite , Christoph Hellwig ( hch @ infradead . org )
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/kmod.h>
# include <linux/module.h>
# include <linux/personality.h>
# include <linux/sched.h>
# include <linux/syscalls.h>
# include <linux/sysctl.h>
# include <linux/types.h>
static void default_handler ( int , struct pt_regs * ) ;
static struct exec_domain * exec_domains = & default_exec_domain ;
static DEFINE_RWLOCK ( exec_domains_lock ) ;
static u_long ident_map [ 32 ] = {
0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ,
8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 ,
16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 ,
24 , 25 , 26 , 27 , 28 , 29 , 30 , 31
} ;
struct exec_domain default_exec_domain = {
. name = " Linux " , /* name */
. handler = default_handler , /* lcall7 causes a seg fault. */
. pers_low = 0 , /* PER_LINUX personality. */
. pers_high = 0 , /* PER_LINUX personality. */
. signal_map = ident_map , /* Identity map signals. */
. signal_invmap = ident_map , /* - both ways. */
} ;
static void
default_handler ( int segment , struct pt_regs * regp )
{
set_personality ( 0 ) ;
if ( current_thread_info ( ) - > exec_domain - > handler ! = default_handler )
current_thread_info ( ) - > exec_domain - > handler ( segment , regp ) ;
else
send_sig ( SIGSEGV , current , 1 ) ;
}
static struct exec_domain *
lookup_exec_domain ( u_long personality )
{
struct exec_domain * ep ;
u_long pers = personality ( personality ) ;
read_lock ( & exec_domains_lock ) ;
for ( ep = exec_domains ; ep ; ep = ep - > next ) {
if ( pers > = ep - > pers_low & & pers < = ep - > pers_high )
if ( try_module_get ( ep - > module ) )
goto out ;
}
# ifdef CONFIG_KMOD
read_unlock ( & exec_domains_lock ) ;
request_module ( " personality-%ld " , pers ) ;
read_lock ( & exec_domains_lock ) ;
for ( ep = exec_domains ; ep ; ep = ep - > next ) {
if ( pers > = ep - > pers_low & & pers < = ep - > pers_high )
if ( try_module_get ( ep - > module ) )
goto out ;
}
# endif
ep = & default_exec_domain ;
out :
read_unlock ( & exec_domains_lock ) ;
return ( ep ) ;
}
int
register_exec_domain ( struct exec_domain * ep )
{
struct exec_domain * tmp ;
int err = - EBUSY ;
if ( ep = = NULL )
return - EINVAL ;
if ( ep - > next ! = NULL )
return - EBUSY ;
write_lock ( & exec_domains_lock ) ;
for ( tmp = exec_domains ; tmp ; tmp = tmp - > next ) {
if ( tmp = = ep )
goto out ;
}
ep - > next = exec_domains ;
exec_domains = ep ;
err = 0 ;
out :
write_unlock ( & exec_domains_lock ) ;
return ( err ) ;
}
int
unregister_exec_domain ( struct exec_domain * ep )
{
struct exec_domain * * epp ;
epp = & exec_domains ;
write_lock ( & exec_domains_lock ) ;
for ( epp = & exec_domains ; * epp ; epp = & ( * epp ) - > next ) {
if ( ep = = * epp )
goto unregister ;
}
write_unlock ( & exec_domains_lock ) ;
return - EINVAL ;
unregister :
* epp = ep - > next ;
ep - > next = NULL ;
write_unlock ( & exec_domains_lock ) ;
return 0 ;
}
int
__set_personality ( u_long personality )
{
struct exec_domain * ep , * oep ;
ep = lookup_exec_domain ( personality ) ;
if ( ep = = current_thread_info ( ) - > exec_domain ) {
current - > personality = personality ;
2006-03-24 14:18:38 +03:00
module_put ( ep - > module ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
if ( atomic_read ( & current - > fs - > count ) ! = 1 ) {
struct fs_struct * fsp , * ofsp ;
fsp = copy_fs_struct ( current - > fs ) ;
if ( fsp = = NULL ) {
module_put ( ep - > module ) ;
return - ENOMEM ;
}
task_lock ( current ) ;
ofsp = current - > fs ;
current - > fs = fsp ;
task_unlock ( current ) ;
put_fs_struct ( ofsp ) ;
}
/*
* At that point we are guaranteed to be the sole owner of
* current - > fs .
*/
current - > personality = personality ;
oep = current_thread_info ( ) - > exec_domain ;
current_thread_info ( ) - > exec_domain = ep ;
set_fs_altroot ( ) ;
module_put ( oep - > module ) ;
return 0 ;
}
int
get_exec_domain_list ( char * page )
{
struct exec_domain * ep ;
int len = 0 ;
read_lock ( & exec_domains_lock ) ;
for ( ep = exec_domains ; ep & & len < PAGE_SIZE - 80 ; ep = ep - > next )
len + = sprintf ( page + len , " %d-%d \t %-16s \t [%s] \n " ,
ep - > pers_low , ep - > pers_high , ep - > name ,
module_name ( ep - > module ) ) ;
read_unlock ( & exec_domains_lock ) ;
return ( len ) ;
}
asmlinkage long
sys_personality ( u_long personality )
{
u_long old = current - > personality ;
if ( personality ! = 0xffffffff ) {
set_personality ( personality ) ;
if ( current - > personality ! = personality )
return - EINVAL ;
}
return ( long ) old ;
}
EXPORT_SYMBOL ( register_exec_domain ) ;
EXPORT_SYMBOL ( unregister_exec_domain ) ;
EXPORT_SYMBOL ( __set_personality ) ;