Beispiel #1
0
int
sys_msgsnd(struct proc *p, void *v, register_t *retval)
{
	struct sys_msgsnd_args /* {
		syscallarg(int) msqid;
		syscallarg(const void *) msgp;
		syscallarg(size_t) msgsz;
		syscallarg(int) msgflg;
	} */ *uap = v;
	int msqid = SCARG(uap, msqid);
	const char *user_msgp = SCARG(uap, msgp);
	size_t msgsz = SCARG(uap, msgsz);
	int msgflg = SCARG(uap, msgflg);
	int segs_needed, eval;
	struct ucred *cred = p->p_ucred;
	struct msqid_kernel *msqkptr;
	struct msg *msghdr;
	short next;

	DPRINTF(("call to msgsnd(%d, %p, %d, %d)\n", msqid, user_msgp, msgsz,
	    msgflg));

	msqid = IPCID_TO_IX(msqid);

	if (msqid < 0 || msqid >= msginfo.msgmni) {
		DPRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
		    msginfo.msgmni));
		return (EINVAL);
	}

	msqkptr = &msqids[msqid];
	if (msqkptr->u.msg_qbytes == 0) {
		DPRINTF(("no such message queue id\n"));
		return (EINVAL);
	}
	if (msqkptr->u.msg_perm.seq != IPCID_TO_SEQ(SCARG(uap, msqid))) {
		DPRINTF(("wrong sequence number\n"));
		return (EINVAL);
	}

	if ((eval = ipcperm(cred, &msqkptr->u.msg_perm, IPC_W))) {
		DPRINTF(("requester doesn't have write access\n"));
		return (eval);
	}

#ifdef MAC
	eval = mac_sysvmsq_check_msqsnd(cred, msqkptr);
	if (eval)
		return (eval);
#endif

	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
	DPRINTF(("msgsz=%d, msgssz=%d, segs_needed=%d\n", msgsz,
	    msginfo.msgssz, segs_needed));
	for (;;) {
		int need_more_resources = 0;

		/*
		 * check msgsz [cannot be negative since it is unsigned]
		 * (inside this loop in case msg_qbytes changes while we sleep)
		 */

		if (msgsz > msqkptr->u.msg_qbytes) {
			DPRINTF(("msgsz > msqkptr->u.msg_qbytes\n"));
			return (EINVAL);
		}

		if (msqkptr->u.msg_perm.mode & MSG_LOCKED) {
			DPRINTF(("msqid is locked\n"));
			need_more_resources = 1;
		}
		if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes) {
			DPRINTF(("msgsz + msg_cbytes > msg_qbytes\n"));
			need_more_resources = 1;
		}
		if (segs_needed > nfree_msgmaps) {
			DPRINTF(("segs_needed > nfree_msgmaps\n"));
			need_more_resources = 1;
		}
		if (free_msghdrs == NULL) {
			DPRINTF(("no more msghdrs\n"));
			need_more_resources = 1;
		}

		if (need_more_resources) {
			int we_own_it;

			if ((msgflg & IPC_NOWAIT) != 0) {
				DPRINTF(("need more resources but caller "
				    "doesn't want to wait\n"));
				return (EAGAIN);
			}

			if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0) {
				DPRINTF(("we don't own the msqid_ds\n"));
				we_own_it = 0;
			} else {
				/* Force later arrivals to wait for our
				   request */
				DPRINTF(("we own the msqid_ds\n"));
				msqkptr->u.msg_perm.mode |= MSG_LOCKED;
				we_own_it = 1;
			}
			DPRINTF(("goodnight\n"));
			eval = tsleep(msqkptr, (PZERO - 4) | PCATCH,
			    "msgwait", 0);
			DPRINTF(("good morning, eval=%d\n", eval));
			if (we_own_it)
				msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
			if (eval != 0) {
				DPRINTF(("msgsnd: interrupted system call\n"));
				return (EINTR);
			}

			/*
			 * Make sure that the msq queue still exists
			 */

			if (msqkptr->u.msg_qbytes == 0) {
				DPRINTF(("msqid deleted\n"));
				return (EIDRM);
			}

		} else {
			DPRINTF(("got all the resources that we need\n"));
			break;
		}
	}

	/*
	 * We have the resources that we need.
	 * Make sure!
	 */

#ifdef DIAGNOSTIC
	if (msqkptr->u.msg_perm.mode & MSG_LOCKED)
		panic("msg_perm.mode & MSG_LOCKED");
	if (segs_needed > nfree_msgmaps)
		panic("segs_needed > nfree_msgmaps");
	if (msgsz + msqkptr->u.msg_cbytes > msqkptr->u.msg_qbytes)
		panic("msgsz + msg_cbytes > msg_qbytes");
	if (free_msghdrs == NULL)
		panic("no more msghdrs");
#endif

	/*
	 * Re-lock the msqid_ds in case we page-fault when copying in the
	 * message
	 */

#ifdef DIAGNOSTIC
	if ((msqkptr->u.msg_perm.mode & MSG_LOCKED) != 0)
		panic("msqid_ds is already locked");
#endif
	msqkptr->u.msg_perm.mode |= MSG_LOCKED;

	/*
	 * Allocate a message header
	 */

	msghdr = free_msghdrs;
	free_msghdrs = msghdr->msg_next;
	msghdr->msg_spot = -1;
	msghdr->msg_ts = msgsz;
#ifdef MAC
	/*
         * XXXMAC: Should the mac_sysvmsq_check_msgmsq check follow here
         * immediately?  Or, should it be checked just before the msg is
         * enqueued in the msgq (as it is done now)?
         */
        mac_sysvmsg_create(cred, msqkptr, msghdr);
#endif

	/*
	 * Allocate space for the message
	 */

	while (segs_needed > 0) {
#ifdef DIAGNOSTIC
		if (nfree_msgmaps <= 0)
			panic("not enough msgmaps");
		if (free_msgmaps == -1)
			panic("nil free_msgmaps");
#endif
		next = free_msgmaps;
#ifdef DIAGNOSTIC
		if (next <= -1)
			panic("next too low #1");
		if (next >= msginfo.msgseg)
			panic("next out of range #1");
#endif
		DPRINTF(("allocating segment %d to message\n", next));
		free_msgmaps = msgmaps[next].next;
		nfree_msgmaps--;
		msgmaps[next].next = msghdr->msg_spot;
		msghdr->msg_spot = next;
		segs_needed--;
	}

	/*
	 * Copy in the message type
	 */

	if ((eval = copyin(user_msgp, &msghdr->msg_type,
	    sizeof(msghdr->msg_type))) != 0) {
		DPRINTF(("error %d copying the message type\n", eval));
		msg_freehdr(msghdr);
		msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
		wakeup(msqkptr);
		return (eval);
	}
	user_msgp += sizeof(msghdr->msg_type);

	/*
	 * Validate the message type
	 */

	if (msghdr->msg_type < 1) {
		msg_freehdr(msghdr);
		msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
		wakeup(msqkptr);
		DPRINTF(("mtype (%d) < 1\n", msghdr->msg_type));
		return (EINVAL);
	}

	/*
	 * Copy in the message body
	 */

	next = msghdr->msg_spot;
	while (msgsz > 0) {
		size_t tlen;
		if (msgsz > msginfo.msgssz)
			tlen = msginfo.msgssz;
		else
			tlen = msgsz;
#ifdef DIAGNOSTIC
		if (next <= -1)
			panic("next too low #2");
		if (next >= msginfo.msgseg)
			panic("next out of range #2");
#endif
		if ((eval = copyin(user_msgp, &msgpool[next * msginfo.msgssz],
		    tlen)) != 0) {
			DPRINTF(("error %d copying in message segment\n",
			    eval));
			msg_freehdr(msghdr);
			msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;
			wakeup(msqkptr);
			return (eval);
		}
		msgsz -= tlen;
		user_msgp += tlen;
		next = msgmaps[next].next;
	}
#ifdef DIAGNOSTIC
	if (next != -1)
		panic("didn't use all the msg segments");
#endif
	/*
	 * We've got the message.  Unlock the msqid_ds.
	 */

	msqkptr->u.msg_perm.mode &= ~MSG_LOCKED;

	/*
	 * Make sure that the msqid_ds is still allocated.
	 */

	if (msqkptr->u.msg_qbytes == 0) {
		msg_freehdr(msghdr);
		wakeup(msqkptr);
		return (EIDRM);
	}

#ifdef MAC
	/*
	 * Note: Since the task/thread allocates the msghdr and usually
	 * primes it with its own MAC label, for a majority of policies, it
	 * won't be necessary to check whether the msghdr has access
	 * permissions to the msgq.  The mac_sysvmsq_check_msqsnd check would
	 * suffice in that case.  However, this hook may be required where
	 * individual policies derive a non-identical label for the msghdr
	 * from the current thread label and may want to check the msghdr
	 * enqueue permissions, along with read/write permissions to the
	 * msgq.
	 */
	eval = mac_sysvmsq_check_msgmsq(cred, msghdr, msqkptr);
	if (eval) {
		msg_freehdr(msghdr);
		wakeup(msqkptr);
		return (eval);
	}
#endif

	/*
	 * Put the message into the queue
	 */

	if (msqkptr->u.msg_first == NULL) {
		msqkptr->u.msg_first = msghdr;
		msqkptr->u.msg_last = msghdr;
	} else {
		msqkptr->u.msg_last->msg_next = msghdr;
		msqkptr->u.msg_last = msghdr;
	}
	msqkptr->u.msg_last->msg_next = NULL;

	msqkptr->u.msg_cbytes += msghdr->msg_ts;
	msqkptr->u.msg_qnum++;
	msqkptr->u.msg_lspid = p->p_p->ps_mainproc->p_pid;
	msqkptr->u.msg_stime = time_second;

	wakeup(msqkptr);
	*retval = 0;
	return (0);
}
Beispiel #2
0
int
msgsnd_nocancel(struct proc *p, struct msgsnd_nocancel_args *uap, int32_t *retval)
{
	int msqid = uap->msqid;
	user_addr_t user_msgp = uap->msgp;
	size_t msgsz = (size_t)uap->msgsz;	/* limit to 4G */
	int msgflg = uap->msgflg;
	int segs_needed, eval;
	struct msqid_kernel *msqptr;
	struct msg *msghdr;
	short next;
	user_long_t msgtype;


	SYSV_MSG_SUBSYS_LOCK();

	if (!msginit(0)) {
		eval =  ENOMEM;
		goto msgsndout;
	}

#ifdef MSG_DEBUG_OK
	printf("call to msgsnd(%d, 0x%qx, %ld, %d)\n", msqid, user_msgp, msgsz,
	    msgflg);
#endif

	AUDIT_ARG(svipc_id, msqid);
	msqid = IPCID_TO_IX(msqid);

	if (msqid < 0 || msqid >= msginfo.msgmni) {
#ifdef MSG_DEBUG_OK
		printf("msqid (%d) out of range (0<=msqid<%d)\n", msqid,
		    msginfo.msgmni);
#endif
		eval = EINVAL;
		goto msgsndout;
	}

	msqptr = &msqids[msqid];
	if (msqptr->u.msg_qbytes == 0) {
#ifdef MSG_DEBUG_OK
		printf("no such message queue id\n");
#endif
		eval = EINVAL;
		goto msgsndout;
	}
	if (msqptr->u.msg_perm._seq != IPCID_TO_SEQ(uap->msqid)) {
#ifdef MSG_DEBUG_OK
		printf("wrong sequence number\n");
#endif
		eval = EINVAL;
		goto msgsndout;
	}

	if ((eval = ipcperm(kauth_cred_get(), &msqptr->u.msg_perm, IPC_W))) {
#ifdef MSG_DEBUG_OK
		printf("requester doesn't have write access\n");
#endif
		goto msgsndout;
	}

#if CONFIG_MACF
	eval = mac_sysvmsq_check_msqsnd(kauth_cred_get(), msqptr);
	if (eval) 
		goto msgsndout;
#endif
	segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz;
#ifdef MSG_DEBUG_OK
	printf("msgsz=%ld, msgssz=%d, segs_needed=%d\n", msgsz, msginfo.msgssz,
	    segs_needed);
#endif

	/*
	 * If we suffer resource starvation, we will sleep in this loop and
	 * wait for more resources to become available.  This is a loop to
	 * ensure reacquisition of the mutex following any sleep, since there
	 * are multiple resources under contention.
	 */
	for (;;) {
		void *blocking_resource = NULL;

		/*
		 * Check that we have not had the maximum message size change
		 * out from under us and render our message invalid while we
		 * slept waiting for some resource.
		 */
		if (msgsz > msqptr->u.msg_qbytes) {
#ifdef MSG_DEBUG_OK
			printf("msgsz > msqptr->msg_qbytes\n");
#endif
			eval = EINVAL;
			goto msgsndout;
		}

		/*
		 * If the user_msqid_ds is already locked, we need to sleep on
		 * the queue until it's unlocked.
		 */
		if (msqptr->u.msg_perm.mode & MSG_LOCKED) {
#ifdef MSG_DEBUG_OK
			printf("msqid is locked\n");
#endif
			blocking_resource = msqptr;
		}

		/*
		 * If our message plus the messages already in the queue would
		 * cause us to exceed the maximum number of bytes wer are
		 * permitted to queue, then block on the queue until it drains.
		 */
		if (msgsz + msqptr->u.msg_cbytes > msqptr->u.msg_qbytes) {
#ifdef MSG_DEBUG_OK
			printf("msgsz + msg_cbytes > msg_qbytes\n");
#endif
			blocking_resource = msqptr;
		}

		/*
		 * Both message maps and message headers are protected by
		 * sleeping on the address of the pointer to the list of free
		 * message headers, since they are allocated and freed in
		 * tandem.
		 */
		if (segs_needed > nfree_msgmaps) {
#ifdef MSG_DEBUG_OK
			printf("segs_needed > nfree_msgmaps\n");
#endif
			blocking_resource = &free_msghdrs;
		}
		if (free_msghdrs == NULL) {
#ifdef MSG_DEBUG_OK
			printf("no more msghdrs\n");
#endif
			blocking_resource = &free_msghdrs;
		}

		if (blocking_resource != NULL) {
			int we_own_it;

			if ((msgflg & IPC_NOWAIT) != 0) {
#ifdef MSG_DEBUG_OK
				printf("need more resources but caller doesn't want to wait\n");
#endif
				eval = EAGAIN;
				goto msgsndout;
			}

			if ((msqptr->u.msg_perm.mode & MSG_LOCKED) != 0) {
#ifdef MSG_DEBUG_OK
				printf("we don't own the user_msqid_ds\n");
#endif
				we_own_it = 0;
			} else {
				/* Force later arrivals to wait for our
				   request */
#ifdef MSG_DEBUG_OK
				printf("we own the user_msqid_ds\n");
#endif
				msqptr->u.msg_perm.mode |= MSG_LOCKED;
				we_own_it = 1;
			}
#ifdef MSG_DEBUG_OK
			printf("goodnight\n");
#endif
			eval = msleep(blocking_resource, &sysv_msg_subsys_mutex, (PZERO - 4) | PCATCH,
			    "msgwait", 0);
#ifdef MSG_DEBUG_OK
			printf("good morning, eval=%d\n", eval);
#endif
			if (we_own_it)
				msqptr->u.msg_perm.mode &= ~MSG_LOCKED;
			if (eval != 0) {
#ifdef MSG_DEBUG_OK
				printf("msgsnd:  interrupted system call\n");
#endif
				eval = EINTR;
				goto msgsndout;
			}

			/*
			 * Make sure that the msq queue still exists
			 */

			if (msqptr->u.msg_qbytes == 0) {
#ifdef MSG_DEBUG_OK
				printf("msqid deleted\n");
#endif
				eval = EIDRM;
				goto msgsndout;
			
			}

		} else {
#ifdef MSG_DEBUG_OK
			printf("got all the resources that we need\n");
#endif
			break;
		}
	}

	/*
	 * We have the resources that we need.
	 * Make sure!
	 */

	if (msqptr->u.msg_perm.mode & MSG_LOCKED)
		panic("msg_perm.mode & MSG_LOCKED");
	if (segs_needed > nfree_msgmaps)
		panic("segs_needed > nfree_msgmaps");
	if (msgsz + msqptr->u.msg_cbytes > msqptr->u.msg_qbytes)
		panic("msgsz + msg_cbytes > msg_qbytes");
	if (free_msghdrs == NULL)
		panic("no more msghdrs");

	/*
	 * Re-lock the user_msqid_ds in case we page-fault when copying in
	 * the message
	 */
	if ((msqptr->u.msg_perm.mode & MSG_LOCKED) != 0)
		panic("user_msqid_ds is already locked");
	msqptr->u.msg_perm.mode |= MSG_LOCKED;

	/*
	 * Allocate a message header
	 */
	msghdr = free_msghdrs;
	free_msghdrs = msghdr->msg_next;
	msghdr->msg_spot = -1;
	msghdr->msg_ts = msgsz;

#if CONFIG_MACF
	mac_sysvmsg_label_associate(kauth_cred_get(), msqptr, msghdr);
#endif
	/*
	 * Allocate space for the message
	 */

	while (segs_needed > 0) {
		if (nfree_msgmaps <= 0)
			panic("not enough msgmaps");
		if (free_msgmaps == -1)
			panic("nil free_msgmaps");
		next = free_msgmaps;
		if (next <= -1)
			panic("next too low #1");
		if (next >= msginfo.msgseg)
			panic("next out of range #1");
#ifdef MSG_DEBUG_OK
		printf("allocating segment %d to message\n", next);
#endif
		free_msgmaps = msgmaps[next].next;
		nfree_msgmaps--;
		msgmaps[next].next = msghdr->msg_spot;
		msghdr->msg_spot = next;
		segs_needed--;
	}

	/*
	 * Copy in the message type.  For a 64 bit process, this is 64 bits,
	 * but we only ever use the low 32 bits, so the cast is OK.
	 */
	if (IS_64BIT_PROCESS(p)) {
		SYSV_MSG_SUBSYS_UNLOCK();
		eval = copyin(user_msgp, &msgtype, sizeof(msgtype));
		SYSV_MSG_SUBSYS_LOCK();
		msghdr->msg_type = CAST_DOWN(long,msgtype);
		user_msgp = user_msgp + sizeof(msgtype);	/* ptr math */
	} else {