diff options
Diffstat (limited to 'ipc')
| -rw-r--r-- | ipc/compat.c | 46 | ||||
| -rw-r--r-- | ipc/ipc_sysctl.c | 32 | ||||
| -rw-r--r-- | ipc/msg.c | 123 | ||||
| -rw-r--r-- | ipc/msgutil.c | 43 | ||||
| -rw-r--r-- | ipc/namespace.c | 3 | ||||
| -rw-r--r-- | ipc/util.c | 16 | ||||
| -rw-r--r-- | ipc/util.h | 2 | 
7 files changed, 207 insertions, 58 deletions
diff --git a/ipc/compat.c b/ipc/compat.c index ad9518eb26e..2547f29dcd1 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -306,6 +306,20 @@ static long do_compat_semctl(int first, int second, int third, u32 pad)  	return err;  } +long compat_do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz) +{ +	struct compat_msgbuf __user *msgp = dest; +	size_t msgsz; + +	if (put_user(msg->m_type, &msgp->mtype)) +		return -EFAULT; + +	msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz; +	if (store_msg(msgp->mtext, msg, msgsz)) +		return -EFAULT; +	return msgsz; +} +  #ifdef CONFIG_ARCH_WANT_OLD_COMPAT_IPC  long compat_sys_semctl(int first, int second, int third, void __user *uptr)  { @@ -337,10 +351,6 @@ long compat_sys_msgsnd(int first, int second, int third, void __user *uptr)  long compat_sys_msgrcv(int first, int second, int msgtyp, int third,  			   int version, void __user *uptr)  { -	struct compat_msgbuf __user *up; -	long type; -	int err; -  	if (first < 0)  		return -EINVAL;  	if (second < 0) @@ -348,23 +358,15 @@ long compat_sys_msgrcv(int first, int second, int msgtyp, int third,  	if (!version) {  		struct compat_ipc_kludge ipck; -		err = -EINVAL;  		if (!uptr) -			goto out; -		err = -EFAULT; +			return -EINVAL;  		if (copy_from_user (&ipck, uptr, sizeof(ipck))) -			goto out; +			return -EFAULT;  		uptr = compat_ptr(ipck.msgp);  		msgtyp = ipck.msgtyp;  	} -	up = uptr; -	err = do_msgrcv(first, &type, up->mtext, second, msgtyp, third); -	if (err < 0) -		goto out; -	if (put_user(type, &up->mtype)) -		err = -EFAULT; -out: -	return err; +	return do_msgrcv(first, uptr, second, msgtyp, third, +			 compat_do_msg_fill);  }  #else  long compat_sys_semctl(int semid, int semnum, int cmd, int arg) @@ -385,16 +387,8 @@ long compat_sys_msgsnd(int msqid, struct compat_msgbuf __user *msgp,  long compat_sys_msgrcv(int msqid, struct compat_msgbuf __user *msgp,  		       compat_ssize_t msgsz, long msgtyp, int msgflg)  { -	long err, mtype; - -	err =  do_msgrcv(msqid, &mtype, msgp->mtext, (ssize_t)msgsz, msgtyp, msgflg); -	if (err < 0) -		goto out; - -	if (put_user(mtype, &msgp->mtype)) -		err = -EFAULT; - out: -	return err; +	return do_msgrcv(msqid, msgp, (ssize_t)msgsz, msgtyp, msgflg, +			 compat_do_msg_fill);  }  #endif diff --git a/ipc/ipc_sysctl.c b/ipc/ipc_sysctl.c index 00fba2bab87..130dfece27a 100644 --- a/ipc/ipc_sysctl.c +++ b/ipc/ipc_sysctl.c @@ -158,6 +158,9 @@ static int proc_ipcauto_dointvec_minmax(ctl_table *table, int write,  static int zero;  static int one = 1; +#ifdef CONFIG_CHECKPOINT_RESTORE +static int int_max = INT_MAX; +#endif  static struct ctl_table ipc_kern_table[] = {  	{ @@ -227,6 +230,35 @@ static struct ctl_table ipc_kern_table[] = {  		.extra1		= &zero,  		.extra2		= &one,  	}, +#ifdef CONFIG_CHECKPOINT_RESTORE +	{ +		.procname	= "sem_next_id", +		.data		= &init_ipc_ns.ids[IPC_SEM_IDS].next_id, +		.maxlen		= sizeof(init_ipc_ns.ids[IPC_SEM_IDS].next_id), +		.mode		= 0644, +		.proc_handler	= proc_ipc_dointvec_minmax, +		.extra1		= &zero, +		.extra2		= &int_max, +	}, +	{ +		.procname	= "msg_next_id", +		.data		= &init_ipc_ns.ids[IPC_MSG_IDS].next_id, +		.maxlen		= sizeof(init_ipc_ns.ids[IPC_MSG_IDS].next_id), +		.mode		= 0644, +		.proc_handler	= proc_ipc_dointvec_minmax, +		.extra1		= &zero, +		.extra2		= &int_max, +	}, +	{ +		.procname	= "shm_next_id", +		.data		= &init_ipc_ns.ids[IPC_SHM_IDS].next_id, +		.maxlen		= sizeof(init_ipc_ns.ids[IPC_SHM_IDS].next_id), +		.mode		= 0644, +		.proc_handler	= proc_ipc_dointvec_minmax, +		.extra1		= &zero, +		.extra2		= &int_max, +	}, +#endif  	{}  }; diff --git a/ipc/msg.c b/ipc/msg.c index a71af5a65ab..950572f9d79 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -755,26 +755,91 @@ static inline int convert_mode(long *msgtyp, int msgflg)  	return SEARCH_EQUAL;  } -long do_msgrcv(int msqid, long *pmtype, void __user *mtext, -		size_t msgsz, long msgtyp, int msgflg) +static long do_msg_fill(void __user *dest, struct msg_msg *msg, size_t bufsz) +{ +	struct msgbuf __user *msgp = dest; +	size_t msgsz; + +	if (put_user(msg->m_type, &msgp->mtype)) +		return -EFAULT; + +	msgsz = (bufsz > msg->m_ts) ? msg->m_ts : bufsz; +	if (store_msg(msgp->mtext, msg, msgsz)) +		return -EFAULT; +	return msgsz; +} + +#ifdef CONFIG_CHECKPOINT_RESTORE +/* + * This function creates new kernel message structure, large enough to store + * bufsz message bytes. + */ +static inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz, +					   int msgflg, long *msgtyp, +					   unsigned long *copy_number) +{ +	struct msg_msg *copy; + +	*copy_number = *msgtyp; +	*msgtyp = 0; +	/* +	 * Create dummy message to copy real message to. +	 */ +	copy = load_msg(buf, bufsz); +	if (!IS_ERR(copy)) +		copy->m_ts = bufsz; +	return copy; +} + +static inline void free_copy(struct msg_msg *copy) +{ +	if (copy) +		free_msg(copy); +} +#else +static inline struct msg_msg *prepare_copy(void __user *buf, size_t bufsz, +					   int msgflg, long *msgtyp, +					   unsigned long *copy_number) +{ +	return ERR_PTR(-ENOSYS); +} + +static inline void free_copy(struct msg_msg *copy) +{ +} +#endif + +long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, +	       int msgflg, +	       long (*msg_handler)(void __user *, struct msg_msg *, size_t))  {  	struct msg_queue *msq;  	struct msg_msg *msg;  	int mode;  	struct ipc_namespace *ns; +	struct msg_msg *copy = NULL; +	unsigned long copy_number = 0; -	if (msqid < 0 || (long) msgsz < 0) +	if (msqid < 0 || (long) bufsz < 0)  		return -EINVAL; +	if (msgflg & MSG_COPY) { +		copy = prepare_copy(buf, bufsz, msgflg, &msgtyp, ©_number); +		if (IS_ERR(copy)) +			return PTR_ERR(copy); +	}  	mode = convert_mode(&msgtyp, msgflg);  	ns = current->nsproxy->ipc_ns;  	msq = msg_lock_check(ns, msqid); -	if (IS_ERR(msq)) +	if (IS_ERR(msq)) { +		free_copy(copy);  		return PTR_ERR(msq); +	}  	for (;;) {  		struct msg_receiver msr_d;  		struct list_head *tmp; +		long msg_counter = 0;  		msg = ERR_PTR(-EACCES);  		if (ipcperms(ns, &msq->q_perm, S_IRUGO)) @@ -793,12 +858,21 @@ long do_msgrcv(int msqid, long *pmtype, void __user *mtext,  				msg = walk_msg;  				if (mode == SEARCH_LESSEQUAL &&  						walk_msg->m_type != 1) { -					msg = walk_msg;  					msgtyp = walk_msg->m_type - 1; -				} else { -					msg = walk_msg; +				} else if (msgflg & MSG_COPY) { +					if (copy_number == msg_counter) { +						/* +						 * Found requested message. +						 * Copy it. +						 */ +						msg = copy_msg(msg, copy); +						if (IS_ERR(msg)) +							goto out_unlock; +						break; +					} +				} else  					break; -				} +				msg_counter++;  			}  			tmp = tmp->next;  		} @@ -807,10 +881,16 @@ long do_msgrcv(int msqid, long *pmtype, void __user *mtext,  			 * Found a suitable message.  			 * Unlink it from the queue.  			 */ -			if ((msgsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) { +			if ((bufsz < msg->m_ts) && !(msgflg & MSG_NOERROR)) {  				msg = ERR_PTR(-E2BIG);  				goto out_unlock;  			} +			/* +			 * If we are copying, then do not unlink message and do +			 * not update queue parameters. +			 */ +			if (msgflg & MSG_COPY) +				goto out_unlock;  			list_del(&msg->m_list);  			msq->q_qnum--;  			msq->q_rtime = get_seconds(); @@ -834,7 +914,7 @@ long do_msgrcv(int msqid, long *pmtype, void __user *mtext,  		if (msgflg & MSG_NOERROR)  			msr_d.r_maxsize = INT_MAX;  		else -			msr_d.r_maxsize = msgsz; +			msr_d.r_maxsize = bufsz;  		msr_d.r_msg = ERR_PTR(-EAGAIN);  		current->state = TASK_INTERRUPTIBLE;  		msg_unlock(msq); @@ -894,32 +974,21 @@ out_unlock:  			break;  		}  	} -	if (IS_ERR(msg)) +	if (IS_ERR(msg)) { +		free_copy(copy);  		return PTR_ERR(msg); +	} -	msgsz = (msgsz > msg->m_ts) ? msg->m_ts : msgsz; -	*pmtype = msg->m_type; -	if (store_msg(mtext, msg, msgsz)) -		msgsz = -EFAULT; - +	bufsz = msg_handler(buf, msg, bufsz);  	free_msg(msg); -	return msgsz; +	return bufsz;  }  SYSCALL_DEFINE5(msgrcv, int, msqid, struct msgbuf __user *, msgp, size_t, msgsz,  		long, msgtyp, int, msgflg)  { -	long err, mtype; - -	err =  do_msgrcv(msqid, &mtype, msgp->mtext, msgsz, msgtyp, msgflg); -	if (err < 0) -		goto out; - -	if (put_user(mtype, &msgp->mtype)) -		err = -EFAULT; -out: -	return err; +	return do_msgrcv(msqid, msgp, msgsz, msgtyp, msgflg, do_msg_fill);  }  #ifdef CONFIG_PROC_FS diff --git a/ipc/msgutil.c b/ipc/msgutil.c index 6471f1bdae9..ebfcbfa8b7f 100644 --- a/ipc/msgutil.c +++ b/ipc/msgutil.c @@ -102,7 +102,50 @@ out_err:  	free_msg(msg);  	return ERR_PTR(err);  } +#ifdef CONFIG_CHECKPOINT_RESTORE +struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst) +{ +	struct msg_msgseg *dst_pseg, *src_pseg; +	int len = src->m_ts; +	int alen; + +	BUG_ON(dst == NULL); +	if (src->m_ts > dst->m_ts) +		return ERR_PTR(-EINVAL); +	alen = len; +	if (alen > DATALEN_MSG) +		alen = DATALEN_MSG; + +	dst->next = NULL; +	dst->security = NULL; + +	memcpy(dst + 1, src + 1, alen); + +	len -= alen; +	dst_pseg = dst->next; +	src_pseg = src->next; +	while (len > 0) { +		alen = len; +		if (alen > DATALEN_SEG) +			alen = DATALEN_SEG; +		memcpy(dst_pseg + 1, src_pseg + 1, alen); +		dst_pseg = dst_pseg->next; +		len -= alen; +		src_pseg = src_pseg->next; +	} + +	dst->m_type = src->m_type; +	dst->m_ts = src->m_ts; + +	return dst; +} +#else +struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst) +{ +	return ERR_PTR(-ENOSYS); +} +#endif  int store_msg(void __user *dest, struct msg_msg *msg, int len)  {  	int alen; diff --git a/ipc/namespace.c b/ipc/namespace.c index cf3386a51de..7c1fa451b0b 100644 --- a/ipc/namespace.c +++ b/ipc/namespace.c @@ -170,7 +170,8 @@ static void ipcns_put(void *ns)  static int ipcns_install(struct nsproxy *nsproxy, void *new)  {  	struct ipc_namespace *ns = new; -	if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN)) +	if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) || +	    !nsown_capable(CAP_SYS_ADMIN))  		return -EPERM;  	/* Ditch state from the old ipc namespace */ diff --git a/ipc/util.c b/ipc/util.c index 72fd0785ac9..74e1d9c7a98 100644 --- a/ipc/util.c +++ b/ipc/util.c @@ -122,6 +122,7 @@ void ipc_init_ids(struct ipc_ids *ids)  	ids->in_use = 0;  	ids->seq = 0; +	ids->next_id = -1;  	{  		int seq_limit = INT_MAX/SEQ_MULTIPLIER;  		if (seq_limit > USHRT_MAX) @@ -252,6 +253,7 @@ int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)  	kuid_t euid;  	kgid_t egid;  	int id, err; +	int next_id = ids->next_id;  	if (size > IPCMNI)  		size = IPCMNI; @@ -264,7 +266,8 @@ int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)  	rcu_read_lock();  	spin_lock(&new->lock); -	err = idr_get_new(&ids->ipcs_idr, new, &id); +	err = idr_get_new_above(&ids->ipcs_idr, new, +				(next_id < 0) ? 0 : ipcid_to_idx(next_id), &id);  	if (err) {  		spin_unlock(&new->lock);  		rcu_read_unlock(); @@ -277,9 +280,14 @@ int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)  	new->cuid = new->uid = euid;  	new->gid = new->cgid = egid; -	new->seq = ids->seq++; -	if(ids->seq > ids->seq_max) -		ids->seq = 0; +	if (next_id < 0) { +		new->seq = ids->seq++; +		if (ids->seq > ids->seq_max) +			ids->seq = 0; +	} else { +		new->seq = ipcid_to_seqx(next_id); +		ids->next_id = -1; +	}  	new->id = ipc_buildid(id, new->seq);  	return id; diff --git a/ipc/util.h b/ipc/util.h index c8fe2f7631e..eeb79a1fbd8 100644 --- a/ipc/util.h +++ b/ipc/util.h @@ -92,6 +92,7 @@ void __init ipc_init_proc_interface(const char *path, const char *header,  #define IPC_SHM_IDS	2  #define ipcid_to_idx(id) ((id) % SEQ_MULTIPLIER) +#define ipcid_to_seqx(id) ((id) / SEQ_MULTIPLIER)  /* must be called with ids->rw_mutex acquired for writing */  int ipc_addid(struct ipc_ids *, struct kern_ipc_perm *, int); @@ -139,6 +140,7 @@ 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 struct msg_msg *copy_msg(struct msg_msg *src, struct msg_msg *dst);  extern int store_msg(void __user *dest, struct msg_msg *msg, int len);  extern void recompute_msgmni(struct ipc_namespace *);  |