xref: /openbsd/sys/kern/sysv_sem.c (revision 5bf1588a)
1 /*	$OpenBSD: sysv_sem.c,v 1.63 2022/09/28 13:21:13 mbuhl Exp $	*/
2 /*	$NetBSD: sysv_sem.c,v 1.26 1996/02/09 19:00:25 christos Exp $	*/
3 
4 /*
5  * Copyright (c) 2002,2003 Todd C. Miller <millert@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  *
19  * Sponsored in part by the Defense Advanced Research Projects
20  * Agency (DARPA) and Air Force Research Laboratory, Air Force
21  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22  */
23 /*
24  * Implementation of SVID semaphores
25  *
26  * Author:  Daniel Boulet
27  *
28  * This software is provided ``AS IS'' without any warranties of any kind.
29  */
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/proc.h>
34 #include <sys/sem.h>
35 #include <sys/sysctl.h>
36 #include <sys/malloc.h>
37 #include <sys/pool.h>
38 
39 #include <sys/mount.h>
40 #include <sys/syscallargs.h>
41 
42 #ifdef SEM_DEBUG
43 #define DPRINTF(x)	printf x
44 #else
45 #define DPRINTF(x)
46 #endif
47 
48 int	semtot = 0;
49 int	semutot = 0;
50 struct	semid_ds **sema;	/* semaphore id list */
51 SLIST_HEAD(, sem_undo) semu_list; /* list of undo structures */
52 struct	pool sema_pool;		/* pool for struct semid_ds */
53 struct	pool semu_pool;		/* pool for struct sem_undo (SEMUSZ) */
54 unsigned short *semseqs;	/* array of sem sequence numbers */
55 
56 struct sem_undo *semu_alloc(struct process *);
57 int semundo_adjust(struct proc *, struct sem_undo **, int, int, int);
58 void semundo_clear(int, int);
59 
60 void
seminit(void)61 seminit(void)
62 {
63 
64 	pool_init(&sema_pool, sizeof(struct semid_ds), 0, 0, PR_WAITOK,
65 	    "semapl", NULL);
66 	pool_init(&semu_pool, SEMUSZ, 0, 0, PR_WAITOK, "semupl", NULL);
67 	sema = mallocarray(seminfo.semmni, sizeof(struct semid_ds *),
68 	    M_SEM, M_WAITOK|M_ZERO);
69 	semseqs = mallocarray(seminfo.semmni, sizeof(unsigned short),
70 	    M_SEM, M_WAITOK|M_ZERO);
71 	SLIST_INIT(&semu_list);
72 }
73 
74 /*
75  * Allocate a new sem_undo structure for a process
76  * (returns ptr to structure or NULL if no more room)
77  */
78 struct sem_undo *
semu_alloc(struct process * pr)79 semu_alloc(struct process *pr)
80 {
81 	struct sem_undo *suptr, *sutmp;
82 
83 	if (semutot == seminfo.semmnu)
84 		return (NULL);		/* no space */
85 
86 	/*
87 	 * Allocate a semu w/o waiting if possible.
88 	 * If we do have to wait, we must check to verify that a semu
89 	 * with un_proc == pr has not been allocated in the meantime.
90 	 */
91 	semutot++;
92 	if ((suptr = pool_get(&semu_pool, PR_NOWAIT)) == NULL) {
93 		sutmp = pool_get(&semu_pool, PR_WAITOK);
94 		SLIST_FOREACH(suptr, &semu_list, un_next) {
95 			if (suptr->un_proc == pr) {
96 				pool_put(&semu_pool, sutmp);
97 				semutot--;
98 				return (suptr);
99 			}
100 		}
101 		suptr = sutmp;
102 	}
103 	suptr->un_cnt = 0;
104 	suptr->un_proc = pr;
105 	SLIST_INSERT_HEAD(&semu_list, suptr, un_next);
106 	return (suptr);
107 }
108 
109 /*
110  * Adjust a particular entry for a particular proc
111  */
112 int
semundo_adjust(struct proc * p,struct sem_undo ** supptr,int semid,int semnum,int adjval)113 semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, int semnum,
114 	int adjval)
115 {
116 	struct process *pr = p->p_p;
117 	struct sem_undo *suptr;
118 	struct undo *sunptr;
119 	int i;
120 
121 	/*
122 	 * Look for and remember the sem_undo if the caller doesn't provide it.
123 	 */
124 	suptr = *supptr;
125 	if (suptr == NULL) {
126 		SLIST_FOREACH(suptr, &semu_list, un_next) {
127 			if (suptr->un_proc == pr) {
128 				*supptr = suptr;
129 				break;
130 			}
131 		}
132 		if (suptr == NULL) {
133 			if (adjval == 0)
134 				return (0);
135 			suptr = semu_alloc(p->p_p);
136 			if (suptr == NULL)
137 				return (ENOSPC);
138 			*supptr = suptr;
139 		}
140 	}
141 
142 	/*
143 	 * Look for the requested entry and adjust it
144 	 * (delete if adjval becomes 0).
145 	 */
146 	sunptr = &suptr->un_ent[0];
147 	for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
148 		if (sunptr->un_id != semid || sunptr->un_num != semnum)
149 			continue;
150 		if (adjval == 0)
151 			sunptr->un_adjval = 0;
152 		else
153 			sunptr->un_adjval += adjval;
154 		if (sunptr->un_adjval != 0)
155 			return (0);
156 
157 		if (--suptr->un_cnt == 0) {
158 			*supptr = NULL;
159 			SLIST_REMOVE(&semu_list, suptr, sem_undo, un_next);
160 			pool_put(&semu_pool, suptr);
161 			semutot--;
162 		} else if (i < suptr->un_cnt)
163 			suptr->un_ent[i] =
164 			    suptr->un_ent[suptr->un_cnt];
165 		return (0);
166 	}
167 
168 	/* Didn't find the right entry - create it */
169 	if (adjval == 0)
170 		return (0);
171 	if (suptr->un_cnt == SEMUME)
172 		return (EINVAL);
173 
174 	sunptr = &suptr->un_ent[suptr->un_cnt];
175 	suptr->un_cnt++;
176 	sunptr->un_adjval = adjval;
177 	sunptr->un_id = semid;
178 	sunptr->un_num = semnum;
179 	return (0);
180 }
181 
182 void
semundo_clear(int semid,int semnum)183 semundo_clear(int semid, int semnum)
184 {
185 	struct sem_undo *suptr = SLIST_FIRST(&semu_list);
186 	struct sem_undo *suprev = NULL;
187 	struct undo *sunptr;
188 	int i;
189 
190 	while (suptr != NULL) {
191 		sunptr = &suptr->un_ent[0];
192 		for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
193 			if (sunptr->un_id == semid) {
194 				if (semnum == -1 || sunptr->un_num == semnum) {
195 					suptr->un_cnt--;
196 					if (i < suptr->un_cnt) {
197 						suptr->un_ent[i] =
198 						  suptr->un_ent[suptr->un_cnt];
199 						i--, sunptr--;
200 					}
201 				}
202 				if (semnum != -1)
203 					break;
204 			}
205 		}
206 		if (suptr->un_cnt == 0) {
207 			struct sem_undo *sutmp = suptr;
208 
209 			if (suptr == SLIST_FIRST(&semu_list))
210 				SLIST_REMOVE_HEAD(&semu_list, un_next);
211 			else
212 				SLIST_REMOVE_AFTER(suprev, un_next);
213 			suptr = SLIST_NEXT(suptr, un_next);
214 			pool_put(&semu_pool, sutmp);
215 			semutot--;
216 		} else {
217 			suprev = suptr;
218 			suptr = SLIST_NEXT(suptr, un_next);
219 		}
220 	}
221 }
222 
223 int
sys___semctl(struct proc * p,void * v,register_t * retval)224 sys___semctl(struct proc *p, void *v, register_t *retval)
225 {
226 	struct sys___semctl_args /* {
227 		syscallarg(int) semid;
228 		syscallarg(int) semnum;
229 		syscallarg(int) cmd;
230 		syscallarg(union semun *) arg;
231 	} */ *uap = v;
232 	struct ucred *cred = p->p_ucred;
233 	int semid = SCARG(uap, semid);
234 	int semnum = SCARG(uap, semnum);
235 	int cmd = SCARG(uap, cmd);
236 	union semun arg, *uarg = SCARG(uap, arg);
237 	struct semid_ds sbuf;
238 	struct semid_ds *semaptr;
239 	unsigned short *semval = NULL, nsems;
240 	int i, ix, error;
241 
242 	switch (cmd) {
243 	case IPC_SET:
244 	case IPC_STAT:
245 	case GETALL:
246 	case SETVAL:
247 	case SETALL:
248 		if ((error = copyin(uarg, &arg, sizeof(union semun))))
249 			return (error);
250 	}
251 	if (cmd == IPC_SET)
252 		if ((error = copyin(arg.buf, &sbuf, sizeof(sbuf))))
253 			return (error);
254 
255 	DPRINTF(("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, uarg));
256 
257 	ix = IPCID_TO_IX(semid);
258 	if (ix < 0 || ix >= seminfo.semmni)
259 		return (EINVAL);
260 
261 again:
262 	if ((semaptr = sema[ix]) == NULL ||
263 	    semaptr->sem_perm.seq != IPCID_TO_SEQ(semid))
264 		return (EINVAL);
265 
266 	switch (cmd) {
267 	case IPC_RMID:
268 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0)
269 			return (error);
270 		semaptr->sem_perm.cuid = cred->cr_uid;
271 		semaptr->sem_perm.uid = cred->cr_uid;
272 		semtot -= semaptr->sem_nsems;
273 		free(semaptr->sem_base, M_SEM,
274 		    semaptr->sem_nsems * sizeof(struct sem));
275 		pool_put(&sema_pool, semaptr);
276 		sema[ix] = NULL;
277 		semundo_clear(ix, -1);
278 		wakeup(&sema[ix]);
279 		break;
280 
281 	case IPC_SET:
282 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
283 			return (error);
284 		semaptr->sem_perm.uid = sbuf.sem_perm.uid;
285 		semaptr->sem_perm.gid = sbuf.sem_perm.gid;
286 		semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
287 		    (sbuf.sem_perm.mode & 0777);
288 		semaptr->sem_ctime = gettime();
289 		break;
290 
291 	case IPC_STAT:
292 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
293 			return (error);
294 		memcpy(&sbuf, semaptr, sizeof sbuf);
295 		sbuf.sem_base = NULL;
296 		error = copyout(&sbuf, arg.buf, sizeof(struct semid_ds));
297 		break;
298 
299 	case GETNCNT:
300 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
301 			return (error);
302 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
303 			return (EINVAL);
304 		*retval = semaptr->sem_base[semnum].semncnt;
305 		break;
306 
307 	case GETPID:
308 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
309 			return (error);
310 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
311 			return (EINVAL);
312 		*retval = semaptr->sem_base[semnum].sempid;
313 		break;
314 
315 	case GETVAL:
316 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
317 			return (error);
318 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
319 			return (EINVAL);
320 		*retval = semaptr->sem_base[semnum].semval;
321 		break;
322 
323 	case GETALL:
324 		nsems = semaptr->sem_nsems;
325 		semval = mallocarray(nsems, sizeof(arg.array[0]),
326 		    M_TEMP, M_WAITOK);
327 		if (semaptr != sema[ix] ||
328 		    semaptr->sem_perm.seq != IPCID_TO_SEQ(semid) ||
329 		    semaptr->sem_nsems != nsems) {
330 			free(semval, M_TEMP, nsems * sizeof(arg.array[0]));
331 			goto again;
332 		}
333 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
334 			goto error;
335 		for (i = 0; i < nsems; i++)
336 			semval[i] = semaptr->sem_base[i].semval;
337 		for (i = 0; i < nsems; i++) {
338 			error = copyout(&semval[i], &arg.array[i],
339 			    sizeof(arg.array[0]));
340 			if (error != 0)
341 				break;
342 		}
343 		break;
344 
345 	case GETZCNT:
346 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
347 			return (error);
348 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
349 			return (EINVAL);
350 		*retval = semaptr->sem_base[semnum].semzcnt;
351 		break;
352 
353 	case SETVAL:
354 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
355 			return (error);
356 		if (semnum < 0 || semnum >= semaptr->sem_nsems)
357 			return (EINVAL);
358 		if (arg.val > seminfo.semvmx)
359 			return (ERANGE);
360 		semaptr->sem_base[semnum].semval = arg.val;
361 		semundo_clear(ix, semnum);
362 		wakeup(&sema[ix]);
363 		break;
364 
365 	case SETALL:
366 		nsems = semaptr->sem_nsems;
367 		semval = mallocarray(nsems, sizeof(arg.array[0]),
368 		    M_TEMP, M_WAITOK);
369 		for (i = 0; i < nsems; i++) {
370 			error = copyin(&arg.array[i], &semval[i],
371 			    sizeof(arg.array[0]));
372 			if (error != 0)
373 				goto error;
374 			if (semval[i] > seminfo.semvmx) {
375 				error = ERANGE;
376 				goto error;
377 			}
378 		}
379 		if (semaptr != sema[ix] ||
380 		    semaptr->sem_perm.seq != IPCID_TO_SEQ(semid) ||
381 		    semaptr->sem_nsems != nsems) {
382 			free(semval, M_TEMP, nsems * sizeof(arg.array[0]));
383 			goto again;
384 		}
385 		if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
386 			goto error;
387 		for (i = 0; i < nsems; i++)
388 			semaptr->sem_base[i].semval = semval[i];
389 		semundo_clear(ix, -1);
390 		wakeup(&sema[ix]);
391 		break;
392 
393 	default:
394 		return (EINVAL);
395 	}
396 
397 error:
398 	free(semval, M_TEMP, nsems * sizeof(arg.array[0]));
399 
400 	return (error);
401 }
402 
403 int
sys_semget(struct proc * p,void * v,register_t * retval)404 sys_semget(struct proc *p, void *v, register_t *retval)
405 {
406 	struct sys_semget_args /* {
407 		syscallarg(key_t) key;
408 		syscallarg(int) nsems;
409 		syscallarg(int) semflg;
410 	} */ *uap = v;
411 	int semid, error;
412 	int key = SCARG(uap, key);
413 	int nsems = SCARG(uap, nsems);
414 	int semflg = SCARG(uap, semflg);
415 	struct semid_ds *semaptr, *semaptr_new = NULL;
416 	struct ucred *cred = p->p_ucred;
417 
418 	DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg));
419 
420 	/*
421 	 * Preallocate space for the new semaphore.  If we are going
422 	 * to sleep, we want to sleep now to eliminate any race
423 	 * condition in allocating a semaphore with a specific key.
424 	 */
425 	if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
426 		if (nsems <= 0 || nsems > seminfo.semmsl) {
427 			DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems,
428 			    seminfo.semmsl));
429 			return (EINVAL);
430 		}
431 		if (nsems > seminfo.semmns - semtot) {
432 			DPRINTF(("not enough semaphores left (need %d, got %d)\n",
433 			    nsems, seminfo.semmns - semtot));
434 			return (ENOSPC);
435 		}
436 		semaptr_new = pool_get(&sema_pool, PR_WAITOK | PR_ZERO);
437 		semaptr_new->sem_base = mallocarray(nsems, sizeof(struct sem),
438 		    M_SEM, M_WAITOK|M_ZERO);
439 		if (nsems > seminfo.semmns - semtot) {
440 			error = ENOSPC;
441 			goto error;
442 		}
443 	}
444 
445 	if (key != IPC_PRIVATE) {
446 		for (semid = 0, semaptr = NULL; semid < seminfo.semmni; semid++) {
447 			if ((semaptr = sema[semid]) != NULL &&
448 			    semaptr->sem_perm.key == key) {
449 				DPRINTF(("found public key\n"));
450 				if ((error = ipcperm(cred, &semaptr->sem_perm,
451 				    semflg & 0700)))
452 					goto error;
453 				if (nsems > 0 && semaptr->sem_nsems < nsems) {
454 					DPRINTF(("too small\n"));
455 					error = EINVAL;
456 					goto error;
457 				}
458 				if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
459 					DPRINTF(("not exclusive\n"));
460 					error = EEXIST;
461 					goto error;
462 				}
463 				if (semaptr_new != NULL) {
464 					free(semaptr_new->sem_base, M_SEM,
465 					    nsems * sizeof(struct sem));
466 					pool_put(&sema_pool, semaptr_new);
467 				}
468 				goto found;
469 			}
470 		}
471 	}
472 
473 	DPRINTF(("need to allocate the semid_ds\n"));
474 	if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
475 		for (semid = 0; semid < seminfo.semmni; semid++) {
476 			if ((semaptr = sema[semid]) == NULL)
477 				break;
478 		}
479 		if (semid == seminfo.semmni) {
480 			DPRINTF(("no more semid_ds's available\n"));
481 			error = ENOSPC;
482 			goto error;
483 		}
484 		DPRINTF(("semid %d is available\n", semid));
485 		semaptr_new->sem_perm.key = key;
486 		semaptr_new->sem_perm.cuid = cred->cr_uid;
487 		semaptr_new->sem_perm.uid = cred->cr_uid;
488 		semaptr_new->sem_perm.cgid = cred->cr_gid;
489 		semaptr_new->sem_perm.gid = cred->cr_gid;
490 		semaptr_new->sem_perm.mode = (semflg & 0777);
491 		semaptr_new->sem_perm.seq = semseqs[semid] =
492 		    (semseqs[semid] + 1) & 0x7fff;
493 		semaptr_new->sem_nsems = nsems;
494 		semaptr_new->sem_otime = 0;
495 		semaptr_new->sem_ctime = gettime();
496 		sema[semid] = semaptr_new;
497 		semtot += nsems;
498 	} else {
499 		DPRINTF(("didn't find it and wasn't asked to create it\n"));
500 		return (ENOENT);
501 	}
502 
503 found:
504 	*retval = IXSEQ_TO_IPCID(semid, sema[semid]->sem_perm);
505 	return (0);
506 error:
507 	if (semaptr_new != NULL) {
508 		free(semaptr_new->sem_base, M_SEM, nsems * sizeof(struct sem));
509 		pool_put(&sema_pool, semaptr_new);
510 	}
511 	return (error);
512 }
513 
514 int
sys_semop(struct proc * p,void * v,register_t * retval)515 sys_semop(struct proc *p, void *v, register_t *retval)
516 {
517 	struct sys_semop_args /* {
518 		syscallarg(int) semid;
519 		syscallarg(struct sembuf *) sops;
520 		syscallarg(size_t) nsops;
521 	} */ *uap = v;
522 #define	NSOPS	8
523 	struct sembuf sopbuf[NSOPS];
524 	int semid = SCARG(uap, semid);
525 	size_t nsops = SCARG(uap, nsops);
526 	struct sembuf *sops;
527 	struct semid_ds *semaptr;
528 	struct sembuf *sopptr = NULL;
529 	struct sem *semptr = NULL;
530 	struct sem_undo *suptr = NULL;
531 	struct ucred *cred = p->p_ucred;
532 	size_t i, j;
533 	int do_wakeup, do_undos, error;
534 
535 	DPRINTF(("call to semop(%d, %p, %lu)\n", semid, SCARG(uap, sops),
536 	    (u_long)nsops));
537 
538 	semid = IPCID_TO_IX(semid);	/* Convert back to zero origin */
539 
540 	if (semid < 0 || semid >= seminfo.semmni)
541 		return (EINVAL);
542 
543 	if ((semaptr = sema[semid]) == NULL ||
544 	    semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid)))
545 		return (EINVAL);
546 
547 	if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
548 		DPRINTF(("error = %d from ipaccess\n", error));
549 		return (error);
550 	}
551 
552 	if (nsops == 0) {
553 		*retval = 0;
554 		return (0);
555 	} else if (nsops > (size_t)seminfo.semopm) {
556 		DPRINTF(("too many sops (max=%d, nsops=%lu)\n", seminfo.semopm,
557 		    (u_long)nsops));
558 		return (E2BIG);
559 	}
560 
561 	if (nsops <= NSOPS)
562 		sops = sopbuf;
563 	else
564 		sops = mallocarray(nsops, sizeof(struct sembuf), M_SEM, M_WAITOK);
565 	error = copyin(SCARG(uap, sops), sops, nsops * sizeof(struct sembuf));
566 	if (error != 0) {
567 		DPRINTF(("error = %d from copyin(%p, %p, %u)\n", error,
568 		    SCARG(uap, sops), &sops, nsops * sizeof(struct sembuf)));
569 		goto done2;
570 	}
571 
572 	/*
573 	 * Loop trying to satisfy the vector of requests.
574 	 * If we reach a point where we must wait, any requests already
575 	 * performed are rolled back and we go to sleep until some other
576 	 * process wakes us up.  At this point, we start all over again.
577 	 *
578 	 * This ensures that from the perspective of other tasks, a set
579 	 * of requests is atomic (never partially satisfied).
580 	 */
581 	do_undos = 0;
582 
583 	for (;;) {
584 		do_wakeup = 0;
585 
586 		for (i = 0; i < nsops; i++) {
587 			sopptr = &sops[i];
588 
589 			if (sopptr->sem_num >= semaptr->sem_nsems) {
590 				error = EFBIG;
591 				goto done2;
592 			}
593 
594 			semptr = &semaptr->sem_base[sopptr->sem_num];
595 
596 			DPRINTF(("semop:  semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
597 			    semaptr, semaptr->sem_base, semptr,
598 			    sopptr->sem_num, semptr->semval, sopptr->sem_op,
599 			    (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait"));
600 
601 			if (sopptr->sem_op < 0) {
602 				if ((int)(semptr->semval +
603 					  sopptr->sem_op) < 0) {
604 					DPRINTF(("semop:  can't do it now\n"));
605 					break;
606 				} else {
607 					semptr->semval += sopptr->sem_op;
608 					if (semptr->semval == 0 &&
609 					    semptr->semzcnt > 0)
610 						do_wakeup = 1;
611 				}
612 				if (sopptr->sem_flg & SEM_UNDO)
613 					do_undos++;
614 			} else if (sopptr->sem_op == 0) {
615 				if (semptr->semval > 0) {
616 					DPRINTF(("semop:  not zero now\n"));
617 					break;
618 				}
619 			} else {
620 				if (semptr->semncnt > 0)
621 					do_wakeup = 1;
622 				semptr->semval += sopptr->sem_op;
623 				if (sopptr->sem_flg & SEM_UNDO)
624 					do_undos++;
625 			}
626 		}
627 
628 		/*
629 		 * Did we get through the entire vector and can we undo it?
630 		 */
631 		if (i >= nsops && do_undos <= SEMUME)
632 			goto done;
633 
634 		/*
635 		 * No ... rollback anything that we've already done
636 		 */
637 		DPRINTF(("semop:  rollback 0 through %d\n", i - 1));
638 		for (j = 0; j < i; j++)
639 			semaptr->sem_base[sops[j].sem_num].semval -=
640 			    sops[j].sem_op;
641 
642 		/*
643 		 * Did we have too many SEM_UNDO's
644 		 */
645 		if (do_undos > SEMUME) {
646 			error = ENOSPC;
647 			goto done2;
648 		}
649 
650 		/*
651 		 * If the request that we couldn't satisfy has the
652 		 * NOWAIT flag set then return with EAGAIN.
653 		 */
654 		if (sopptr->sem_flg & IPC_NOWAIT) {
655 			error = EAGAIN;
656 			goto done2;
657 		}
658 
659 		if (sopptr->sem_op == 0)
660 			semptr->semzcnt++;
661 		else
662 			semptr->semncnt++;
663 
664 		DPRINTF(("semop:  good night!\n"));
665 		error = tsleep_nsec(&sema[semid], PLOCK | PCATCH,
666 		    "semwait", INFSLP);
667 		DPRINTF(("semop:  good morning (error=%d)!\n", error));
668 
669 		suptr = NULL;	/* sem_undo may have been reallocated */
670 
671 		/*
672 		 * Make sure that the semaphore still exists
673 		 */
674 		if (sema[semid] == NULL ||
675 		    semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) {
676 			error = EIDRM;
677 			goto done2;
678 		}
679 
680 		/*
681 		 * The semaphore is still alive.  Readjust the count of
682 		 * waiting processes.
683 		 */
684 		if (sopptr->sem_op == 0)
685 			semptr->semzcnt--;
686 		else
687 			semptr->semncnt--;
688 
689 		/*
690 		 * Is it really morning, or was our sleep interrupted?
691 		 * (Delayed check of tsleep() return code because we
692 		 * need to decrement sem[nz]cnt either way.)
693 		 */
694 		if (error != 0) {
695 			error = EINTR;
696 			goto done2;
697 		}
698 		DPRINTF(("semop:  good morning!\n"));
699 	}
700 
701 done:
702 	/*
703 	 * Process any SEM_UNDO requests.
704 	 */
705 	if (do_undos) {
706 		for (i = 0; i < nsops; i++) {
707 			/*
708 			 * We only need to deal with SEM_UNDO's for non-zero
709 			 * op's.
710 			 */
711 			int adjval;
712 
713 			if ((sops[i].sem_flg & SEM_UNDO) == 0)
714 				continue;
715 			adjval = sops[i].sem_op;
716 			if (adjval == 0)
717 				continue;
718 			error = semundo_adjust(p, &suptr, semid,
719 			    sops[i].sem_num, -adjval);
720 			if (error == 0)
721 				continue;
722 
723 			/*
724 			 * Uh-Oh!  We ran out of either sem_undo's or undo's.
725 			 * Rollback the adjustments to this point and then
726 			 * rollback the semaphore ups and down so we can return
727 			 * with an error with all structures restored.  We
728 			 * rollback the undo's in the exact reverse order that
729 			 * we applied them.  This guarantees that we won't run
730 			 * out of space as we roll things back out.
731 			 */
732 			for (j = i; j > 0;) {
733 				j--;
734 				if ((sops[j].sem_flg & SEM_UNDO) == 0)
735 					continue;
736 				adjval = sops[j].sem_op;
737 				if (adjval == 0)
738 					continue;
739 				if (semundo_adjust(p, &suptr, semid,
740 				    sops[j].sem_num, adjval) != 0)
741 					panic("semop - can't undo undos");
742 			}
743 
744 			for (j = 0; j < nsops; j++)
745 				semaptr->sem_base[sops[j].sem_num].semval -=
746 				    sops[j].sem_op;
747 
748 			DPRINTF(("error = %d from semundo_adjust\n", error));
749 			goto done2;
750 		} /* loop through the sops */
751 	} /* if (do_undos) */
752 
753 	/* We're definitely done - set the sempid's */
754 	for (i = 0; i < nsops; i++) {
755 		sopptr = &sops[i];
756 		semptr = &semaptr->sem_base[sopptr->sem_num];
757 		semptr->sempid = p->p_p->ps_pid;
758 	}
759 
760 	semaptr->sem_otime = gettime();
761 
762 	/* Do a wakeup if any semaphore was up'd. */
763 	if (do_wakeup) {
764 		DPRINTF(("semop:  doing wakeup\n"));
765 		wakeup(&sema[semid]);
766 		DPRINTF(("semop:  back from wakeup\n"));
767 	}
768 	DPRINTF(("semop:  done\n"));
769 	*retval = 0;
770 done2:
771 	if (sops != sopbuf)
772 		free(sops, M_SEM, nsops * sizeof(struct sembuf));
773 	return (error);
774 }
775 
776 /*
777  * Go through the undo structures for this process and apply the adjustments to
778  * semaphores.
779  */
780 void
semexit(struct process * pr)781 semexit(struct process *pr)
782 {
783 	struct sem_undo *suptr;
784 	struct sem_undo **supptr;
785 
786 	/*
787 	 * Go through the chain of undo vectors looking for one associated with
788 	 * this process.  Remember the pointer to the pointer to the element
789 	 * to dequeue it later.
790 	 */
791 	supptr = &SLIST_FIRST(&semu_list);
792 	SLIST_FOREACH(suptr, &semu_list, un_next) {
793 		if (suptr->un_proc == pr)
794 			break;
795 		supptr = &SLIST_NEXT(suptr, un_next);
796 	}
797 
798 	/*
799 	 * If there is no undo vector, skip to the end.
800 	 */
801 	if (suptr == NULL)
802 		return;
803 
804 	/*
805 	 * We now have an undo vector for this process.
806 	 */
807 	DPRINTF(("process @%p has undo structure with %d entries\n", pr,
808 	    suptr->un_cnt));
809 
810 	/*
811 	 * If there are any active undo elements then process them.
812 	 */
813 	if (suptr->un_cnt > 0) {
814 		int ix;
815 
816 		for (ix = 0; ix < suptr->un_cnt; ix++) {
817 			int semid = suptr->un_ent[ix].un_id;
818 			int semnum = suptr->un_ent[ix].un_num;
819 			int adjval = suptr->un_ent[ix].un_adjval;
820 			struct semid_ds *semaptr;
821 
822 			if ((semaptr = sema[semid]) == NULL)
823 				panic("semexit - semid not allocated");
824 			if (semnum >= semaptr->sem_nsems)
825 				panic("semexit - semnum out of range");
826 
827 			DPRINTF(("semexit:  %p id=%d num=%d(adj=%d) ; sem=%d\n",
828 			    suptr->un_proc, suptr->un_ent[ix].un_id,
829 			    suptr->un_ent[ix].un_num,
830 			    suptr->un_ent[ix].un_adjval,
831 			    semaptr->sem_base[semnum].semval));
832 
833 			if (adjval < 0 &&
834 			    semaptr->sem_base[semnum].semval < -adjval)
835 				semaptr->sem_base[semnum].semval = 0;
836 			else
837 				semaptr->sem_base[semnum].semval += adjval;
838 
839 			wakeup(&sema[semid]);
840 			DPRINTF(("semexit:  back from wakeup\n"));
841 		}
842 	}
843 
844 	/*
845 	 * Deallocate the undo vector.
846 	 */
847 	DPRINTF(("removing vector\n"));
848 	*supptr = SLIST_NEXT(suptr, un_next);
849 	pool_put(&semu_pool, suptr);
850 	semutot--;
851 }
852 
853 /* Expand semsegs and semseqs arrays */
854 void
sema_reallocate(int val)855 sema_reallocate(int val)
856 {
857 	struct semid_ds **sema_new;
858 	unsigned short *newseqs;
859 	sema_new = mallocarray(val, sizeof(struct semid_ds *),
860 	    M_SEM, M_WAITOK|M_ZERO);
861 	memcpy(sema_new, sema,
862 	    seminfo.semmni * sizeof(struct semid_ds *));
863 	newseqs = mallocarray(val, sizeof(unsigned short), M_SEM,
864 	    M_WAITOK|M_ZERO);
865 	memcpy(newseqs, semseqs,
866 	    seminfo.semmni * sizeof(unsigned short));
867 	free(sema, M_SEM, seminfo.semmni * sizeof(struct semid_ds *));
868 	free(semseqs, M_SEM, seminfo.semmni * sizeof(unsigned short));
869 	sema = sema_new;
870 	semseqs = newseqs;
871 	seminfo.semmni = val;
872 }
873 
874 const struct sysctl_bounded_args sysvsem_vars[] = {
875 	{ KERN_SEMINFO_SEMUME, &seminfo.semume, SYSCTL_INT_READONLY },
876 	{ KERN_SEMINFO_SEMUSZ, &seminfo.semusz, SYSCTL_INT_READONLY },
877 	{ KERN_SEMINFO_SEMVMX, &seminfo.semvmx, SYSCTL_INT_READONLY },
878 	{ KERN_SEMINFO_SEMAEM, &seminfo.semaem, SYSCTL_INT_READONLY },
879 	{ KERN_SEMINFO_SEMOPM, &seminfo.semopm, 1, INT_MAX },
880 };
881 
882 /*
883  * Userland access to struct seminfo.
884  */
885 int
sysctl_sysvsem(int * name,u_int namelen,void * oldp,size_t * oldlenp,void * newp,size_t newlen)886 sysctl_sysvsem(int *name, u_int namelen, void *oldp, size_t *oldlenp,
887 	void *newp, size_t newlen)
888 {
889 	int error, val;
890 
891 	if (namelen != 1)
892                         return (ENOTDIR);       /* leaf-only */
893 
894 	switch (name[0]) {
895 	case KERN_SEMINFO_SEMMNI:
896 		val = seminfo.semmni;
897 		error = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
898 		    &val, val, 0xffff);
899 		/* returns success and skips reallocation if val is unchanged */
900 		if (error || val == seminfo.semmni)
901 			return (error);
902 		sema_reallocate(val);
903 		return (0);
904 	case KERN_SEMINFO_SEMMNS:
905 		/* can't decrease semmns or go over 2^16 */
906 		return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
907 		    &seminfo.semmns, seminfo.semmns, 0xffff));
908 	case KERN_SEMINFO_SEMMNU:
909 		/* can't decrease semmnu or go over 2^16 */
910 		return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
911 		    &seminfo.semmnu, seminfo.semmnu, 0xffff));
912 	case KERN_SEMINFO_SEMMSL:
913 		/* can't decrease semmsl or go over 2^16 */
914 		return (sysctl_int_bounded(oldp, oldlenp, newp, newlen,
915 		    &seminfo.semmsl, seminfo.semmsl, 0xffff));
916 	default:
917 		return (sysctl_bounded_arr(sysvsem_vars, nitems(sysvsem_vars),
918 		    name, namelen, oldp, oldlenp, newp, newlen));
919 	}
920 	/* NOTREACHED */
921 }
922