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>
2008-10-04 14:28:09 +04:00
# include <linux/proc_fs.h>
2005-04-17 02:20:36 +04:00
# include <linux/sched.h>
2008-10-04 14:28:09 +04:00
# include <linux/seq_file.h>
2005-04-17 02:20:36 +04:00
# include <linux/syscalls.h>
# include <linux/sysctl.h>
# include <linux/types.h>
2009-03-30 03:50:06 +04:00
# include <linux/fs_struct.h>
2005-04-17 02:20:36 +04:00
static void default_handler ( int , struct pt_regs * ) ;
static struct exec_domain * exec_domains = & default_exec_domain ;
static DEFINE_RWLOCK ( exec_domains_lock ) ;
2010-06-05 01:14:58 +04:00
static unsigned long ident_map [ 32 ] = {
2005-04-17 02:20:36 +04:00
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 *
2010-06-05 01:14:58 +04:00
lookup_exec_domain ( unsigned int personality )
2005-04-17 02:20:36 +04:00
{
2010-06-05 01:14:58 +04:00
unsigned int pers = personality ( personality ) ;
struct exec_domain * ep ;
2007-10-18 14:06:10 +04:00
2005-04-17 02:20:36 +04:00
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 ;
}
2008-07-08 21:00:17 +04:00
# ifdef CONFIG_MODULES
2005-04-17 02:20:36 +04:00
read_unlock ( & exec_domains_lock ) ;
2010-06-05 01:14:58 +04:00
request_module ( " personality-%d " , pers ) ;
2005-04-17 02:20:36 +04:00
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
2010-06-05 01:14:58 +04:00
__set_personality ( unsigned int personality )
2005-04-17 02:20:36 +04:00
{
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 ;
}
current - > personality = personality ;
oep = current_thread_info ( ) - > exec_domain ;
current_thread_info ( ) - > exec_domain = ep ;
module_put ( oep - > module ) ;
return 0 ;
}
2008-10-04 14:28:09 +04:00
# ifdef CONFIG_PROC_FS
static int execdomains_proc_show ( struct seq_file * m , void * v )
2005-04-17 02:20:36 +04:00
{
struct exec_domain * ep ;
read_lock ( & exec_domains_lock ) ;
2008-10-04 14:28:09 +04:00
for ( ep = exec_domains ; ep ; ep = ep - > next )
seq_printf ( m , " %d-%d \t %-16s \t [%s] \n " ,
2005-04-17 02:20:36 +04:00
ep - > pers_low , ep - > pers_high , ep - > name ,
module_name ( ep - > module ) ) ;
read_unlock ( & exec_domains_lock ) ;
2008-10-04 14:28:09 +04:00
return 0 ;
}
static int execdomains_proc_open ( struct inode * inode , struct file * file )
{
return single_open ( file , execdomains_proc_show , NULL ) ;
}
static const struct file_operations execdomains_proc_fops = {
. open = execdomains_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
static int __init proc_execdomains_init ( void )
{
proc_create ( " execdomains " , 0 , NULL , & execdomains_proc_fops ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-10-04 14:28:09 +04:00
module_init ( proc_execdomains_init ) ;
# endif
2005-04-17 02:20:36 +04:00
2010-06-05 01:14:58 +04:00
SYSCALL_DEFINE1 ( personality , unsigned int , personality )
2005-04-17 02:20:36 +04:00
{
2010-06-05 01:14:58 +04:00
unsigned int old = current - > personality ;
2005-04-17 02:20:36 +04:00
if ( personality ! = 0xffffffff ) {
set_personality ( personality ) ;
if ( current - > personality ! = personality )
return - EINVAL ;
}
2010-06-05 01:14:58 +04:00
return old ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( register_exec_domain ) ;
EXPORT_SYMBOL ( unregister_exec_domain ) ;
EXPORT_SYMBOL ( __set_personality ) ;