2005-04-17 02:20:36 +04:00
/*
* linux / ipc / util . c
* Copyright ( C ) 1992 Krishna Balasubramanian
*
* Sep 1997 - Call suser ( ) last after " normal " permission checks so we
* get BSD style process accounting right .
* Occurs in several places in the IPC code .
* Chris Evans , < chris @ ferret . lmh . ox . ac . uk >
* Nov 1999 - ipc helper functions , unified SMP locking
2006-01-15 04:43:54 +03:00
* Manfred Spraul < manfred @ colorfullife . com >
2005-04-17 02:20:36 +04:00
* Oct 2002 - One lock per IPC id . RCU ipc_free for lock - free grow_ary ( ) .
* Mingming Cao < cmm @ us . ibm . com >
2006-04-03 01:07:33 +04:00
* Mar 2006 - support for audit of ipc object properties
* Dustin Kirkland < dustin . kirkland @ us . ibm . com >
2006-10-02 13:18:20 +04:00
* Jun 2006 - namespaces ssupport
* OpenVZ , SWsoft Inc .
* Pavel Emelianov < xemul @ openvz . org >
2005-04-17 02:20:36 +04:00
*/
# include <linux/mm.h>
# include <linux/shm.h>
# include <linux/init.h>
# include <linux/msg.h>
# include <linux/vmalloc.h>
# include <linux/slab.h>
2006-01-11 23:17:46 +03:00
# include <linux/capability.h>
2005-04-17 02:20:36 +04:00
# include <linux/highuid.h>
# include <linux/security.h>
# include <linux/rcupdate.h>
# include <linux/workqueue.h>
2005-09-07 02:17:09 +04:00
# include <linux/seq_file.h>
# include <linux/proc_fs.h>
2006-04-03 01:07:33 +04:00
# include <linux/audit.h>
2006-10-02 13:18:20 +04:00
# include <linux/nsproxy.h>
2007-10-19 10:40:54 +04:00
# include <linux/rwsem.h>
2008-04-29 12:00:42 +04:00
# include <linux/memory.h>
2008-02-08 15:18:22 +03:00
# include <linux/ipc_namespace.h>
2005-04-17 02:20:36 +04:00
# include <asm/unistd.h>
# include "util.h"
2005-09-07 02:17:09 +04:00
struct ipc_proc_iface {
const char * path ;
const char * header ;
2006-10-02 13:18:20 +04:00
int ids ;
2005-09-07 02:17:09 +04:00
int ( * show ) ( struct seq_file * , void * ) ;
} ;
2008-04-29 12:00:42 +04:00
# ifdef CONFIG_MEMORY_HOTPLUG
2008-04-29 12:00:43 +04:00
static void ipc_memory_notifier ( struct work_struct * work )
{
ipcns_notify ( IPCNS_MEMCHANGED ) ;
}
static DECLARE_WORK ( ipc_memory_wq , ipc_memory_notifier ) ;
2008-04-29 12:00:42 +04:00
static int ipc_memory_callback ( struct notifier_block * self ,
unsigned long action , void * arg )
{
switch ( action ) {
case MEM_ONLINE : /* memory successfully brought online */
case MEM_OFFLINE : /* or offline: it's time to recompute msgmni */
/*
* This is done by invoking the ipcns notifier chain with the
* IPC_MEMCHANGED event .
2008-04-29 12:00:43 +04:00
* In order not to keep the lock on the hotplug memory chain
* for too long , queue a work item that will , when waken up ,
* activate the ipcns notification chain .
* No need to keep several ipc work items on the queue .
2008-04-29 12:00:42 +04:00
*/
2008-04-29 12:00:43 +04:00
if ( ! work_pending ( & ipc_memory_wq ) )
schedule_work ( & ipc_memory_wq ) ;
2008-04-29 12:00:42 +04:00
break ;
case MEM_GOING_ONLINE :
case MEM_GOING_OFFLINE :
case MEM_CANCEL_ONLINE :
case MEM_CANCEL_OFFLINE :
default :
break ;
}
return NOTIFY_OK ;
}
# endif /* CONFIG_MEMORY_HOTPLUG */
2005-04-17 02:20:36 +04:00
/**
* ipc_init - initialise IPC subsystem
*
* The various system5 IPC resources ( semaphores , messages and shared
2007-02-10 12:45:59 +03:00
* memory ) are initialised
2008-04-29 12:00:42 +04:00
* A callback routine is registered into the memory hotplug notifier
* chain : since msgmni scales to lowmem this callback routine will be
* called upon successful memory add / remove to recompute msmgni .
2005-04-17 02:20:36 +04:00
*/
static int __init ipc_init ( void )
{
sem_init ( ) ;
msg_init ( ) ;
shm_init ( ) ;
2008-04-29 12:00:42 +04:00
hotplug_memory_notifier ( ipc_memory_callback , IPC_CALLBACK_PRI ) ;
register_ipcns_notifier ( & init_ipc_ns ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
__initcall ( ipc_init ) ;
/**
* ipc_init_ids - initialise IPC identifiers
* @ ids : Identifier set
*
2007-10-19 10:40:48 +04:00
* Set up the sequence range to use for the ipc identifier range ( limited
* below IPCMNI ) then initialise the ids idr .
2005-04-17 02:20:36 +04:00
*/
2007-10-19 10:40:48 +04:00
void ipc_init_ids ( struct ipc_ids * ids )
2005-04-17 02:20:36 +04:00
{
2007-10-19 10:40:54 +04:00
init_rwsem ( & ids - > rw_mutex ) ;
2005-04-17 02:20:36 +04:00
ids - > in_use = 0 ;
ids - > seq = 0 ;
{
int seq_limit = INT_MAX / SEQ_MULTIPLIER ;
2010-05-25 01:33:03 +04:00
if ( seq_limit > USHRT_MAX )
ids - > seq_max = USHRT_MAX ;
2005-04-17 02:20:36 +04:00
else
ids - > seq_max = seq_limit ;
}
2007-10-19 10:40:48 +04:00
idr_init ( & ids - > ipcs_idr ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-07 02:17:09 +04:00
# ifdef CONFIG_PROC_FS
2007-02-12 11:55:35 +03:00
static const struct file_operations sysvipc_proc_fops ;
2005-09-07 02:17:09 +04:00
/**
2007-02-10 12:45:59 +03:00
* ipc_init_proc_interface - Create a proc interface for sysipc types using a seq_file interface .
2005-09-07 02:17:09 +04:00
* @ path : Path in procfs
* @ header : Banner to be printed at the beginning of the file .
* @ ids : ipc id table to iterate .
* @ show : show routine .
*/
void __init ipc_init_proc_interface ( const char * path , const char * header ,
2006-10-02 13:18:20 +04:00
int ids , int ( * show ) ( struct seq_file * , void * ) )
2005-09-07 02:17:09 +04:00
{
struct proc_dir_entry * pde ;
struct ipc_proc_iface * iface ;
iface = kmalloc ( sizeof ( * iface ) , GFP_KERNEL ) ;
if ( ! iface )
return ;
iface - > path = path ;
iface - > header = header ;
iface - > ids = ids ;
iface - > show = show ;
2008-04-29 12:02:12 +04:00
pde = proc_create_data ( path ,
S_IRUGO , /* world readable */
NULL , /* parent dir */
& sysvipc_proc_fops ,
iface ) ;
if ( ! pde ) {
2005-09-07 02:17:09 +04:00
kfree ( iface ) ;
}
}
# endif
2005-04-17 02:20:36 +04:00
/**
* ipc_findkey - find a key in an ipc identifier set
* @ ids : Identifier set
* @ key : The key to find
*
2007-10-19 10:40:54 +04:00
* Requires ipc_ids . rw_mutex locked .
2007-10-19 10:40:48 +04:00
* Returns the LOCKED pointer to the ipc structure if found or NULL
* if not .
2007-10-19 10:40:53 +04:00
* If key is found ipc points to the owning ipc structure
2005-04-17 02:20:36 +04:00
*/
2007-10-19 10:40:49 +04:00
static struct kern_ipc_perm * ipc_findkey ( struct ipc_ids * ids , key_t key )
2005-04-17 02:20:36 +04:00
{
2007-10-19 10:40:48 +04:00
struct kern_ipc_perm * ipc ;
int next_id ;
int total ;
2005-04-17 02:20:36 +04:00
2007-10-19 10:40:48 +04:00
for ( total = 0 , next_id = 0 ; total < ids - > in_use ; next_id + + ) {
ipc = idr_find ( & ids - > ipcs_idr , next_id ) ;
if ( ipc = = NULL )
2005-04-17 02:20:36 +04:00
continue ;
2007-10-19 10:40:48 +04:00
if ( ipc - > key ! = key ) {
total + + ;
continue ;
}
ipc_lock_by_ptr ( ipc ) ;
return ipc ;
2005-04-17 02:20:36 +04:00
}
2007-10-19 10:40:48 +04:00
return NULL ;
2005-04-17 02:20:36 +04:00
}
2007-10-19 10:40:48 +04:00
/**
* ipc_get_maxid - get the last assigned id
* @ ids : IPC identifier set
*
2007-10-19 10:40:54 +04:00
* Called with ipc_ids . rw_mutex held .
2005-04-17 02:20:36 +04:00
*/
2007-10-19 10:40:48 +04:00
int ipc_get_maxid ( struct ipc_ids * ids )
{
struct kern_ipc_perm * ipc ;
int max_id = - 1 ;
int total , id ;
if ( ids - > in_use = = 0 )
return - 1 ;
2005-04-17 02:20:36 +04:00
2007-10-19 10:40:48 +04:00
if ( ids - > in_use = = IPCMNI )
return IPCMNI - 1 ;
/* Look for the last assigned id */
total = 0 ;
for ( id = 0 ; id < IPCMNI & & total < ids - > in_use ; id + + ) {
ipc = idr_find ( & ids - > ipcs_idr , id ) ;
if ( ipc ! = NULL ) {
max_id = id ;
total + + ;
}
}
return max_id ;
2005-04-17 02:20:36 +04:00
}
/**
* ipc_addid - add an IPC identifier
* @ ids : IPC identifier set
* @ new : new IPC permission set
2007-10-19 10:40:48 +04:00
* @ size : limit for the number of used ids
2005-04-17 02:20:36 +04:00
*
2007-10-19 10:40:53 +04:00
* Add an entry ' new ' to the IPC ids idr . The permissions object is
2005-04-17 02:20:36 +04:00
* initialised and the first free entry is set up and the id assigned
2007-10-19 10:40:53 +04:00
* is returned . The ' new ' entry is returned in a locked state on success .
2007-10-19 10:40:57 +04:00
* On failure the entry is not locked and a negative err - code is returned .
2005-04-17 02:20:36 +04:00
*
2007-10-19 10:40:54 +04:00
* Called with ipc_ids . rw_mutex held as a writer .
2005-04-17 02:20:36 +04:00
*/
int ipc_addid ( struct ipc_ids * ids , struct kern_ipc_perm * new , int size )
{
2008-11-14 02:39:06 +03:00
uid_t euid ;
gid_t egid ;
2007-10-19 10:40:48 +04:00
int id , err ;
2005-04-17 02:20:36 +04:00
2007-10-19 10:40:48 +04:00
if ( size > IPCMNI )
size = IPCMNI ;
if ( ids - > in_use > = size )
2007-10-19 10:40:57 +04:00
return - ENOSPC ;
2007-10-19 10:40:48 +04:00
2008-11-20 02:36:08 +03:00
spin_lock_init ( & new - > lock ) ;
new - > deleted = 0 ;
rcu_read_lock ( ) ;
spin_lock ( & new - > lock ) ;
2007-10-19 10:40:48 +04:00
err = idr_get_new ( & ids - > ipcs_idr , new , & id ) ;
2008-11-20 02:36:08 +03:00
if ( err ) {
spin_unlock ( & new - > lock ) ;
rcu_read_unlock ( ) ;
2007-10-19 10:40:57 +04:00
return err ;
2008-11-20 02:36:08 +03:00
}
2007-10-19 10:40:48 +04:00
2005-04-17 02:20:36 +04:00
ids - > in_use + + ;
2008-11-14 02:39:06 +03:00
current_euid_egid ( & euid , & egid ) ;
new - > cuid = new - > uid = euid ;
new - > gid = new - > cgid = egid ;
2005-04-17 02:20:36 +04:00
new - > seq = ids - > seq + + ;
if ( ids - > seq > ids - > seq_max )
ids - > seq = 0 ;
2008-04-29 12:00:35 +04:00
new - > id = ipc_buildid ( id , new - > seq ) ;
2005-04-17 02:20:36 +04:00
return id ;
}
2007-10-19 10:40:49 +04:00
/**
* ipcget_new - create a new ipc object
* @ ns : namespace
2007-10-19 10:40:53 +04:00
* @ ids : IPC identifer set
2007-10-19 10:40:49 +04:00
* @ ops : the actual creation routine to call
* @ params : its parameters
*
2007-10-19 10:40:53 +04:00
* This routine is called by sys_msgget , sys_semget ( ) and sys_shmget ( )
* when the key is IPC_PRIVATE .
2007-10-19 10:40:49 +04:00
*/
2008-02-08 15:18:54 +03:00
static int ipcget_new ( struct ipc_namespace * ns , struct ipc_ids * ids ,
2007-10-19 10:40:49 +04:00
struct ipc_ops * ops , struct ipc_params * params )
{
int err ;
2007-10-19 10:40:57 +04:00
retry :
2007-10-19 10:40:49 +04:00
err = idr_pre_get ( & ids - > ipcs_idr , GFP_KERNEL ) ;
if ( ! err )
return - ENOMEM ;
2007-10-19 10:40:54 +04:00
down_write ( & ids - > rw_mutex ) ;
2007-10-19 10:40:49 +04:00
err = ops - > getnew ( ns , params ) ;
2007-10-19 10:40:54 +04:00
up_write ( & ids - > rw_mutex ) ;
2007-10-19 10:40:49 +04:00
2007-10-19 10:40:57 +04:00
if ( err = = - EAGAIN )
goto retry ;
2007-10-19 10:40:49 +04:00
return err ;
}
/**
* ipc_check_perms - check security and permissions for an IPC
2011-03-26 23:27:41 +03:00
* @ ns : IPC namespace
2007-10-19 10:40:49 +04:00
* @ ipcp : ipc permission set
* @ ops : the actual security routine to call
* @ params : its parameters
2007-10-19 10:40:53 +04:00
*
* This routine is called by sys_msgget ( ) , sys_semget ( ) and sys_shmget ( )
* when the key is not IPC_PRIVATE and that key already exists in the
* ids IDR .
*
* On success , the IPC id is returned .
*
2007-10-19 10:40:54 +04:00
* It is called with ipc_ids . rw_mutex and ipcp - > lock held .
2007-10-19 10:40:49 +04:00
*/
2011-03-24 02:43:24 +03:00
static int ipc_check_perms ( struct ipc_namespace * ns ,
struct kern_ipc_perm * ipcp ,
struct ipc_ops * ops ,
struct ipc_params * params )
2007-10-19 10:40:49 +04:00
{
int err ;
2011-03-24 02:43:24 +03:00
if ( ipcperms ( ns , ipcp , params - > flg ) )
2007-10-19 10:40:49 +04:00
err = - EACCES ;
else {
err = ops - > associate ( ipcp , params - > flg ) ;
if ( ! err )
err = ipcp - > id ;
}
return err ;
}
/**
* ipcget_public - get an ipc object or create a new one
* @ ns : namespace
2007-10-19 10:40:53 +04:00
* @ ids : IPC identifer set
2007-10-19 10:40:49 +04:00
* @ ops : the actual creation routine to call
* @ params : its parameters
*
2007-10-19 10:40:53 +04:00
* This routine is called by sys_msgget , sys_semget ( ) and sys_shmget ( )
* when the key is not IPC_PRIVATE .
* It adds a new entry if the key is not found and does some permission
* / security checkings if the key is found .
*
* On success , the ipc id is returned .
2007-10-19 10:40:49 +04:00
*/
2008-02-08 15:18:54 +03:00
static int ipcget_public ( struct ipc_namespace * ns , struct ipc_ids * ids ,
2007-10-19 10:40:49 +04:00
struct ipc_ops * ops , struct ipc_params * params )
{
struct kern_ipc_perm * ipcp ;
int flg = params - > flg ;
int err ;
2007-10-19 10:40:57 +04:00
retry :
2007-10-19 10:40:49 +04:00
err = idr_pre_get ( & ids - > ipcs_idr , GFP_KERNEL ) ;
2007-10-19 10:40:54 +04:00
/*
* Take the lock as a writer since we are potentially going to add
* a new entry + read locks are not " upgradable "
*/
down_write ( & ids - > rw_mutex ) ;
2007-10-19 10:40:49 +04:00
ipcp = ipc_findkey ( ids , params - > key ) ;
if ( ipcp = = NULL ) {
/* key not used */
if ( ! ( flg & IPC_CREAT ) )
err = - ENOENT ;
else if ( ! err )
err = - ENOMEM ;
else
err = ops - > getnew ( ns , params ) ;
} else {
/* ipc object has been locked by ipc_findkey() */
if ( flg & IPC_CREAT & & flg & IPC_EXCL )
err = - EEXIST ;
else {
err = 0 ;
if ( ops - > more_checks )
err = ops - > more_checks ( ipcp , params ) ;
if ( ! err )
2007-10-19 10:40:53 +04:00
/*
* ipc_check_perms returns the IPC id on
* success
*/
2011-03-24 02:43:24 +03:00
err = ipc_check_perms ( ns , ipcp , ops , params ) ;
2007-10-19 10:40:49 +04:00
}
ipc_unlock ( ipcp ) ;
}
2007-10-19 10:40:54 +04:00
up_write ( & ids - > rw_mutex ) ;
2007-10-19 10:40:49 +04:00
2007-10-19 10:40:57 +04:00
if ( err = = - EAGAIN )
goto retry ;
2007-10-19 10:40:49 +04:00
return err ;
}
2005-04-17 02:20:36 +04:00
/**
* ipc_rmid - remove an IPC identifier
2007-10-19 10:40:53 +04:00
* @ ids : IPC identifier set
* @ ipcp : ipc perm structure containing the identifier to remove
2005-04-17 02:20:36 +04:00
*
2007-10-19 10:40:54 +04:00
* ipc_ids . rw_mutex ( as a writer ) and the spinlock for this ID are held
* before this function is called , and remain locked on the exit .
2005-04-17 02:20:36 +04:00
*/
2007-10-19 10:40:48 +04:00
void ipc_rmid ( struct ipc_ids * ids , struct kern_ipc_perm * ipcp )
2005-04-17 02:20:36 +04:00
{
2007-10-19 10:40:52 +04:00
int lid = ipcid_to_idx ( ipcp - > id ) ;
2007-10-19 10:40:48 +04:00
idr_remove ( & ids - > ipcs_idr , lid ) ;
2005-04-17 02:20:36 +04:00
ids - > in_use - - ;
2007-10-19 10:40:48 +04:00
ipcp - > deleted = 1 ;
return ;
2005-04-17 02:20:36 +04:00
}
/**
* ipc_alloc - allocate ipc space
* @ size : size desired
*
* Allocate memory from the appropriate pools and return a pointer to it .
* NULL is returned if the allocation fails
*/
void * ipc_alloc ( int size )
{
void * out ;
if ( size > PAGE_SIZE )
out = vmalloc ( size ) ;
else
out = kmalloc ( size , GFP_KERNEL ) ;
return out ;
}
/**
* ipc_free - free ipc space
* @ ptr : pointer returned by ipc_alloc
* @ size : size of block
*
2007-02-10 12:45:59 +03:00
* Free a block created with ipc_alloc ( ) . The caller must know the size
2005-04-17 02:20:36 +04:00
* used in the allocation call .
*/
void ipc_free ( void * ptr , int size )
{
if ( size > PAGE_SIZE )
vfree ( ptr ) ;
else
kfree ( ptr ) ;
}
/*
* rcu allocations :
* There are three headers that are prepended to the actual allocation :
* - during use : ipc_rcu_hdr .
* - during the rcu grace period : ipc_rcu_grace .
* - [ only if vmalloc ] : ipc_rcu_sched .
* Their lifetime doesn ' t overlap , thus the headers share the same memory .
* Unlike a normal union , they are right - aligned , thus some container_of
* forward / backward casting is necessary :
*/
struct ipc_rcu_hdr
{
int refcount ;
int is_vmalloc ;
void * data [ 0 ] ;
} ;
struct ipc_rcu_grace
{
struct rcu_head rcu ;
/* "void *" makes sure alignment of following data is sane. */
void * data [ 0 ] ;
} ;
struct ipc_rcu_sched
{
struct work_struct work ;
/* "void *" makes sure alignment of following data is sane. */
void * data [ 0 ] ;
} ;
# define HDRLEN_KMALLOC (sizeof(struct ipc_rcu_grace) > sizeof(struct ipc_rcu_hdr) ? \
sizeof ( struct ipc_rcu_grace ) : sizeof ( struct ipc_rcu_hdr ) )
# define HDRLEN_VMALLOC (sizeof(struct ipc_rcu_sched) > HDRLEN_KMALLOC ? \
sizeof ( struct ipc_rcu_sched ) : HDRLEN_KMALLOC )
static inline int rcu_use_vmalloc ( int size )
{
/* Too big for a single page? */
if ( HDRLEN_KMALLOC + size > PAGE_SIZE )
return 1 ;
return 0 ;
}
/**
* ipc_rcu_alloc - allocate ipc and rcu space
* @ size : size desired
*
* Allocate memory for the rcu header structure + the object .
* Returns the pointer to the object .
* NULL is returned if the allocation fails .
*/
void * ipc_rcu_alloc ( int size )
{
void * out ;
/*
* We prepend the allocation with the rcu struct , and
* workqueue if necessary ( for vmalloc ) .
*/
if ( rcu_use_vmalloc ( size ) ) {
out = vmalloc ( HDRLEN_VMALLOC + size ) ;
if ( out ) {
out + = HDRLEN_VMALLOC ;
container_of ( out , struct ipc_rcu_hdr , data ) - > is_vmalloc = 1 ;
container_of ( out , struct ipc_rcu_hdr , data ) - > refcount = 1 ;
}
} else {
out = kmalloc ( HDRLEN_KMALLOC + size , GFP_KERNEL ) ;
if ( out ) {
out + = HDRLEN_KMALLOC ;
container_of ( out , struct ipc_rcu_hdr , data ) - > is_vmalloc = 0 ;
container_of ( out , struct ipc_rcu_hdr , data ) - > refcount = 1 ;
}
}
return out ;
}
void ipc_rcu_getref ( void * ptr )
{
container_of ( ptr , struct ipc_rcu_hdr , data ) - > refcount + + ;
}
2006-11-22 17:55:48 +03:00
static void ipc_do_vfree ( struct work_struct * work )
{
vfree ( container_of ( work , struct ipc_rcu_sched , work ) ) ;
}
2005-04-17 02:20:36 +04:00
/**
2005-11-07 12:01:06 +03:00
* ipc_schedule_free - free ipc + rcu space
* @ head : RCU callback structure for queued work
2005-04-17 02:20:36 +04:00
*
* Since RCU callback function is called in bh ,
2007-02-10 12:45:59 +03:00
* we need to defer the vfree to schedule_work ( ) .
2005-04-17 02:20:36 +04:00
*/
static void ipc_schedule_free ( struct rcu_head * head )
{
2007-10-19 10:40:53 +04:00
struct ipc_rcu_grace * grace ;
struct ipc_rcu_sched * sched ;
grace = container_of ( head , struct ipc_rcu_grace , rcu ) ;
sched = container_of ( & ( grace - > data [ 0 ] ) , struct ipc_rcu_sched ,
data [ 0 ] ) ;
2005-04-17 02:20:36 +04:00
2006-11-22 17:55:48 +03:00
INIT_WORK ( & sched - > work , ipc_do_vfree ) ;
2005-04-17 02:20:36 +04:00
schedule_work ( & sched - > work ) ;
}
void ipc_rcu_putref ( void * ptr )
{
if ( - - container_of ( ptr , struct ipc_rcu_hdr , data ) - > refcount > 0 )
return ;
if ( container_of ( ptr , struct ipc_rcu_hdr , data ) - > is_vmalloc ) {
call_rcu ( & container_of ( ptr , struct ipc_rcu_grace , data ) - > rcu ,
ipc_schedule_free ) ;
} else {
2011-03-18 07:11:44 +03:00
kfree_rcu ( container_of ( ptr , struct ipc_rcu_grace , data ) , rcu ) ;
2005-04-17 02:20:36 +04:00
}
}
/**
* ipcperms - check IPC permissions
2011-03-26 23:27:41 +03:00
* @ ns : IPC namespace
2005-04-17 02:20:36 +04:00
* @ ipcp : IPC permission set
* @ flag : desired permission set .
*
* Check user , group , other permissions for access
* to ipc resources . return 0 if allowed
2011-03-24 02:43:24 +03:00
*
* @ flag will most probably be 0 or S_ . . . UGO from < linux / stat . h >
2005-04-17 02:20:36 +04:00
*/
2011-03-24 02:43:24 +03:00
int ipcperms ( struct ipc_namespace * ns , struct kern_ipc_perm * ipcp , short flag )
{
2008-11-14 02:39:06 +03:00
uid_t euid = current_euid ( ) ;
2008-12-10 11:40:06 +03:00
int requested_mode , granted_mode ;
2005-04-17 02:20:36 +04:00
2008-12-10 11:40:06 +03:00
audit_ipc_obj ( ipcp ) ;
2005-04-17 02:20:36 +04:00
requested_mode = ( flag > > 6 ) | ( flag > > 3 ) | flag ;
granted_mode = ipcp - > mode ;
2008-11-14 02:39:06 +03:00
if ( euid = = ipcp - > cuid | |
euid = = ipcp - > uid )
2005-04-17 02:20:36 +04:00
granted_mode > > = 6 ;
else if ( in_group_p ( ipcp - > cgid ) | | in_group_p ( ipcp - > gid ) )
granted_mode > > = 3 ;
/* is there some bit set in requested_mode but not in granted_mode? */
if ( ( requested_mode & ~ granted_mode & 0007 ) & &
2011-03-24 02:43:24 +03:00
! ns_capable ( ns - > user_ns , CAP_IPC_OWNER ) )
2005-04-17 02:20:36 +04:00
return - 1 ;
return security_ipc_permission ( ipcp , flag ) ;
}
/*
* Functions to convert between the kern_ipc_perm structure and the
* old / new ipc_perm structures
*/
/**
* kernel_to_ipc64_perm - convert kernel ipc permissions to user
* @ in : kernel permissions
* @ out : new style IPC permissions
*
2007-02-10 12:45:59 +03:00
* Turn the kernel object @ in into a set of permissions descriptions
* for returning to userspace ( @ out ) .
2005-04-17 02:20:36 +04:00
*/
void kernel_to_ipc64_perm ( struct kern_ipc_perm * in , struct ipc64_perm * out )
{
out - > key = in - > key ;
out - > uid = in - > uid ;
out - > gid = in - > gid ;
out - > cuid = in - > cuid ;
out - > cgid = in - > cgid ;
out - > mode = in - > mode ;
out - > seq = in - > seq ;
}
/**
2007-10-19 10:40:53 +04:00
* ipc64_perm_to_ipc_perm - convert new ipc permissions to old
2005-04-17 02:20:36 +04:00
* @ in : new style IPC permissions
* @ out : old style IPC permissions
*
2007-02-10 12:45:59 +03:00
* Turn the new style permissions object @ in into a compatibility
* object and store it into the @ out pointer .
2005-04-17 02:20:36 +04:00
*/
void ipc64_perm_to_ipc_perm ( struct ipc64_perm * in , struct ipc_perm * out )
{
out - > key = in - > key ;
SET_UID ( out - > uid , in - > uid ) ;
SET_GID ( out - > gid , in - > gid ) ;
SET_UID ( out - > cuid , in - > cuid ) ;
SET_GID ( out - > cgid , in - > cgid ) ;
out - > mode = in - > mode ;
out - > seq = in - > seq ;
}
2007-10-19 10:40:53 +04:00
/**
2007-10-19 10:40:54 +04:00
* ipc_lock - Lock an ipc structure without rw_mutex held
2007-10-19 10:40:53 +04:00
* @ ids : IPC identifier set
* @ id : ipc id to look for
*
* Look for an id in the ipc ids idr and lock the associated ipc object .
*
* The ipc object is locked on exit .
*/
2007-10-19 10:40:48 +04:00
struct kern_ipc_perm * ipc_lock ( struct ipc_ids * ids , int id )
2005-04-17 02:20:36 +04:00
{
2007-10-19 10:40:48 +04:00
struct kern_ipc_perm * out ;
2007-10-19 10:40:52 +04:00
int lid = ipcid_to_idx ( id ) ;
2005-04-17 02:20:36 +04:00
rcu_read_lock ( ) ;
2007-10-19 10:40:48 +04:00
out = idr_find ( & ids - > ipcs_idr , lid ) ;
if ( out = = NULL ) {
2005-04-17 02:20:36 +04:00
rcu_read_unlock ( ) ;
2007-10-19 10:40:51 +04:00
return ERR_PTR ( - EINVAL ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-19 10:40:48 +04:00
2005-04-17 02:20:36 +04:00
spin_lock ( & out - > lock ) ;
/* ipc_rmid() may have already freed the ID while ipc_lock
* was spinning : here verify that the structure is still valid
*/
if ( out - > deleted ) {
spin_unlock ( & out - > lock ) ;
rcu_read_unlock ( ) ;
2007-10-19 10:40:51 +04:00
return ERR_PTR ( - EINVAL ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-19 10:40:48 +04:00
2005-04-17 02:20:36 +04:00
return out ;
}
2008-02-08 15:18:54 +03:00
struct kern_ipc_perm * ipc_lock_check ( struct ipc_ids * ids , int id )
{
struct kern_ipc_perm * out ;
out = ipc_lock ( ids , id ) ;
if ( IS_ERR ( out ) )
return out ;
if ( ipc_checkid ( out , id ) ) {
ipc_unlock ( out ) ;
return ERR_PTR ( - EIDRM ) ;
}
return out ;
}
/**
* ipcget - Common sys_ * get ( ) code
* @ ns : namsepace
* @ ids : IPC identifier set
* @ ops : operations to be called on ipc object creation , permission checks
* and further checks
* @ params : the parameters needed by the previous operations .
*
* Common routine called by sys_msgget ( ) , sys_semget ( ) and sys_shmget ( ) .
*/
int ipcget ( struct ipc_namespace * ns , struct ipc_ids * ids ,
struct ipc_ops * ops , struct ipc_params * params )
{
if ( params - > key = = IPC_PRIVATE )
return ipcget_new ( ns , ids , ops , params ) ;
else
return ipcget_public ( ns , ids , ops , params ) ;
}
2008-04-29 12:00:51 +04:00
/**
* ipc_update_perm - update the permissions of an IPC .
* @ in : the permission given as input .
* @ out : the permission of the ipc to set .
*/
void ipc_update_perm ( struct ipc64_perm * in , struct kern_ipc_perm * out )
{
out - > uid = in - > uid ;
out - > gid = in - > gid ;
out - > mode = ( out - > mode & ~ S_IRWXUGO )
| ( in - > mode & S_IRWXUGO ) ;
}
2008-04-29 12:00:54 +04:00
/**
* ipcctl_pre_down - retrieve an ipc and check permissions for some IPC_XXX cmd
2011-03-26 23:27:41 +03:00
* @ ns : the ipc namespace
2008-04-29 12:00:54 +04:00
* @ ids : the table of ids where to look for the ipc
* @ id : the id of the ipc to retrieve
* @ cmd : the cmd to check
* @ perm : the permission to set
* @ extra_perm : one extra permission parameter used by msq
*
* This function does some common audit and permissions check for some IPC_XXX
* cmd and is called from semctl_down , shmctl_down and msgctl_down .
* It must be called without any lock held and
* - retrieves the ipc with the given id in the given table .
* - performs some audit and permission check , depending on the given cmd
* - returns the ipc with both ipc and rw_mutex locks held in case of success
* or an err - code without any lock held otherwise .
*/
2011-03-24 02:43:24 +03:00
struct kern_ipc_perm * ipcctl_pre_down ( struct ipc_namespace * ns ,
struct ipc_ids * ids , int id , int cmd ,
2008-04-29 12:00:54 +04:00
struct ipc64_perm * perm , int extra_perm )
{
struct kern_ipc_perm * ipcp ;
2008-11-14 02:39:06 +03:00
uid_t euid ;
2008-04-29 12:00:54 +04:00
int err ;
down_write ( & ids - > rw_mutex ) ;
2008-07-25 12:48:03 +04:00
ipcp = ipc_lock_check ( ids , id ) ;
2008-04-29 12:00:54 +04:00
if ( IS_ERR ( ipcp ) ) {
err = PTR_ERR ( ipcp ) ;
goto out_up ;
}
2008-12-10 11:40:06 +03:00
audit_ipc_obj ( ipcp ) ;
2008-12-10 11:47:15 +03:00
if ( cmd = = IPC_SET )
audit_ipc_set_perm ( extra_perm , perm - > uid ,
2008-04-29 12:00:54 +04:00
perm - > gid , perm - > mode ) ;
2008-11-14 02:39:06 +03:00
euid = current_euid ( ) ;
2011-03-24 02:43:24 +03:00
if ( euid = = ipcp - > cuid | | euid = = ipcp - > uid | |
ns_capable ( ns - > user_ns , CAP_SYS_ADMIN ) )
2008-04-29 12:00:54 +04:00
return ipcp ;
err = - EPERM ;
ipc_unlock ( ipcp ) ;
out_up :
up_write ( & ids - > rw_mutex ) ;
return ERR_PTR ( err ) ;
}
2005-04-17 02:20:36 +04:00
# ifdef __ARCH_WANT_IPC_PARSE_VERSION
/**
* ipc_parse_version - IPC call version
* @ cmd : pointer to command
*
* Return IPC_64 for new style IPC and IPC_OLD for old style IPC .
2007-02-10 12:45:59 +03:00
* The @ cmd value is turned from an encoding command and version into
2005-04-17 02:20:36 +04:00
* just the command code .
*/
int ipc_parse_version ( int * cmd )
{
if ( * cmd & IPC_64 ) {
* cmd ^ = IPC_64 ;
return IPC_64 ;
} else {
return IPC_OLD ;
}
}
# endif /* __ARCH_WANT_IPC_PARSE_VERSION */
2005-09-07 02:17:09 +04:00
# ifdef CONFIG_PROC_FS
2007-02-12 11:52:10 +03:00
struct ipc_proc_iter {
struct ipc_namespace * ns ;
struct ipc_proc_iface * iface ;
} ;
2007-10-19 10:40:48 +04:00
/*
* This routine locks the ipc structure found at least at position pos .
*/
2008-02-06 12:36:28 +03:00
static struct kern_ipc_perm * sysvipc_find_ipc ( struct ipc_ids * ids , loff_t pos ,
loff_t * new_pos )
2005-09-07 02:17:09 +04:00
{
2007-10-19 10:40:48 +04:00
struct kern_ipc_perm * ipc ;
int total , id ;
2006-10-02 13:18:20 +04:00
2007-10-19 10:40:48 +04:00
total = 0 ;
for ( id = 0 ; id < pos & & total < ids - > in_use ; id + + ) {
ipc = idr_find ( & ids - > ipcs_idr , id ) ;
if ( ipc ! = NULL )
total + + ;
}
2005-09-07 02:17:09 +04:00
2007-10-19 10:40:48 +04:00
if ( total > = ids - > in_use )
return NULL ;
2005-09-07 02:17:09 +04:00
2007-10-19 10:40:48 +04:00
for ( ; pos < IPCMNI ; pos + + ) {
ipc = idr_find ( & ids - > ipcs_idr , pos ) ;
if ( ipc ! = NULL ) {
* new_pos = pos + 1 ;
ipc_lock_by_ptr ( ipc ) ;
2005-09-07 02:17:09 +04:00
return ipc ;
}
}
/* Out of range - return NULL to terminate iteration */
return NULL ;
}
2007-10-19 10:40:48 +04:00
static void * sysvipc_proc_next ( struct seq_file * s , void * it , loff_t * pos )
{
struct ipc_proc_iter * iter = s - > private ;
struct ipc_proc_iface * iface = iter - > iface ;
struct kern_ipc_perm * ipc = it ;
/* If we had an ipc id locked before, unlock it */
if ( ipc & & ipc ! = SEQ_START_TOKEN )
ipc_unlock ( ipc ) ;
2008-02-08 15:18:57 +03:00
return sysvipc_find_ipc ( & iter - > ns - > ids [ iface - > ids ] , * pos , pos ) ;
2007-10-19 10:40:48 +04:00
}
2005-09-07 02:17:09 +04:00
/*
2007-10-19 10:40:53 +04:00
* File positions : pos 0 - > header , pos n - > ipc id = n - 1.
* SeqFile iterator : iterator value locked ipc pointer or SEQ_TOKEN_START .
2005-09-07 02:17:09 +04:00
*/
static void * sysvipc_proc_start ( struct seq_file * s , loff_t * pos )
{
2007-02-12 11:52:10 +03:00
struct ipc_proc_iter * iter = s - > private ;
struct ipc_proc_iface * iface = iter - > iface ;
2006-10-02 13:18:20 +04:00
struct ipc_ids * ids ;
2008-02-08 15:18:57 +03:00
ids = & iter - > ns - > ids [ iface - > ids ] ;
2005-09-07 02:17:09 +04:00
/*
* Take the lock - this will be released by the corresponding
* call to stop ( ) .
*/
2007-10-19 10:40:54 +04:00
down_read ( & ids - > rw_mutex ) ;
2005-09-07 02:17:09 +04:00
/* pos < 0 is invalid */
if ( * pos < 0 )
return NULL ;
/* pos == 0 means header */
if ( * pos = = 0 )
return SEQ_START_TOKEN ;
/* Find the (pos-1)th ipc */
2007-10-19 10:40:48 +04:00
return sysvipc_find_ipc ( ids , * pos - 1 , pos ) ;
2005-09-07 02:17:09 +04:00
}
static void sysvipc_proc_stop ( struct seq_file * s , void * it )
{
struct kern_ipc_perm * ipc = it ;
2007-02-12 11:52:10 +03:00
struct ipc_proc_iter * iter = s - > private ;
struct ipc_proc_iface * iface = iter - > iface ;
2006-10-02 13:18:20 +04:00
struct ipc_ids * ids ;
2005-09-07 02:17:09 +04:00
2007-10-19 10:40:53 +04:00
/* If we had a locked structure, release it */
2005-09-07 02:17:09 +04:00
if ( ipc & & ipc ! = SEQ_START_TOKEN )
ipc_unlock ( ipc ) ;
2008-02-08 15:18:57 +03:00
ids = & iter - > ns - > ids [ iface - > ids ] ;
2005-09-07 02:17:09 +04:00
/* Release the lock we took in start() */
2007-10-19 10:40:54 +04:00
up_read ( & ids - > rw_mutex ) ;
2005-09-07 02:17:09 +04:00
}
static int sysvipc_proc_show ( struct seq_file * s , void * it )
{
2007-02-12 11:52:10 +03:00
struct ipc_proc_iter * iter = s - > private ;
struct ipc_proc_iface * iface = iter - > iface ;
2005-09-07 02:17:09 +04:00
if ( it = = SEQ_START_TOKEN )
return seq_puts ( s , iface - > header ) ;
return iface - > show ( s , it ) ;
}
2009-09-23 03:43:43 +04:00
static const struct seq_operations sysvipc_proc_seqops = {
2005-09-07 02:17:09 +04:00
. start = sysvipc_proc_start ,
. stop = sysvipc_proc_stop ,
. next = sysvipc_proc_next ,
. show = sysvipc_proc_show ,
} ;
2007-02-12 11:52:10 +03:00
static int sysvipc_proc_open ( struct inode * inode , struct file * file )
{
2005-09-07 02:17:09 +04:00
int ret ;
struct seq_file * seq ;
2007-02-12 11:52:10 +03:00
struct ipc_proc_iter * iter ;
ret = - ENOMEM ;
iter = kmalloc ( sizeof ( * iter ) , GFP_KERNEL ) ;
if ( ! iter )
goto out ;
2005-09-07 02:17:09 +04:00
ret = seq_open ( file , & sysvipc_proc_seqops ) ;
2007-02-12 11:52:10 +03:00
if ( ret )
goto out_kfree ;
seq = file - > private_data ;
seq - > private = iter ;
iter - > iface = PDE ( inode ) - > data ;
iter - > ns = get_ipc_ns ( current - > nsproxy - > ipc_ns ) ;
out :
2005-09-07 02:17:09 +04:00
return ret ;
2007-02-12 11:52:10 +03:00
out_kfree :
kfree ( iter ) ;
goto out ;
}
static int sysvipc_proc_release ( struct inode * inode , struct file * file )
{
struct seq_file * seq = file - > private_data ;
struct ipc_proc_iter * iter = seq - > private ;
put_ipc_ns ( iter - > ns ) ;
return seq_release_private ( inode , file ) ;
2005-09-07 02:17:09 +04:00
}
2007-02-12 11:55:35 +03:00
static const struct file_operations sysvipc_proc_fops = {
2005-09-07 02:17:09 +04:00
. open = sysvipc_proc_open ,
. read = seq_read ,
. llseek = seq_lseek ,
2007-02-12 11:52:10 +03:00
. release = sysvipc_proc_release ,
2005-09-07 02:17:09 +04:00
} ;
# endif /* CONFIG_PROC_FS */