]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/sysv_sem.c
7061dadc136266f02d40924aafe5a0131218a758
[apple/xnu.git] / bsd / kern / sysv_sem.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * Implementation of SVID semaphores
24 *
25 * Author: Daniel Boulet
26 *
27 * This software is provided ``AS IS'' without any warranties of any kind.
28 */
29 /*
30 * John Bellardo modified the implementation for Darwin. 12/2000
31 */
32
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/proc.h>
37 #include <sys/sem.h>
38 #include <sys/malloc.h>
39 #include <mach/mach_types.h>
40
41 #include <sys/filedesc.h>
42 #include <sys/file.h>
43
44 /*#include <sys/sysproto.h>*/
45 /*#include <sys/sysent.h>*/
46
47 /* Uncomment this line to see the debugging output */
48 /* #define SEM_DEBUG */
49
50 /* Macros to deal with the semaphore subsystem lock. The lock currently uses
51 * the semlock_holder static variable as a mutex. NULL means no lock, any
52 * value other than NULL means locked. semlock_holder is used because it was
53 * present in the code before the Darwin port, and for no other reason.
54 * When the time comes to relax the funnel requirements of the kernel only
55 * these macros should need to be changed. A spin lock would work well.
56 */
57 /* Aquire the lock */
58 #define SUBSYSTEM_LOCK_AQUIRE(p) { sysv_sem_aquiring_threads++; \
59 while (semlock_holder != NULL) \
60 (void) tsleep((caddr_t)&semlock_holder, (PZERO - 4), "sysvsem", 0); \
61 semlock_holder = p; \
62 sysv_sem_aquiring_threads--; }
63
64 /* Release the lock */
65 #define SUBSYSTEM_LOCK_RELEASE { semlock_holder = NULL; wakeup((caddr_t)&semlock_holder); }
66
67 /* Release the lock and return a value */
68 #define UNLOCK_AND_RETURN(ret) { SUBSYSTEM_LOCK_RELEASE; return(ret); }
69
70 #define M_SYSVSEM M_SUBPROC
71
72 #if 0
73 static void seminit __P((void *));
74 SYSINIT(sysv_sem, SI_SUB_SYSV_SEM, SI_ORDER_FIRST, seminit, NULL)
75 #endif 0
76
77 /* Hard system limits to avoid resource starvation / DOS attacks.
78 * These are not needed if we can make the semaphore pages swappable.
79 */
80 static struct seminfo limitseminfo = {
81 SEMMAP, /* # of entries in semaphore map */
82 SEMMNI, /* # of semaphore identifiers */
83 SEMMNS, /* # of semaphores in system */
84 SEMMNU, /* # of undo structures in system */
85 SEMMSL, /* max # of semaphores per id */
86 SEMOPM, /* max # of operations per semop call */
87 SEMUME, /* max # of undo entries per process */
88 SEMUSZ, /* size in bytes of undo structure */
89 SEMVMX, /* semaphore maximum value */
90 SEMAEM /* adjust on exit max value */
91 };
92
93 /* Current system allocations. We use this structure to track how many
94 * resources we have allocated so far. This way we can set large hard limits
95 * and not allocate the memory for them up front.
96 */
97 struct seminfo seminfo = {
98 SEMMAP, /* Unused, # of entries in semaphore map */
99 0, /* # of semaphore identifiers */
100 0, /* # of semaphores in system */
101 0, /* # of undo entries in system */
102 SEMMSL, /* max # of semaphores per id */
103 SEMOPM, /* max # of operations per semop call */
104 SEMUME, /* max # of undo entries per process */
105 SEMUSZ, /* size in bytes of undo structure */
106 SEMVMX, /* semaphore maximum value */
107 SEMAEM /* adjust on exit max value */
108 };
109
110 /* A counter so the module unload code knows when there are no more processes using
111 * the sysv_sem code */
112 static long sysv_sem_sleeping_threads = 0;
113 static long sysv_sem_aquiring_threads = 0;
114
115 struct semctl_args;
116 int semctl __P((struct proc *p, struct semctl_args *uap, int *));
117 struct semget_args;
118 int semget __P((struct proc *p, struct semget_args *uap, int *));
119 struct semop_args;
120 int semop __P((struct proc *p, struct semop_args *uap, int *));
121 struct semconfig_args;
122 int semconfig __P((struct proc *p, struct semconfig_args *uap, int *));
123
124
125 static struct sem_undo *semu_alloc __P((struct proc *p));
126 static int semundo_adjust __P((struct proc *p, struct sem_undo **supptr,
127 int semid, int semnum, int adjval));
128 static void semundo_clear __P((int semid, int semnum));
129
130 typedef int sy_call_t __P((struct proc *, void *, int *));
131
132 /* XXX casting to (sy_call_t *) is bogus, as usual. */
133 static sy_call_t *semcalls[] = {
134 (sy_call_t *)semctl, (sy_call_t *)semget,
135 (sy_call_t *)semop, (sy_call_t *)semconfig
136 };
137
138 static int semtot = 0; /* # of used semaphores */
139 struct semid_ds *sema = NULL; /* semaphore id pool */
140 struct sem *sem = NULL; /* semaphore pool */
141 static struct sem_undo *semu_list = NULL; /* list of active undo structures */
142 struct sem_undo *semu = NULL; /* semaphore undo pool */
143
144 static struct proc *semlock_holder = NULL;
145
146 /* seminit no longer needed. The data structures are grown dynamically */
147 void
148 seminit()
149 {
150 }
151
152 /*
153 * Entry point for all SEM calls
154 *
155 * In Darwin this is no longer the entry point. It will be removed after
156 * the code has been tested better.
157 */
158 struct semsys_args {
159 u_int which;
160 int a2;
161 int a3;
162 int a4;
163 int a5;
164 };
165 int
166 semsys(p, uap, retval)
167 struct proc *p;
168 /* XXX actually varargs. */
169 struct semsys_args *uap;
170 register_t *retval;
171 {
172
173 /* The individual calls handling the locking now */
174 /*while (semlock_holder != NULL && semlock_holder != p)
175 (void) tsleep((caddr_t)&semlock_holder, (PZERO - 4), "semsys", 0);
176 */
177
178 if (uap->which >= sizeof(semcalls)/sizeof(semcalls[0]))
179 return (EINVAL);
180 return ((*semcalls[uap->which])(p, &uap->a2, retval));
181 }
182
183 /*
184 * Lock or unlock the entire semaphore facility.
185 *
186 * This will probably eventually evolve into a general purpose semaphore
187 * facility status enquiry mechanism (I don't like the "read /dev/kmem"
188 * approach currently taken by ipcs and the amount of info that we want
189 * to be able to extract for ipcs is probably beyond what the capability
190 * of the getkerninfo facility.
191 *
192 * At the time that the current version of semconfig was written, ipcs is
193 * the only user of the semconfig facility. It uses it to ensure that the
194 * semaphore facility data structures remain static while it fishes around
195 * in /dev/kmem.
196 */
197
198 #ifndef _SYS_SYSPROTO_H_
199 struct semconfig_args {
200 semconfig_ctl_t flag;
201 };
202 #endif
203
204 int
205 semconfig(p, uap, retval)
206 struct proc *p;
207 struct semconfig_args *uap;
208 register_t *retval;
209 {
210 int eval = 0;
211
212 switch (uap->flag) {
213 case SEM_CONFIG_FREEZE:
214 SUBSYSTEM_LOCK_AQUIRE(p);
215 break;
216
217 case SEM_CONFIG_THAW:
218 SUBSYSTEM_LOCK_RELEASE;
219 break;
220
221 default:
222 printf("semconfig: unknown flag parameter value (%d) - ignored\n",
223 uap->flag);
224 eval = EINVAL;
225 break;
226 }
227
228 *retval = 0;
229 return(eval);
230 }
231
232 /* Expand the semu array to the given capacity. If the expansion fails
233 * return 0, otherwise return 1.
234 *
235 * Assumes we already have the subsystem lock.
236 */
237 static int
238 grow_semu_array(newSize)
239 int newSize;
240 {
241 register int i, j;
242 register struct sem_undo *newSemu;
243 if (newSize <= seminfo.semmnu)
244 return 0;
245 if (newSize > limitseminfo.semmnu) /* enforce hard limit */
246 {
247 #ifdef SEM_DEBUG
248 printf("undo structure hard limit of %d reached, requested %d\n",
249 limitseminfo.semmnu, newSize);
250 #endif
251 return 0;
252 }
253 newSize = (newSize/SEMMNU_INC + 1) * SEMMNU_INC;
254 newSize = newSize > limitseminfo.semmnu ? limitseminfo.semmnu : newSize;
255
256 #ifdef SEM_DEBUG
257 printf("growing semu[] from %d to %d\n", seminfo.semmnu, newSize);
258 #endif
259 MALLOC(newSemu, struct sem_undo*, sizeof(struct sem_undo)*newSize,
260 M_SYSVSEM, M_WAITOK);
261 if (NULL == newSemu)
262 {
263 #ifdef SEM_DEBUG
264 printf("allocation failed. no changes made.\n");
265 #endif
266 return 0;
267 }
268
269 /* Initialize our structure. */
270 for (i = 0; i < seminfo.semmnu; i++)
271 {
272 newSemu[i] = semu[i];
273 for(j = 0; j < SEMUME; j++) /* Is this really needed? */
274 newSemu[i].un_ent[j] = semu[i].un_ent[j];
275 }
276 for (i = seminfo.semmnu; i < newSize; i++)
277 {
278 newSemu[i].un_proc = NULL;
279 }
280
281 /* Clean up the old array */
282 if (semu)
283 FREE(semu, M_SYSVSEM);
284
285 semu = newSemu;
286 seminfo.semmnu = newSize;
287 #ifdef SEM_DEBUG
288 printf("expansion successful\n");
289 #endif
290 return 1;
291 }
292
293 /*
294 * Expand the sema array to the given capacity. If the expansion fails
295 * we return 0, otherwise we return 1.
296 *
297 * Assumes we already have the subsystem lock.
298 */
299 static int
300 grow_sema_array(newSize)
301 int newSize;
302 {
303 register struct semid_ds *newSema;
304 register int i;
305
306 if (newSize <= seminfo.semmni)
307 return 0;
308 if (newSize > limitseminfo.semmni) /* enforce hard limit */
309 {
310 #ifdef SEM_DEBUG
311 printf("identifier hard limit of %d reached, requested %d\n",
312 limitseminfo.semmni, newSize);
313 #endif
314 return 0;
315 }
316 newSize = (newSize/SEMMNI_INC + 1) * SEMMNI_INC;
317 newSize = newSize > limitseminfo.semmni ? limitseminfo.semmni : newSize;
318
319 #ifdef SEM_DEBUG
320 printf("growing sema[] from %d to %d\n", seminfo.semmni, newSize);
321 #endif
322 MALLOC(newSema, struct semid_ds*, sizeof(struct semid_ds)*newSize,
323 M_SYSVSEM, M_WAITOK);
324 if (NULL == newSema)
325 {
326 #ifdef SEM_DEBUG
327 printf("allocation failed. no changes made.\n");
328 #endif
329 return 0;
330 }
331
332 /* Initialize our new ids, and copy over the old ones */
333 for (i = 0; i < seminfo.semmni; i++)
334 {
335 newSema[i] = sema[i];
336 /* This is a hack. What we really want to be able to
337 * do is change the value a process is waiting on
338 * without waking it up, but I don't know how to do
339 * this with the existing code, so we wake up the
340 * process and let it do a lot of work to determine the
341 * semaphore set is really not available yet, and then
342 * sleep on the correct, reallocated semid_ds pointer.
343 */
344 if (sema[i].sem_perm.mode & SEM_ALLOC)
345 wakeup((caddr_t)&sema[i]);
346 }
347
348 for (i = seminfo.semmni; i < newSize; i++)
349 {
350 newSema[i].sem_base = 0;
351 newSema[i].sem_perm.mode = 0;
352 }
353
354 /* Clean up the old array */
355 if (sema)
356 FREE(sema, M_SYSVSEM);
357
358 sema = newSema;
359 seminfo.semmni = newSize;
360 #ifdef SEM_DEBUG
361 printf("expansion successful\n");
362 #endif
363 return 1;
364 }
365
366 /*
367 * Expand the sem array to the given capacity. If the expansion fails
368 * we return 0 (fail), otherwise we return 1 (success).
369 *
370 * Assumes we already hold the subsystem lock.
371 */
372 static int
373 grow_sem_array(newSize)
374 int newSize;
375 {
376 register struct sem *newSem = NULL;
377 register int i;
378
379 if (newSize < semtot)
380 return 0;
381 if (newSize > limitseminfo.semmns) /* enforce hard limit */
382 {
383 #ifdef SEM_DEBUG
384 printf("semaphore hard limit of %d reached, requested %d\n",
385 limitseminfo.semmns, newSize);
386 #endif
387 return 0;
388 }
389 newSize = (newSize/SEMMNS_INC + 1) * SEMMNS_INC;
390 newSize = newSize > limitseminfo.semmns ? limitseminfo.semmns : newSize;
391
392 #ifdef SEM_DEBUG
393 printf("growing sem array from %d to %d\n", seminfo.semmns, newSize);
394 #endif
395 MALLOC(newSem, struct sem*, sizeof(struct sem)*newSize,
396 M_SYSVSEM, M_WAITOK);
397 if (NULL == newSem)
398 {
399 #ifdef SEM_DEBUG
400 printf("allocation failed. no changes made.\n");
401 #endif
402 return 0;
403 }
404
405 /* We have our new memory, now copy the old contents over */
406 if (sem)
407 for(i = 0; i < seminfo.semmns; i++)
408 newSem[i] = sem[i];
409
410 /* Update our id structures to point to the new semaphores */
411 for(i = 0; i < seminfo.semmni; i++)
412 if (sema[i].sem_perm.mode & SEM_ALLOC) /* ID in use */
413 {
414 if (newSem > sem)
415 sema[i].sem_base += newSem - sem;
416 else
417 sema[i].sem_base -= sem - newSem;
418 }
419
420 /* clean up the old array */
421 if (sem)
422 FREE(sem, M_SYSVSEM);
423
424 sem = newSem;
425 seminfo.semmns = newSize;
426 #ifdef SEM_DEBUG
427 printf("expansion complete\n");
428 #endif
429 return 1;
430 }
431
432 /*
433 * Allocate a new sem_undo structure for a process
434 * (returns ptr to structure or NULL if no more room)
435 *
436 * Assumes we already hold the subsystem lock.
437 */
438
439 static struct sem_undo *
440 semu_alloc(p)
441 struct proc *p;
442 {
443 register int i;
444 register struct sem_undo *suptr;
445 register struct sem_undo **supptr;
446 int attempt;
447
448 /*
449 * Try twice to allocate something.
450 * (we'll purge any empty structures after the first pass so
451 * two passes are always enough)
452 */
453
454 for (attempt = 0; attempt < 2; attempt++) {
455 /*
456 * Look for a free structure.
457 * Fill it in and return it if we find one.
458 */
459
460 for (i = 0; i < seminfo.semmnu; i++) {
461 suptr = SEMU(i);
462 if (suptr->un_proc == NULL) {
463 suptr->un_next = semu_list;
464 semu_list = suptr;
465 suptr->un_cnt = 0;
466 suptr->un_proc = p;
467 return(suptr);
468 }
469 }
470
471 /*
472 * We didn't find a free one, if this is the first attempt
473 * then try to free some structures.
474 */
475
476 if (attempt == 0) {
477 /* All the structures are in use - try to free some */
478 int did_something = 0;
479
480 supptr = &semu_list;
481 while ((suptr = *supptr) != NULL) {
482 if (suptr->un_cnt == 0) {
483 suptr->un_proc = NULL;
484 *supptr = suptr->un_next;
485 did_something = 1;
486 } else
487 supptr = &(suptr->un_next);
488 }
489
490 /* If we didn't free anything. Try expanding
491 * the semu[] array. If that doesn't work
492 * then fail. We expand last to get the
493 * most reuse out of existing resources.
494 */
495 if (!did_something)
496 if (!grow_semu_array(seminfo.semmnu + 1))
497 return(NULL);
498 } else {
499 /*
500 * The second pass failed even though we freed
501 * something after the first pass!
502 * This is IMPOSSIBLE!
503 */
504 panic("semu_alloc - second attempt failed");
505 }
506 }
507 return (NULL);
508 }
509
510 /*
511 * Adjust a particular entry for a particular proc
512 *
513 * Assumes we already hold the subsystem lock.
514 */
515
516 static int
517 semundo_adjust(p, supptr, semid, semnum, adjval)
518 register struct proc *p;
519 struct sem_undo **supptr;
520 int semid, semnum;
521 int adjval;
522 {
523 register struct sem_undo *suptr;
524 register struct undo *sunptr;
525 int i;
526
527 /* Look for and remember the sem_undo if the caller doesn't provide
528 it */
529
530 suptr = *supptr;
531 if (suptr == NULL) {
532 for (suptr = semu_list; suptr != NULL;
533 suptr = suptr->un_next) {
534 if (suptr->un_proc == p) {
535 *supptr = suptr;
536 break;
537 }
538 }
539 if (suptr == NULL) {
540 if (adjval == 0)
541 return(0);
542 suptr = semu_alloc(p);
543 if (suptr == NULL)
544 return(ENOSPC);
545 *supptr = suptr;
546 }
547 }
548
549 /*
550 * Look for the requested entry and adjust it (delete if adjval becomes
551 * 0).
552 */
553 sunptr = &suptr->un_ent[0];
554 for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
555 if (sunptr->un_id != semid || sunptr->un_num != semnum)
556 continue;
557 if (adjval == 0)
558 sunptr->un_adjval = 0;
559 else
560 sunptr->un_adjval += adjval;
561 if (sunptr->un_adjval == 0) {
562 suptr->un_cnt--;
563 if (i < suptr->un_cnt)
564 suptr->un_ent[i] =
565 suptr->un_ent[suptr->un_cnt];
566 }
567 return(0);
568 }
569
570 /* Didn't find the right entry - create it */
571 if (adjval == 0)
572 return(0);
573 if (suptr->un_cnt != seminfo.semume) {
574 sunptr = &suptr->un_ent[suptr->un_cnt];
575 suptr->un_cnt++;
576 sunptr->un_adjval = adjval;
577 sunptr->un_id = semid; sunptr->un_num = semnum;
578 } else
579 return(EINVAL);
580 return(0);
581 }
582
583 /* Assumes we already hold the subsystem lock.
584 */
585 static void
586 semundo_clear(semid, semnum)
587 int semid, semnum;
588 {
589 register struct sem_undo *suptr;
590
591 for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) {
592 register struct undo *sunptr = &suptr->un_ent[0];
593 register int i = 0;
594
595 while (i < suptr->un_cnt) {
596 if (sunptr->un_id == semid) {
597 if (semnum == -1 || sunptr->un_num == semnum) {
598 suptr->un_cnt--;
599 if (i < suptr->un_cnt) {
600 suptr->un_ent[i] =
601 suptr->un_ent[suptr->un_cnt];
602 continue;
603 }
604 }
605 if (semnum != -1)
606 break;
607 }
608 i++, sunptr++;
609 }
610 }
611 }
612
613 /*
614 * Note that the user-mode half of this passes a union, not a pointer
615 */
616 #ifndef _SYS_SYSPROTO_H_
617 struct semctl_args {
618 int semid;
619 int semnum;
620 int cmd;
621 union semun arg;
622 };
623 #endif
624
625 int
626 semctl(p, uap, retval)
627 struct proc *p;
628 register struct semctl_args *uap;
629 register_t *retval;
630 {
631 int semid = uap->semid;
632 int semnum = uap->semnum;
633 int cmd = uap->cmd;
634 union semun arg = uap->arg;
635 union semun real_arg;
636 struct ucred *cred = p->p_ucred;
637 int i, rval, eval;
638 struct semid_ds sbuf;
639 register struct semid_ds *semaptr;
640
641 SUBSYSTEM_LOCK_AQUIRE(p);
642 #ifdef SEM_DEBUG
643 printf("call to semctl(%d, %d, %d, 0x%x)\n", semid, semnum, cmd, arg);
644 #endif
645
646 semid = IPCID_TO_IX(semid);
647 if (semid < 0 || semid >= seminfo.semmsl)
648 {
649 #ifdef SEM_DEBUG
650 printf("Invalid semid\n");
651 #endif
652 UNLOCK_AND_RETURN(EINVAL);
653 }
654
655 semaptr = &sema[semid];
656 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
657 semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid))
658 UNLOCK_AND_RETURN(EINVAL);
659
660 eval = 0;
661 rval = 0;
662
663 switch (cmd) {
664 case IPC_RMID:
665 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
666 UNLOCK_AND_RETURN(eval);
667 semaptr->sem_perm.cuid = cred->cr_uid;
668 semaptr->sem_perm.uid = cred->cr_uid;
669 semtot -= semaptr->sem_nsems;
670 for (i = semaptr->sem_base - sem; i < semtot; i++)
671 sem[i] = sem[i + semaptr->sem_nsems];
672 for (i = 0; i < seminfo.semmni; i++) {
673 if ((sema[i].sem_perm.mode & SEM_ALLOC) &&
674 sema[i].sem_base > semaptr->sem_base)
675 sema[i].sem_base -= semaptr->sem_nsems;
676 }
677 semaptr->sem_perm.mode = 0;
678 semundo_clear(semid, -1);
679 wakeup((caddr_t)semaptr);
680 break;
681
682 case IPC_SET:
683 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
684 UNLOCK_AND_RETURN(eval);
685 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
686 UNLOCK_AND_RETURN(eval);*/
687 if ((eval = copyin(arg.buf, (caddr_t)&sbuf,
688 sizeof(sbuf))) != 0)
689 UNLOCK_AND_RETURN(eval);
690 semaptr->sem_perm.uid = sbuf.sem_perm.uid;
691 semaptr->sem_perm.gid = sbuf.sem_perm.gid;
692 semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
693 (sbuf.sem_perm.mode & 0777);
694 semaptr->sem_ctime = time_second;
695 break;
696
697 case IPC_STAT:
698 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
699 UNLOCK_AND_RETURN(eval);
700 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
701 UNLOCK_AND_RETURN(eval);*/
702 eval = copyout((caddr_t)semaptr, arg.buf,
703 sizeof(struct semid_ds));
704 break;
705
706 case GETNCNT:
707 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
708 UNLOCK_AND_RETURN(eval);
709 if (semnum < 0 || semnum >= semaptr->sem_nsems)
710 UNLOCK_AND_RETURN(EINVAL);
711 rval = semaptr->sem_base[semnum].semncnt;
712 break;
713
714 case GETPID:
715 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
716 UNLOCK_AND_RETURN(eval);
717 if (semnum < 0 || semnum >= semaptr->sem_nsems)
718 UNLOCK_AND_RETURN(EINVAL);
719 rval = semaptr->sem_base[semnum].sempid;
720 break;
721
722 case GETVAL:
723 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
724 UNLOCK_AND_RETURN(eval);
725 if (semnum < 0 || semnum >= semaptr->sem_nsems)
726 UNLOCK_AND_RETURN(EINVAL);
727 rval = semaptr->sem_base[semnum].semval;
728 break;
729
730 case GETALL:
731 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
732 UNLOCK_AND_RETURN(eval);
733 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
734 UNLOCK_AND_RETURN(eval);*/
735 for (i = 0; i < semaptr->sem_nsems; i++) {
736 eval = copyout((caddr_t)&semaptr->sem_base[i].semval,
737 &arg.array[i], sizeof(arg.array[0]));
738 if (eval != 0)
739 break;
740 }
741 break;
742
743 case GETZCNT:
744 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
745 UNLOCK_AND_RETURN(eval);
746 if (semnum < 0 || semnum >= semaptr->sem_nsems)
747 UNLOCK_AND_RETURN(EINVAL);
748 rval = semaptr->sem_base[semnum].semzcnt;
749 break;
750
751 case SETVAL:
752 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
753 {
754 #ifdef SEM_DEBUG
755 printf("Invalid credentials for write\n");
756 #endif
757 UNLOCK_AND_RETURN(eval);
758 }
759 if (semnum < 0 || semnum >= semaptr->sem_nsems)
760 {
761 #ifdef SEM_DEBUG
762 printf("Invalid number out of range for set\n");
763 #endif
764 UNLOCK_AND_RETURN(EINVAL);
765 }
766 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
767 {
768 #ifdef SEM_DEBUG
769 printf("Error during value copyin\n");
770 #endif
771 UNLOCK_AND_RETURN(eval);
772 }*/
773 semaptr->sem_base[semnum].semval = arg.val;
774 semundo_clear(semid, semnum);
775 wakeup((caddr_t)semaptr);
776 break;
777
778 case SETALL:
779 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
780 UNLOCK_AND_RETURN(eval);
781 /*if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0)
782 UNLOCK_AND_RETURN(eval);*/
783 for (i = 0; i < semaptr->sem_nsems; i++) {
784 eval = copyin(&arg.array[i],
785 (caddr_t)&semaptr->sem_base[i].semval,
786 sizeof(arg.array[0]));
787 if (eval != 0)
788 break;
789 }
790 semundo_clear(semid, -1);
791 wakeup((caddr_t)semaptr);
792 break;
793
794 default:
795 UNLOCK_AND_RETURN(EINVAL);
796 }
797
798 if (eval == 0)
799 *retval = rval;
800 UNLOCK_AND_RETURN(eval);
801 }
802
803 #ifndef _SYS_SYSPROTO_H_
804 struct semget_args {
805 key_t key;
806 int nsems;
807 int semflg;
808 };
809 #endif
810
811 int
812 semget(p, uap, retval)
813 struct proc *p;
814 register struct semget_args *uap;
815 register_t *retval;
816 {
817 int semid, eval;
818 int key = uap->key;
819 int nsems = uap->nsems;
820 int semflg = uap->semflg;
821 struct ucred *cred = p->p_ucred;
822
823 SUBSYSTEM_LOCK_AQUIRE(p);
824 #ifdef SEM_DEBUG
825 if (key != IPC_PRIVATE)
826 printf("semget(0x%x, %d, 0%o)\n", key, nsems, semflg);
827 else
828 printf("semget(IPC_PRIVATE, %d, 0%o)\n", nsems, semflg);
829 #endif
830
831 if (key != IPC_PRIVATE) {
832 for (semid = 0; semid < seminfo.semmni; semid++) {
833 if ((sema[semid].sem_perm.mode & SEM_ALLOC) &&
834 sema[semid].sem_perm.key == key)
835 break;
836 }
837 if (semid < seminfo.semmni) {
838 #ifdef SEM_DEBUG
839 printf("found public key\n");
840 #endif
841 if ((eval = ipcperm(cred, &sema[semid].sem_perm,
842 semflg & 0700)))
843 UNLOCK_AND_RETURN(eval);
844 if (nsems > 0 && sema[semid].sem_nsems < nsems) {
845 #ifdef SEM_DEBUG
846 printf("too small\n");
847 #endif
848 UNLOCK_AND_RETURN(EINVAL);
849 }
850 if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
851 #ifdef SEM_DEBUG
852 printf("not exclusive\n");
853 #endif
854 UNLOCK_AND_RETURN(EEXIST);
855 }
856 goto found;
857 }
858 }
859
860 #ifdef SEM_DEBUG
861 printf("need to allocate an id for the request\n");
862 #endif
863 if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
864 if (nsems <= 0 || nsems > seminfo.semmsl) {
865 #ifdef SEM_DEBUG
866 printf("nsems out of range (0<%d<=%d)\n", nsems,
867 seminfo.semmsl);
868 #endif
869 UNLOCK_AND_RETURN(EINVAL);
870 }
871 if (nsems > seminfo.semmns - semtot) {
872 #ifdef SEM_DEBUG
873 printf("not enough semaphores left (need %d, got %d)\n",
874 nsems, seminfo.semmns - semtot);
875 #endif
876 if (!grow_sem_array(semtot + nsems))
877 {
878 #ifdef SEM_DEBUG
879 printf("failed to grow the sem array\n");
880 #endif
881 UNLOCK_AND_RETURN(ENOSPC);
882 }
883 }
884 for (semid = 0; semid < seminfo.semmni; semid++) {
885 if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0)
886 break;
887 }
888 if (semid == seminfo.semmni) {
889 #ifdef SEM_DEBUG
890 printf("no more id's available\n");
891 #endif
892 if (!grow_sema_array(seminfo.semmni + 1))
893 {
894 #ifdef SEM_DEBUG
895 printf("failed to grow sema array\n");
896 #endif
897 UNLOCK_AND_RETURN(ENOSPC);
898 }
899 }
900 #ifdef SEM_DEBUG
901 printf("semid %d is available\n", semid);
902 #endif
903 sema[semid].sem_perm.key = key;
904 sema[semid].sem_perm.cuid = cred->cr_uid;
905 sema[semid].sem_perm.uid = cred->cr_uid;
906 sema[semid].sem_perm.cgid = cred->cr_gid;
907 sema[semid].sem_perm.gid = cred->cr_gid;
908 sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC;
909 sema[semid].sem_perm.seq =
910 (sema[semid].sem_perm.seq + 1) & 0x7fff;
911 sema[semid].sem_nsems = nsems;
912 sema[semid].sem_otime = 0;
913 sema[semid].sem_ctime = time_second;
914 sema[semid].sem_base = &sem[semtot];
915 semtot += nsems;
916 bzero(sema[semid].sem_base,
917 sizeof(sema[semid].sem_base[0])*nsems);
918 #ifdef SEM_DEBUG
919 printf("sembase = 0x%x, next = 0x%x\n", sema[semid].sem_base,
920 &sem[semtot]);
921 #endif
922 } else {
923 #ifdef SEM_DEBUG
924 printf("didn't find it and wasn't asked to create it\n");
925 #endif
926 UNLOCK_AND_RETURN(ENOENT);
927 }
928
929 found:
930 *retval = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm);
931 #ifdef SEM_DEBUG
932 printf("semget is done, returning %d\n", *retval);
933 #endif
934 SUBSYSTEM_LOCK_RELEASE;
935 return(0);
936 }
937
938 #ifndef _SYS_SYSPROTO_H_
939 struct semop_args {
940 int semid;
941 struct sembuf *sops;
942 int nsops;
943 };
944 #endif
945
946 int
947 semop(p, uap, retval)
948 struct proc *p;
949 register struct semop_args *uap;
950 register_t *retval;
951 {
952 int semid = uap->semid;
953 int nsops = uap->nsops;
954 struct sembuf sops[MAX_SOPS];
955 register struct semid_ds *semaptr;
956 register struct sembuf *sopptr;
957 register struct sem *semptr;
958 struct sem_undo *suptr = NULL;
959 struct ucred *cred = p->p_ucred;
960 int i, j, eval;
961 int do_wakeup, do_undos;
962
963 SUBSYSTEM_LOCK_AQUIRE(p);
964 #ifdef SEM_DEBUG
965 printf("call to semop(%d, 0x%x, %d)\n", semid, sops, nsops);
966 #endif
967
968 semid = IPCID_TO_IX(semid); /* Convert back to zero origin */
969
970 if (semid < 0 || semid >= seminfo.semmsl)
971 UNLOCK_AND_RETURN(EINVAL);
972
973 semaptr = &sema[semid];
974 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0)
975 UNLOCK_AND_RETURN(EINVAL);
976 if (semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid))
977 UNLOCK_AND_RETURN(EINVAL);
978
979 if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
980 #ifdef SEM_DEBUG
981 printf("eval = %d from ipaccess\n", eval);
982 #endif
983 UNLOCK_AND_RETURN(eval);
984 }
985
986 if (nsops > MAX_SOPS) {
987 #ifdef SEM_DEBUG
988 printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS, nsops);
989 #endif
990 UNLOCK_AND_RETURN(E2BIG);
991 }
992
993 if ((eval = copyin(uap->sops, &sops, nsops * sizeof(sops[0]))) != 0) {
994 #ifdef SEM_DEBUG
995 printf("eval = %d from copyin(%08x, %08x, %ld)\n", eval,
996 uap->sops, &sops, nsops * sizeof(sops[0]));
997 #endif
998 UNLOCK_AND_RETURN(eval);
999 }
1000
1001 /*
1002 * Loop trying to satisfy the vector of requests.
1003 * If we reach a point where we must wait, any requests already
1004 * performed are rolled back and we go to sleep until some other
1005 * process wakes us up. At this point, we start all over again.
1006 *
1007 * This ensures that from the perspective of other tasks, a set
1008 * of requests is atomic (never partially satisfied).
1009 */
1010 do_undos = 0;
1011
1012 for (;;) {
1013 do_wakeup = 0;
1014
1015 for (i = 0; i < nsops; i++) {
1016 sopptr = &sops[i];
1017
1018 if (sopptr->sem_num >= semaptr->sem_nsems)
1019 UNLOCK_AND_RETURN(EFBIG);
1020
1021 semptr = &semaptr->sem_base[sopptr->sem_num];
1022
1023 #ifdef SEM_DEBUG
1024 printf("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
1025 semaptr, semaptr->sem_base, semptr,
1026 sopptr->sem_num, semptr->semval, sopptr->sem_op,
1027 (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait");
1028 #endif
1029
1030 if (sopptr->sem_op < 0) {
1031 if (semptr->semval + sopptr->sem_op < 0) {
1032 #ifdef SEM_DEBUG
1033 printf("semop: can't do it now\n");
1034 #endif
1035 break;
1036 } else {
1037 semptr->semval += sopptr->sem_op;
1038 if (semptr->semval == 0 &&
1039 semptr->semzcnt > 0)
1040 do_wakeup = 1;
1041 }
1042 if (sopptr->sem_flg & SEM_UNDO)
1043 do_undos = 1;
1044 } else if (sopptr->sem_op == 0) {
1045 if (semptr->semval > 0) {
1046 #ifdef SEM_DEBUG
1047 printf("semop: not zero now\n");
1048 #endif
1049 break;
1050 }
1051 } else {
1052 if (semptr->semncnt > 0)
1053 do_wakeup = 1;
1054 semptr->semval += sopptr->sem_op;
1055 if (sopptr->sem_flg & SEM_UNDO)
1056 do_undos = 1;
1057 }
1058 }
1059
1060 /*
1061 * Did we get through the entire vector?
1062 */
1063 if (i >= nsops)
1064 goto done;
1065
1066 /*
1067 * No ... rollback anything that we've already done
1068 */
1069 #ifdef SEM_DEBUG
1070 printf("semop: rollback 0 through %d\n", i-1);
1071 #endif
1072 for (j = 0; j < i; j++)
1073 semaptr->sem_base[sops[j].sem_num].semval -=
1074 sops[j].sem_op;
1075
1076 /*
1077 * If the request that we couldn't satisfy has the
1078 * NOWAIT flag set then return with EAGAIN.
1079 */
1080 if (sopptr->sem_flg & IPC_NOWAIT)
1081 UNLOCK_AND_RETURN(EAGAIN);
1082
1083 if (sopptr->sem_op == 0)
1084 semptr->semzcnt++;
1085 else
1086 semptr->semncnt++;
1087
1088 #ifdef SEM_DEBUG
1089 printf("semop: good night!\n");
1090 #endif
1091 /* Release our lock on the semaphore subsystem so
1092 * another thread can get at the semaphore we are
1093 * waiting for. We will get the lock back after we
1094 * wake up.
1095 */
1096 SUBSYSTEM_LOCK_RELEASE;
1097 sysv_sem_sleeping_threads++;
1098 eval = tsleep((caddr_t)semaptr, (PZERO - 4) | PCATCH,
1099 "semwait", 0);
1100 sysv_sem_sleeping_threads--;
1101
1102 #ifdef SEM_DEBUG
1103 printf("semop: good morning (eval=%d)!\n", eval);
1104 #endif
1105 /* There is no need to get the lock if we are just
1106 * going to return without performing more semaphore
1107 * operations.
1108 */
1109 if (eval != 0)
1110 return(EINTR);
1111
1112 SUBSYSTEM_LOCK_AQUIRE(p); /* Get it back */
1113 suptr = NULL; /* sem_undo may have been reallocated */
1114 semaptr = &sema[semid]; /* sema may have been reallocated */
1115
1116
1117 #ifdef SEM_DEBUG
1118 printf("semop: good morning!\n");
1119 #endif
1120
1121 /*
1122 * Make sure that the semaphore still exists
1123 */
1124 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 ||
1125 semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) {
1126 /* The man page says to return EIDRM. */
1127 /* Unfortunately, BSD doesn't define that code! */
1128 #ifdef EIDRM
1129 UNLOCK_AND_RETURN(EIDRM);
1130 #else
1131 UNLOCK_AND_RETURN(EINVAL);
1132 #endif
1133 }
1134
1135 /*
1136 * The semaphore is still alive. Readjust the count of
1137 * waiting processes. semptr needs to be recomputed
1138 * because the sem[] may have been reallocated while
1139 * we were sleeping, updating our sem_base pointer.
1140 */
1141 semptr = &semaptr->sem_base[sopptr->sem_num];
1142 if (sopptr->sem_op == 0)
1143 semptr->semzcnt--;
1144 else
1145 semptr->semncnt--;
1146 }
1147
1148 done:
1149 /*
1150 * Process any SEM_UNDO requests.
1151 */
1152 if (do_undos) {
1153 for (i = 0; i < nsops; i++) {
1154 /*
1155 * We only need to deal with SEM_UNDO's for non-zero
1156 * op's.
1157 */
1158 int adjval;
1159
1160 if ((sops[i].sem_flg & SEM_UNDO) == 0)
1161 continue;
1162 adjval = sops[i].sem_op;
1163 if (adjval == 0)
1164 continue;
1165 eval = semundo_adjust(p, &suptr, semid,
1166 sops[i].sem_num, -adjval);
1167 if (eval == 0)
1168 continue;
1169
1170 /*
1171 * Oh-Oh! We ran out of either sem_undo's or undo's.
1172 * Rollback the adjustments to this point and then
1173 * rollback the semaphore ups and down so we can return
1174 * with an error with all structures restored. We
1175 * rollback the undo's in the exact reverse order that
1176 * we applied them. This guarantees that we won't run
1177 * out of space as we roll things back out.
1178 */
1179 for (j = i - 1; j >= 0; j--) {
1180 if ((sops[j].sem_flg & SEM_UNDO) == 0)
1181 continue;
1182 adjval = sops[j].sem_op;
1183 if (adjval == 0)
1184 continue;
1185 if (semundo_adjust(p, &suptr, semid,
1186 sops[j].sem_num, adjval) != 0)
1187 panic("semop - can't undo undos");
1188 }
1189
1190 for (j = 0; j < nsops; j++)
1191 semaptr->sem_base[sops[j].sem_num].semval -=
1192 sops[j].sem_op;
1193
1194 #ifdef SEM_DEBUG
1195 printf("eval = %d from semundo_adjust\n", eval);
1196 #endif
1197 UNLOCK_AND_RETURN(eval);
1198 } /* loop through the sops */
1199 } /* if (do_undos) */
1200
1201 /* We're definitely done - set the sempid's */
1202 for (i = 0; i < nsops; i++) {
1203 sopptr = &sops[i];
1204 semptr = &semaptr->sem_base[sopptr->sem_num];
1205 semptr->sempid = p->p_pid;
1206 }
1207
1208 /* Do a wakeup if any semaphore was up'd.
1209 * we will release our lock on the semaphore subsystem before
1210 * we wakeup other processes to prevent a little thrashing.
1211 * Note that this is fine because we are done using the
1212 * semaphore structures at this point in time. We only use
1213 * a local variable pointer value, and the retval
1214 * parameter.
1215 * Note 2: Future use of sem_wakeup may reqiure the lock.
1216 */
1217 SUBSYSTEM_LOCK_RELEASE;
1218 if (do_wakeup) {
1219 #ifdef SEM_DEBUG
1220 printf("semop: doing wakeup\n");
1221 #ifdef SEM_WAKEUP
1222 sem_wakeup((caddr_t)semaptr);
1223 #else
1224 wakeup((caddr_t)semaptr);
1225 #endif
1226 printf("semop: back from wakeup\n");
1227 #else
1228 wakeup((caddr_t)semaptr);
1229 #endif
1230 }
1231 #ifdef SEM_DEBUG
1232 printf("semop: done\n");
1233 #endif
1234 *retval = 0;
1235 return(0);
1236 }
1237
1238 /*
1239 * Go through the undo structures for this process and apply the adjustments to
1240 * semaphores.
1241 */
1242 void
1243 semexit(p)
1244 struct proc *p;
1245 {
1246 register struct sem_undo *suptr;
1247 register struct sem_undo **supptr;
1248 int did_something;
1249
1250 /* If we have not allocated our semaphores yet there can't be
1251 * anything to undo, but we need the lock to prevent
1252 * dynamic memory race conditions.
1253 */
1254 SUBSYSTEM_LOCK_AQUIRE(p);
1255 if (!sem)
1256 {
1257 SUBSYSTEM_LOCK_RELEASE;
1258 return;
1259 }
1260 did_something = 0;
1261
1262 /*
1263 * Go through the chain of undo vectors looking for one
1264 * associated with this process.
1265 */
1266
1267 for (supptr = &semu_list; (suptr = *supptr) != NULL;
1268 supptr = &suptr->un_next) {
1269 if (suptr->un_proc == p)
1270 break;
1271 }
1272
1273 if (suptr == NULL)
1274 goto unlock;
1275
1276 #ifdef SEM_DEBUG
1277 printf("proc @%08x has undo structure with %d entries\n", p,
1278 suptr->un_cnt);
1279 #endif
1280
1281 /*
1282 * If there are any active undo elements then process them.
1283 */
1284 if (suptr->un_cnt > 0) {
1285 int ix;
1286
1287 for (ix = 0; ix < suptr->un_cnt; ix++) {
1288 int semid = suptr->un_ent[ix].un_id;
1289 int semnum = suptr->un_ent[ix].un_num;
1290 int adjval = suptr->un_ent[ix].un_adjval;
1291 struct semid_ds *semaptr;
1292
1293 semaptr = &sema[semid];
1294 if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0)
1295 panic("semexit - semid not allocated");
1296 if (semnum >= semaptr->sem_nsems)
1297 panic("semexit - semnum out of range");
1298
1299 #ifdef SEM_DEBUG
1300 printf("semexit: %08x id=%d num=%d(adj=%d) ; sem=%d\n",
1301 suptr->un_proc, suptr->un_ent[ix].un_id,
1302 suptr->un_ent[ix].un_num,
1303 suptr->un_ent[ix].un_adjval,
1304 semaptr->sem_base[semnum].semval);
1305 #endif
1306
1307 if (adjval < 0) {
1308 if (semaptr->sem_base[semnum].semval < -adjval)
1309 semaptr->sem_base[semnum].semval = 0;
1310 else
1311 semaptr->sem_base[semnum].semval +=
1312 adjval;
1313 } else
1314 semaptr->sem_base[semnum].semval += adjval;
1315
1316 /* Maybe we should build a list of semaptr's to wake
1317 * up, finish all access to data structures, release the
1318 * subsystem lock, and wake all the processes. Something
1319 * to think about. It wouldn't buy us anything unless
1320 * wakeup had the potential to block, or the syscall
1321 * funnel state was changed to allow multiple threads
1322 * in the BSD code at once.
1323 */
1324 #ifdef SEM_WAKEUP
1325 sem_wakeup((caddr_t)semaptr);
1326 #else
1327 wakeup((caddr_t)semaptr);
1328 #endif
1329 #ifdef SEM_DEBUG
1330 printf("semexit: back from wakeup\n");
1331 #endif
1332 }
1333 }
1334
1335 /*
1336 * Deallocate the undo vector.
1337 */
1338 #ifdef SEM_DEBUG
1339 printf("removing vector\n");
1340 #endif
1341 suptr->un_proc = NULL;
1342 *supptr = suptr->un_next;
1343
1344 unlock:
1345 /*
1346 * There is a semaphore leak (i.e. memory leak) in this code.
1347 * We should be deleting the IPC_PRIVATE semaphores when they are
1348 * no longer needed, and we dont. We would have to track which processes
1349 * know about which IPC_PRIVATE semaphores, updating the list after
1350 * every fork. We can't just delete them semaphore when the process
1351 * that created it dies, because that process may well have forked
1352 * some children. So we need to wait until all of it's children have
1353 * died, and so on. Maybe we should tag each IPC_PRIVATE sempahore
1354 * with the creating group ID, count the number of processes left in
1355 * that group, and delete the semaphore when the group is gone.
1356 * Until that code gets implemented we will leak IPC_PRIVATE semaphores.
1357 * There is an upper bound on the size of our semaphore array, so
1358 * leaking the semaphores should not work as a DOS attack.
1359 *
1360 * Please note that the original BSD code this file is based on had the
1361 * same leaky semaphore problem.
1362 */
1363
1364 SUBSYSTEM_LOCK_RELEASE;
1365 }
1366