diff --git a/ipc/msg.c b/ipc/msg.c index 08591a0278ce23fe67af16328f6f632480ef23b3..c2ee26f01055c0314a4386629946bb5ce3511055 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -81,7 +81,7 @@ static struct ipc_ids init_msg_ids; ipc_buildid(&msg_ids(ns), id, seq) static void freeque(struct ipc_namespace *, struct msg_queue *); -static int newque (struct ipc_namespace *ns, key_t key, int msgflg); +static int newque(struct ipc_namespace *, struct ipc_params *); #ifdef CONFIG_PROC_FS static int sysvipc_msg_proc_show(struct seq_file *s, void *it); #endif @@ -144,10 +144,12 @@ static inline void msg_rmid(struct ipc_namespace *ns, struct msg_queue *s) ipc_rmid(&msg_ids(ns), &s->q_perm); } -static int newque (struct ipc_namespace *ns, key_t key, int msgflg) +static int newque(struct ipc_namespace *ns, struct ipc_params *params) { struct msg_queue *msq; int id, retval; + key_t key = params->key; + int msgflg = params->flg; msq = ipc_rcu_alloc(sizeof(*msq)); if (!msq) @@ -264,56 +266,27 @@ static void freeque(struct ipc_namespace *ns, struct msg_queue *msq) ipc_rcu_putref(msq); } +static inline int msg_security(void *msq, int msgflg) +{ + return security_msg_queue_associate((struct msg_queue *) msq, msgflg); +} + asmlinkage long sys_msgget(key_t key, int msgflg) { - struct msg_queue *msq; - int ret; struct ipc_namespace *ns; + struct ipc_ops msg_ops; + struct ipc_params msg_params; ns = current->nsproxy->ipc_ns; - ret = idr_pre_get(&msg_ids(ns).ipcs_idr, GFP_KERNEL); + msg_ops.getnew = newque; + msg_ops.associate = msg_security; + msg_ops.more_checks = NULL; - if (key == IPC_PRIVATE) { - if (!ret) - ret = -ENOMEM; - else { - mutex_lock(&msg_ids(ns).mutex); - ret = newque(ns, key, msgflg); - mutex_unlock(&msg_ids(ns).mutex); - } - } else { - mutex_lock(&msg_ids(ns).mutex); - msq = (struct msg_queue *) ipc_findkey(&msg_ids(ns), key); - if (msq == NULL) { - /* key not used */ - if (!(msgflg & IPC_CREAT)) - ret = -ENOENT; - else if (!ret) - ret = -ENOMEM; - else - ret = newque(ns, key, msgflg); - } else { - /* msq has been locked by ipc_findkey() */ - - if (msgflg & IPC_CREAT && msgflg & IPC_EXCL) - ret = -EEXIST; - else { - if (ipcperms(&msq->q_perm, msgflg)) - ret = -EACCES; - else { - ret = security_msg_queue_associate( - msq, msgflg); - if (!ret) - ret = msq->q_perm.id; - } - } - msg_unlock(msq); - } - mutex_unlock(&msg_ids(ns).mutex); - } + msg_params.key = key; + msg_params.flg = msgflg; - return ret; + return ipcget(ns, &msg_ids(ns), &msg_ops, &msg_params); } static inline unsigned long diff --git a/ipc/sem.c b/ipc/sem.c index f92a2565d12b0c7fc896f7c73a581e734d6eb225..6af226af0b90ea538aa4586471111d70cece3a4e 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -97,7 +97,7 @@ static struct ipc_ids init_sem_ids; -static int newary(struct ipc_namespace *, key_t, int, int); +static int newary(struct ipc_namespace *, struct ipc_params *); static void freeary(struct ipc_namespace *, struct sem_array *); #ifdef CONFIG_PROC_FS static int sysvipc_sem_proc_show(struct seq_file *s, void *it); @@ -214,12 +214,15 @@ static inline void sem_rmid(struct ipc_namespace *ns, struct sem_array *s) */ #define IN_WAKEUP 1 -static int newary (struct ipc_namespace *ns, key_t key, int nsems, int semflg) +static int newary(struct ipc_namespace *ns, struct ipc_params *params) { int id; int retval; struct sem_array *sma; int size; + key_t key = params->key; + int nsems = params->u.nsems; + int semflg = params->flg; if (!nsems) return -EINVAL; @@ -263,61 +266,40 @@ static int newary (struct ipc_namespace *ns, key_t key, int nsems, int semflg) return sma->sem_perm.id; } -asmlinkage long sys_semget (key_t key, int nsems, int semflg) + +static inline int sem_security(void *sma, int semflg) +{ + return security_sem_associate((struct sem_array *) sma, semflg); +} + +static inline int sem_more_checks(void *sma, struct ipc_params *params) +{ + if (params->u.nsems > ((struct sem_array *)sma)->sem_nsems) + return -EINVAL; + + return 0; +} + +asmlinkage long sys_semget(key_t key, int nsems, int semflg) { - int err; - struct sem_array *sma; struct ipc_namespace *ns; + struct ipc_ops sem_ops; + struct ipc_params sem_params; ns = current->nsproxy->ipc_ns; if (nsems < 0 || nsems > ns->sc_semmsl) return -EINVAL; - err = idr_pre_get(&sem_ids(ns).ipcs_idr, GFP_KERNEL); + sem_ops.getnew = newary; + sem_ops.associate = sem_security; + sem_ops.more_checks = sem_more_checks; - if (key == IPC_PRIVATE) { - if (!err) - err = -ENOMEM; - else { - mutex_lock(&sem_ids(ns).mutex); - err = newary(ns, key, nsems, semflg); - mutex_unlock(&sem_ids(ns).mutex); - } - } else { - mutex_lock(&sem_ids(ns).mutex); - sma = (struct sem_array *) ipc_findkey(&sem_ids(ns), key); - if (sma == NULL) { - /* key not used */ - if (!(semflg & IPC_CREAT)) - err = -ENOENT; - else if (!err) - err = -ENOMEM; - else - err = newary(ns, key, nsems, semflg); - } else { - /* sma has been locked by ipc_findkey() */ - - if (semflg & IPC_CREAT && semflg & IPC_EXCL) - err = -EEXIST; - else { - if (nsems > sma->sem_nsems) - err = -EINVAL; - else if (ipcperms(&sma->sem_perm, semflg)) - err = -EACCES; - else { - err = security_sem_associate(sma, - semflg); - if (!err) - err = sma->sem_perm.id; - } - } - sem_unlock(sma); - } - mutex_unlock(&sem_ids(ns).mutex); - } + sem_params.key = key; + sem_params.flg = semflg; + sem_params.u.nsems = nsems; - return err; + return ipcget(ns, &sem_ids(ns), &sem_ops, &sem_params); } /* Manage the doubly linked list sma->sem_pending as a FIFO: diff --git a/ipc/shm.c b/ipc/shm.c index dcc333239683d7d15ea427a19b236a6fd769d69d..d20cc25c5bdfb0cc7143584150f394f1f4b49901 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -68,8 +68,7 @@ static struct ipc_ids init_shm_ids; #define shm_buildid(ns, id, seq) \ ipc_buildid(&shm_ids(ns), id, seq) -static int newseg (struct ipc_namespace *ns, key_t key, - int shmflg, size_t size); +static int newseg(struct ipc_namespace *, struct ipc_params *); static void shm_open(struct vm_area_struct *vma); static void shm_close(struct vm_area_struct *vma); static void shm_destroy (struct ipc_namespace *ns, struct shmid_kernel *shp); @@ -341,8 +340,11 @@ static struct vm_operations_struct shm_vm_ops = { #endif }; -static int newseg (struct ipc_namespace *ns, key_t key, int shmflg, size_t size) +static int newseg(struct ipc_namespace *ns, struct ipc_params *params) { + key_t key = params->key; + int shmflg = params->flg; + size_t size = params->u.size; int error; struct shmid_kernel *shp; int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT; @@ -423,57 +425,36 @@ static int newseg (struct ipc_namespace *ns, key_t key, int shmflg, size_t size) return error; } +static inline int shm_security(void *shp, int shmflg) +{ + return security_shm_associate((struct shmid_kernel *) shp, shmflg); +} + +static inline int shm_more_checks(void *shp, struct ipc_params *params) +{ + if (((struct shmid_kernel *)shp)->shm_segsz < params->u.size) + return -EINVAL; + + return 0; +} + asmlinkage long sys_shmget (key_t key, size_t size, int shmflg) { - struct shmid_kernel *shp; - int err; struct ipc_namespace *ns; + struct ipc_ops shm_ops; + struct ipc_params shm_params; ns = current->nsproxy->ipc_ns; - err = idr_pre_get(&shm_ids(ns).ipcs_idr, GFP_KERNEL); + shm_ops.getnew = newseg; + shm_ops.associate = shm_security; + shm_ops.more_checks = shm_more_checks; - if (key == IPC_PRIVATE) { - if (!err) - err = -ENOMEM; - else { - mutex_lock(&shm_ids(ns).mutex); - err = newseg(ns, key, shmflg, size); - mutex_unlock(&shm_ids(ns).mutex); - } - } else { - mutex_lock(&shm_ids(ns).mutex); - shp = (struct shmid_kernel *) ipc_findkey(&shm_ids(ns), key); - if (shp == NULL) { - if (!(shmflg & IPC_CREAT)) - err = -ENOENT; - else if (!err) - err = -ENOMEM; - else - err = newseg(ns, key, shmflg, size); - } else { - /* shp has been locked by ipc_findkey() */ - - if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) - err = -EEXIST; - else { - if (shp->shm_segsz < size) - err = -EINVAL; - else if (ipcperms(&shp->shm_perm, shmflg)) - err = -EACCES; - else { - err = security_shm_associate(shp, - shmflg); - if (!err) - err = shp->shm_perm.id; - } - } - shm_unlock(shp); - } - mutex_unlock(&shm_ids(ns).mutex); - } + shm_params.key = key; + shm_params.flg = shmflg; + shm_params.u.size = size; - return err; + return ipcget(ns, &shm_ids(ns), &shm_ops, &shm_params); } static inline unsigned long copy_shmid_to_user(void __user *buf, struct shmid64_ds *in, int version) diff --git a/ipc/util.c b/ipc/util.c index 2a205875d277bcce202306124a332cbb1fa07c36..03b88798f71f642e4e1a070137c97f5110d8b4da 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -197,7 +197,7 @@ void __init ipc_init_proc_interface(const char *path, const char *header, * If key is found ipc contains its ipc structure */ -struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key) +static struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key) { struct kern_ipc_perm *ipc; int next_id; @@ -300,6 +300,105 @@ int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size) return id; } +/** + * ipcget_new - create a new ipc object + * @ns: namespace + * @ids: identifer set + * @ops: the actual creation routine to call + * @params: its parameters + * + * This routine is called sys_msgget, sys_semget() and sys_shmget() when + * the key is IPC_PRIVATE + */ +int ipcget_new(struct ipc_namespace *ns, struct ipc_ids *ids, + struct ipc_ops *ops, struct ipc_params *params) +{ + int err; + + err = idr_pre_get(&ids->ipcs_idr, GFP_KERNEL); + + if (!err) + return -ENOMEM; + + mutex_lock(&ids->mutex); + err = ops->getnew(ns, params); + mutex_unlock(&ids->mutex); + + return err; +} + +/** + * ipc_check_perms - check security and permissions for an IPC + * @ipcp: ipc permission set + * @ids: identifer set + * @ops: the actual security routine to call + * @params: its parameters + */ +static int ipc_check_perms(struct kern_ipc_perm *ipcp, struct ipc_ops *ops, + struct ipc_params *params) +{ + int err; + + if (ipcperms(ipcp, params->flg)) + 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 + * @ids: identifer set + * @ops: the actual creation routine to call + * @params: its parameters + * + * This routine is called sys_msgget, sys_semget() and sys_shmget() when + * the key is not IPC_PRIVATE + */ +int ipcget_public(struct ipc_namespace *ns, struct ipc_ids *ids, + struct ipc_ops *ops, struct ipc_params *params) +{ + struct kern_ipc_perm *ipcp; + int flg = params->flg; + int err; + + err = idr_pre_get(&ids->ipcs_idr, GFP_KERNEL); + + mutex_lock(&ids->mutex); + 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) + err = ipc_check_perms(ipcp, ops, params); + } + ipc_unlock(ipcp); + } + mutex_unlock(&ids->mutex); + + return err; +} + + /** * ipc_rmid - remove an IPC identifier * @ids: identifier set diff --git a/ipc/util.h b/ipc/util.h index c9063267d4f8fc15a0be1dd00a3d83fb029c2333..30b2a6d7cbed9655cf059f688670ce93122e36df 100644 --- a/ipc/util.h +++ b/ipc/util.h @@ -35,6 +35,35 @@ struct ipc_ids { struct idr ipcs_idr; }; +/* + * Structure that holds the parameters needed by the ipc operations + * (see after) + */ +struct ipc_params { + key_t key; + int flg; + union { + size_t size; /* for shared memories */ + int nsems; /* for semaphores */ + } u; /* holds the getnew() specific param */ +}; + +/* + * Structure that holds some ipc operations. This structure is used to unify + * the calls to sys_msgget(), sys_semget(), sys_shmget() + * . routine to call to create a new ipc object. Can be one of newque, + * newary, newseg + * . routine to call to call to check permissions for a new ipc object. + * Can be one of security_msg_associate, security_sem_associate, + * security_shm_associate + * . routine to call for an extra check if needed + */ +struct ipc_ops { + int (*getnew) (struct ipc_namespace *, struct ipc_params *); + int (*associate) (void *, int); + int (*more_checks) (void *, struct ipc_params *); +}; + struct seq_file; void ipc_init_ids(struct ipc_ids *); @@ -50,7 +79,6 @@ void __init ipc_init_proc_interface(const char *path, const char *header, #define IPC_SHM_IDS 2 /* must be called with ids->mutex acquired.*/ -struct kern_ipc_perm *ipc_findkey(struct ipc_ids *ids, key_t key); int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int); int ipc_get_maxid(struct ipc_ids *); @@ -95,5 +123,18 @@ int ipc_parse_version (int *cmd); extern void free_msg(struct msg_msg *msg); extern struct msg_msg *load_msg(const void __user *src, int len); extern int store_msg(void __user *dest, struct msg_msg *msg, int len); +extern int ipcget_new(struct ipc_namespace *, struct ipc_ids *, + struct ipc_ops *, struct ipc_params *); +extern int ipcget_public(struct ipc_namespace *, struct ipc_ids *, + struct ipc_ops *, struct ipc_params *); + +static inline 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); +} #endif