File: | kern/sysv_sem.c |
Warning: | line 476, column 9 Although the value stored to 'semaptr' is used in the enclosing expression, the value is never actually read from 'semaptr' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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)struct { struct sem_undo *slh_first; } 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 |
61 | seminit(void) |
62 | { |
63 | |
64 | pool_init(&sema_pool, sizeof(struct semid_ds), 0, 0, PR_WAITOK0x0001, |
65 | "semapl", NULL((void *)0)); |
66 | pool_init(&semu_pool, SEMUSZ(sizeof(struct sem_undo)+sizeof(struct undo)*10), 0, 0, PR_WAITOK0x0001, "semupl", NULL((void *)0)); |
67 | sema = mallocarray(seminfo.semmni, sizeof(struct semid_ds *), |
68 | M_SEM31, M_WAITOK0x0001|M_ZERO0x0008); |
69 | semseqs = mallocarray(seminfo.semmni, sizeof(unsigned short), |
70 | M_SEM31, M_WAITOK0x0001|M_ZERO0x0008); |
71 | SLIST_INIT(&semu_list){ ((&semu_list)->slh_first) = ((void *)0); }; |
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 * |
79 | semu_alloc(struct process *pr) |
80 | { |
81 | struct sem_undo *suptr, *sutmp; |
82 | |
83 | if (semutot == seminfo.semmnu) |
84 | return (NULL((void *)0)); /* 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_NOWAIT0x0002)) == NULL((void *)0)) { |
93 | sutmp = pool_get(&semu_pool, PR_WAITOK0x0001); |
94 | SLIST_FOREACH(suptr, &semu_list, un_next)for((suptr) = ((&semu_list)->slh_first); (suptr) != (( void *)0); (suptr) = ((suptr)->un_next.sle_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)do { (suptr)->un_next.sle_next = (&semu_list)->slh_first ; (&semu_list)->slh_first = (suptr); } while (0); |
106 | return (suptr); |
107 | } |
108 | |
109 | /* |
110 | * Adjust a particular entry for a particular proc |
111 | */ |
112 | int |
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((void *)0)) { |
126 | SLIST_FOREACH(suptr, &semu_list, un_next)for((suptr) = ((&semu_list)->slh_first); (suptr) != (( void *)0); (suptr) = ((suptr)->un_next.sle_next)) { |
127 | if (suptr->un_proc == pr) { |
128 | *supptr = suptr; |
129 | break; |
130 | } |
131 | } |
132 | if (suptr == NULL((void *)0)) { |
133 | if (adjval == 0) |
134 | return (0); |
135 | suptr = semu_alloc(p->p_p); |
136 | if (suptr == NULL((void *)0)) |
137 | return (ENOSPC28); |
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((void *)0); |
159 | SLIST_REMOVE(&semu_list, suptr, sem_undo, un_next)do { if ((&semu_list)->slh_first == (suptr)) { do { (( &semu_list))->slh_first = ((&semu_list))->slh_first ->un_next.sle_next; } while (0); } else { struct sem_undo * curelm = (&semu_list)->slh_first; while (curelm->un_next .sle_next != (suptr)) curelm = curelm->un_next.sle_next; curelm ->un_next.sle_next = curelm->un_next.sle_next->un_next .sle_next; } ((suptr)->un_next.sle_next) = ((void *)-1); } while (0); |
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 == SEMUME10) |
172 | return (EINVAL22); |
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 |
183 | semundo_clear(int semid, int semnum) |
184 | { |
185 | struct sem_undo *suptr = SLIST_FIRST(&semu_list)((&semu_list)->slh_first); |
186 | struct sem_undo *suprev = NULL((void *)0); |
187 | struct undo *sunptr; |
188 | int i; |
189 | |
190 | while (suptr != NULL((void *)0)) { |
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)((&semu_list)->slh_first)) |
210 | SLIST_REMOVE_HEAD(&semu_list, un_next)do { (&semu_list)->slh_first = (&semu_list)->slh_first ->un_next.sle_next; } while (0); |
211 | else |
212 | SLIST_REMOVE_AFTER(suprev, un_next)do { (suprev)->un_next.sle_next = (suprev)->un_next.sle_next ->un_next.sle_next; } while (0); |
213 | suptr = SLIST_NEXT(suptr, un_next)((suptr)->un_next.sle_next); |
214 | pool_put(&semu_pool, sutmp); |
215 | semutot--; |
216 | } else { |
217 | suprev = suptr; |
218 | suptr = SLIST_NEXT(suptr, un_next)((suptr)->un_next.sle_next); |
219 | } |
220 | } |
221 | } |
222 | |
223 | int |
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)((uap)->semid.le.datum); |
234 | int semnum = SCARG(uap, semnum)((uap)->semnum.le.datum); |
235 | int cmd = SCARG(uap, cmd)((uap)->cmd.le.datum); |
236 | union semun arg, *uarg = SCARG(uap, arg)((uap)->arg.le.datum); |
237 | struct semid_ds sbuf; |
238 | struct semid_ds *semaptr; |
239 | unsigned short *semval = NULL((void *)0), nsems; |
240 | int i, ix, error; |
241 | |
242 | switch (cmd) { |
243 | case IPC_SET1: |
244 | case IPC_STAT2: |
245 | case GETALL6: |
246 | case SETVAL8: |
247 | case SETALL9: |
248 | if ((error = copyin(uarg, &arg, sizeof(union semun)))) |
249 | return (error); |
250 | } |
251 | if (cmd == IPC_SET1) |
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)((semid) & 0xffff); |
258 | if (ix < 0 || ix >= seminfo.semmni) |
259 | return (EINVAL22); |
260 | |
261 | again: |
262 | if ((semaptr = sema[ix]) == NULL((void *)0) || |
263 | semaptr->sem_perm.seq != IPCID_TO_SEQ(semid)(((semid) >> 16) & 0xffff)) |
264 | return (EINVAL22); |
265 | |
266 | switch (cmd) { |
267 | case IPC_RMID0: |
268 | if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M010000)) != 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_SEM31, |
274 | semaptr->sem_nsems * sizeof(struct sem)); |
275 | pool_put(&sema_pool, semaptr); |
276 | sema[ix] = NULL((void *)0); |
277 | semundo_clear(ix, -1); |
278 | wakeup(&sema[ix]); |
279 | break; |
280 | |
281 | case IPC_SET1: |
282 | if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M010000))) |
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_STAT2: |
292 | if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R000400))) |
293 | return (error); |
294 | memcpy(&sbuf, semaptr, sizeof sbuf)__builtin_memcpy((&sbuf), (semaptr), (sizeof sbuf)); |
295 | sbuf.sem_base = NULL((void *)0); |
296 | error = copyout(&sbuf, arg.buf, sizeof(struct semid_ds)); |
297 | break; |
298 | |
299 | case GETNCNT3: |
300 | if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R000400))) |
301 | return (error); |
302 | if (semnum < 0 || semnum >= semaptr->sem_nsems) |
303 | return (EINVAL22); |
304 | *retval = semaptr->sem_base[semnum].semncnt; |
305 | break; |
306 | |
307 | case GETPID4: |
308 | if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R000400))) |
309 | return (error); |
310 | if (semnum < 0 || semnum >= semaptr->sem_nsems) |
311 | return (EINVAL22); |
312 | *retval = semaptr->sem_base[semnum].sempid; |
313 | break; |
314 | |
315 | case GETVAL5: |
316 | if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R000400))) |
317 | return (error); |
318 | if (semnum < 0 || semnum >= semaptr->sem_nsems) |
319 | return (EINVAL22); |
320 | *retval = semaptr->sem_base[semnum].semval; |
321 | break; |
322 | |
323 | case GETALL6: |
324 | nsems = semaptr->sem_nsems; |
325 | semval = mallocarray(nsems, sizeof(arg.array[0]), |
326 | M_TEMP127, M_WAITOK0x0001); |
327 | if (semaptr != sema[ix] || |
328 | semaptr->sem_perm.seq != IPCID_TO_SEQ(semid)(((semid) >> 16) & 0xffff) || |
329 | semaptr->sem_nsems != nsems) { |
330 | free(semval, M_TEMP127, nsems * sizeof(arg.array[0])); |
331 | goto again; |
332 | } |
333 | if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R000400))) |
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 GETZCNT7: |
346 | if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R000400))) |
347 | return (error); |
348 | if (semnum < 0 || semnum >= semaptr->sem_nsems) |
349 | return (EINVAL22); |
350 | *retval = semaptr->sem_base[semnum].semzcnt; |
351 | break; |
352 | |
353 | case SETVAL8: |
354 | if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W000200))) |
355 | return (error); |
356 | if (semnum < 0 || semnum >= semaptr->sem_nsems) |
357 | return (EINVAL22); |
358 | if (arg.val > seminfo.semvmx) |
359 | return (ERANGE34); |
360 | semaptr->sem_base[semnum].semval = arg.val; |
361 | semundo_clear(ix, semnum); |
362 | wakeup(&sema[ix]); |
363 | break; |
364 | |
365 | case SETALL9: |
366 | nsems = semaptr->sem_nsems; |
367 | semval = mallocarray(nsems, sizeof(arg.array[0]), |
368 | M_TEMP127, M_WAITOK0x0001); |
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 = ERANGE34; |
376 | goto error; |
377 | } |
378 | } |
379 | if (semaptr != sema[ix] || |
380 | semaptr->sem_perm.seq != IPCID_TO_SEQ(semid)(((semid) >> 16) & 0xffff) || |
381 | semaptr->sem_nsems != nsems) { |
382 | free(semval, M_TEMP127, nsems * sizeof(arg.array[0])); |
383 | goto again; |
384 | } |
385 | if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W000200))) |
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 (EINVAL22); |
395 | } |
396 | |
397 | error: |
398 | free(semval, M_TEMP127, nsems * sizeof(arg.array[0])); |
399 | |
400 | return (error); |
401 | } |
402 | |
403 | int |
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)((uap)->key.le.datum); |
413 | int nsems = SCARG(uap, nsems)((uap)->nsems.le.datum); |
414 | int semflg = SCARG(uap, semflg)((uap)->semflg.le.datum); |
415 | struct semid_ds *semaptr, *semaptr_new = NULL((void *)0); |
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(key_t)0 || (semflg & IPC_CREAT001000)) { |
426 | if (nsems <= 0 || nsems > seminfo.semmsl) { |
427 | DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems, |
428 | seminfo.semmsl)); |
429 | return (EINVAL22); |
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 (ENOSPC28); |
435 | } |
436 | semaptr_new = pool_get(&sema_pool, PR_WAITOK0x0001 | PR_ZERO0x0008); |
437 | semaptr_new->sem_base = mallocarray(nsems, sizeof(struct sem), |
438 | M_SEM31, M_WAITOK0x0001|M_ZERO0x0008); |
439 | if (nsems > seminfo.semmns - semtot) { |
440 | error = ENOSPC28; |
441 | goto error; |
442 | } |
443 | } |
444 | |
445 | if (key != IPC_PRIVATE(key_t)0) { |
446 | for (semid = 0, semaptr = NULL((void *)0); semid < seminfo.semmni; semid++) { |
447 | if ((semaptr = sema[semid]) != NULL((void *)0) && |
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 = EINVAL22; |
456 | goto error; |
457 | } |
458 | if ((semflg & IPC_CREAT001000) && (semflg & IPC_EXCL002000)) { |
459 | DPRINTF(("not exclusive\n")); |
460 | error = EEXIST17; |
461 | goto error; |
462 | } |
463 | if (semaptr_new != NULL((void *)0)) { |
464 | free(semaptr_new->sem_base, M_SEM31, |
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(key_t)0 || (semflg & IPC_CREAT001000)) { |
475 | for (semid = 0; semid < seminfo.semmni; semid++) { |
476 | if ((semaptr = sema[semid]) == NULL((void *)0)) |
Although the value stored to 'semaptr' is used in the enclosing expression, the value is never actually read from 'semaptr' | |
477 | break; |
478 | } |
479 | if (semid == seminfo.semmni) { |
480 | DPRINTF(("no more semid_ds's available\n")); |
481 | error = ENOSPC28; |
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 (ENOENT2); |
501 | } |
502 | |
503 | found: |
504 | *retval = IXSEQ_TO_IPCID(semid, sema[semid]->sem_perm)(((sema[semid]->sem_perm.seq) << 16) | (semid & 0xffff )); |
505 | return (0); |
506 | error: |
507 | if (semaptr_new != NULL((void *)0)) { |
508 | free(semaptr_new->sem_base, M_SEM31, nsems * sizeof(struct sem)); |
509 | pool_put(&sema_pool, semaptr_new); |
510 | } |
511 | return (error); |
512 | } |
513 | |
514 | int |
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 NSOPS8 8 |
523 | struct sembuf sopbuf[NSOPS8]; |
524 | int semid = SCARG(uap, semid)((uap)->semid.le.datum); |
525 | size_t nsops = SCARG(uap, nsops)((uap)->nsops.le.datum); |
526 | struct sembuf *sops; |
527 | struct semid_ds *semaptr; |
528 | struct sembuf *sopptr = NULL((void *)0); |
529 | struct sem *semptr = NULL((void *)0); |
530 | struct sem_undo *suptr = NULL((void *)0); |
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)((semid) & 0xffff); /* Convert back to zero origin */ |
539 | |
540 | if (semid < 0 || semid >= seminfo.semmni) |
541 | return (EINVAL22); |
542 | |
543 | if ((semaptr = sema[semid]) == NULL((void *)0) || |
544 | semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))(((((uap)->semid.le.datum)) >> 16) & 0xffff)) |
545 | return (EINVAL22); |
546 | |
547 | if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W000200))) { |
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 (E2BIG7); |
559 | } |
560 | |
561 | if (nsops <= NSOPS8) |
562 | sops = sopbuf; |
563 | else |
564 | sops = mallocarray(nsops, sizeof(struct sembuf), M_SEM31, M_WAITOK0x0001); |
565 | error = copyin(SCARG(uap, sops)((uap)->sops.le.datum), 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 = EFBIG27; |
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_UNDO010000) |
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_UNDO010000) |
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 <= SEMUME10) |
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 > SEMUME10) { |
646 | error = ENOSPC28; |
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_NOWAIT004000) { |
655 | error = EAGAIN35; |
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], PLOCK36 | PCATCH0x100, |
666 | "semwait", INFSLP0xffffffffffffffffULL); |
667 | DPRINTF(("semop: good morning (error=%d)!\n", error)); |
668 | |
669 | suptr = NULL((void *)0); /* sem_undo may have been reallocated */ |
670 | |
671 | /* |
672 | * Make sure that the semaphore still exists |
673 | */ |
674 | if (sema[semid] == NULL((void *)0) || |
675 | semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))(((((uap)->semid.le.datum)) >> 16) & 0xffff)) { |
676 | error = EIDRM89; |
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 = EINTR4; |
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_UNDO010000) == 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_UNDO010000) == 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_SEM31, 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 |
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)((&semu_list)->slh_first); |
792 | SLIST_FOREACH(suptr, &semu_list, un_next)for((suptr) = ((&semu_list)->slh_first); (suptr) != (( void *)0); (suptr) = ((suptr)->un_next.sle_next)) { |
793 | if (suptr->un_proc == pr) |
794 | break; |
795 | supptr = &SLIST_NEXT(suptr, un_next)((suptr)->un_next.sle_next); |
796 | } |
797 | |
798 | /* |
799 | * If there is no undo vector, skip to the end. |
800 | */ |
801 | if (suptr == NULL((void *)0)) |
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((void *)0)) |
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)((suptr)->un_next.sle_next); |
849 | pool_put(&semu_pool, suptr); |
850 | semutot--; |
851 | } |
852 | |
853 | /* Expand semsegs and semseqs arrays */ |
854 | void |
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_SEM31, M_WAITOK0x0001|M_ZERO0x0008); |
861 | memcpy(sema_new, sema,__builtin_memcpy((sema_new), (sema), (seminfo.semmni * sizeof (struct semid_ds *))) |
862 | seminfo.semmni * sizeof(struct semid_ds *))__builtin_memcpy((sema_new), (sema), (seminfo.semmni * sizeof (struct semid_ds *))); |
863 | newseqs = mallocarray(val, sizeof(unsigned short), M_SEM31, |
864 | M_WAITOK0x0001|M_ZERO0x0008); |
865 | memcpy(newseqs, semseqs,__builtin_memcpy((newseqs), (semseqs), (seminfo.semmni * sizeof (unsigned short))) |
866 | seminfo.semmni * sizeof(unsigned short))__builtin_memcpy((newseqs), (semseqs), (seminfo.semmni * sizeof (unsigned short))); |
867 | free(sema, M_SEM31, seminfo.semmni * sizeof(struct semid_ds *)); |
868 | free(semseqs, M_SEM31, 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_SEMUME6, &seminfo.semume, SYSCTL_INT_READONLY1,0 }, |
876 | { KERN_SEMINFO_SEMUSZ7, &seminfo.semusz, SYSCTL_INT_READONLY1,0 }, |
877 | { KERN_SEMINFO_SEMVMX8, &seminfo.semvmx, SYSCTL_INT_READONLY1,0 }, |
878 | { KERN_SEMINFO_SEMAEM9, &seminfo.semaem, SYSCTL_INT_READONLY1,0 }, |
879 | { KERN_SEMINFO_SEMOPM5, &seminfo.semopm, 1, INT_MAX0x7fffffff }, |
880 | }; |
881 | |
882 | /* |
883 | * Userland access to struct seminfo. |
884 | */ |
885 | int |
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 (ENOTDIR20); /* leaf-only */ |
893 | |
894 | switch (name[0]) { |
895 | case KERN_SEMINFO_SEMMNI1: |
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_SEMMNS2: |
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_SEMMNU3: |
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_SEMMSL4: |
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)(sizeof((sysvsem_vars)) / sizeof((sysvsem_vars)[0])), |
918 | name, namelen, oldp, oldlenp, newp, newlen)); |
919 | } |
920 | /* NOTREACHED */ |
921 | } |