1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69#include <linux/slab.h>
70#include <linux/spinlock.h>
71#include <linux/init.h>
72#include <linux/proc_fs.h>
73#include <linux/time.h>
74#include <linux/smp_lock.h>
75#include <linux/security.h>
76#include <linux/syscalls.h>
77#include <linux/audit.h>
78#include <linux/capability.h>
79#include <linux/seq_file.h>
80#include <linux/mutex.h>
81
82#include <asm/uaccess.h>
83#include "util.h"
84
85
86#define sem_lock(id) ((struct sem_array*)ipc_lock(&sem_ids,id))
87#define sem_unlock(sma) ipc_unlock(&(sma)->sem_perm)
88#define sem_rmid(id) ((struct sem_array*)ipc_rmid(&sem_ids,id))
89#define sem_checkid(sma, semid) \
90 ipc_checkid(&sem_ids,&sma->sem_perm,semid)
91#define sem_buildid(id, seq) \
92 ipc_buildid(&sem_ids, id, seq)
93static struct ipc_ids sem_ids;
94
95static int newary (key_t, int, int);
96static void freeary (struct sem_array *sma, int id);
97#ifdef CONFIG_PROC_FS
98static int sysvipc_sem_proc_show(struct seq_file *s, void *it);
99#endif
100
101#define SEMMSL_FAST 256
102#define SEMOPM_FAST 64
103
104
105
106
107
108
109
110
111
112
113int sem_ctls[4] = {SEMMSL, SEMMNS, SEMOPM, SEMMNI};
114#define sc_semmsl (sem_ctls[0])
115#define sc_semmns (sem_ctls[1])
116#define sc_semopm (sem_ctls[2])
117#define sc_semmni (sem_ctls[3])
118
119static int used_sems;
120
121void __init sem_init (void)
122{
123 used_sems = 0;
124 ipc_init_ids(&sem_ids,sc_semmni);
125 ipc_init_proc_interface("sysvipc/sem",
126 " key semid perms nsems uid gid cuid cgid otime ctime\n",
127 &sem_ids,
128 sysvipc_sem_proc_show);
129}
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163#define IN_WAKEUP 1
164
165static int newary (key_t key, int nsems, int semflg)
166{
167 int id;
168 int retval;
169 struct sem_array *sma;
170 int size;
171
172 if (!nsems)
173 return -EINVAL;
174 if (used_sems + nsems > sc_semmns)
175 return -ENOSPC;
176
177 size = sizeof (*sma) + nsems * sizeof (struct sem);
178 sma = ipc_rcu_alloc(size);
179 if (!sma) {
180 return -ENOMEM;
181 }
182 memset (sma, 0, size);
183
184 sma->sem_perm.mode = (semflg & S_IRWXUGO);
185 sma->sem_perm.key = key;
186
187 sma->sem_perm.security = NULL;
188 retval = security_sem_alloc(sma);
189 if (retval) {
190 ipc_rcu_putref(sma);
191 return retval;
192 }
193
194 id = ipc_addid(&sem_ids, &sma->sem_perm, sc_semmni);
195 if(id == -1) {
196 security_sem_free(sma);
197 ipc_rcu_putref(sma);
198 return -ENOSPC;
199 }
200 used_sems += nsems;
201
202 sma->sem_id = sem_buildid(id, sma->sem_perm.seq);
203 sma->sem_base = (struct sem *) &sma[1];
204
205 sma->sem_pending_last = &sma->sem_pending;
206
207 sma->sem_nsems = nsems;
208 sma->sem_ctime = get_seconds();
209 sem_unlock(sma);
210
211 return sma->sem_id;
212}
213
214asmlinkage long sys_semget (key_t key, int nsems, int semflg)
215{
216 int id, err = -EINVAL;
217 struct sem_array *sma;
218
219 if (nsems < 0 || nsems > sc_semmsl)
220 return -EINVAL;
221 mutex_lock(&sem_ids.mutex);
222
223 if (key == IPC_PRIVATE) {
224 err = newary(key, nsems, semflg);
225 } else if ((id = ipc_findkey(&sem_ids, key)) == -1) {
226 if (!(semflg & IPC_CREAT))
227 err = -ENOENT;
228 else
229 err = newary(key, nsems, semflg);
230 } else if (semflg & IPC_CREAT && semflg & IPC_EXCL) {
231 err = -EEXIST;
232 } else {
233 sma = sem_lock(id);
234 BUG_ON(sma==NULL);
235 if (nsems > sma->sem_nsems)
236 err = -EINVAL;
237 else if (ipcperms(&sma->sem_perm, semflg))
238 err = -EACCES;
239 else {
240 int semid = sem_buildid(id, sma->sem_perm.seq);
241 err = security_sem_associate(sma, semflg);
242 if (!err)
243 err = semid;
244 }
245 sem_unlock(sma);
246 }
247
248 mutex_unlock(&sem_ids.mutex);
249 return err;
250}
251
252
253
254
255static inline void append_to_queue (struct sem_array * sma,
256 struct sem_queue * q)
257{
258 *(q->prev = sma->sem_pending_last) = q;
259 *(sma->sem_pending_last = &q->next) = NULL;
260}
261
262static inline void prepend_to_queue (struct sem_array * sma,
263 struct sem_queue * q)
264{
265 q->next = sma->sem_pending;
266 *(q->prev = &sma->sem_pending) = q;
267 if (q->next)
268 q->next->prev = &q->next;
269 else
270 sma->sem_pending_last = &q->next;
271}
272
273static inline void remove_from_queue (struct sem_array * sma,
274 struct sem_queue * q)
275{
276 *(q->prev) = q->next;
277 if (q->next)
278 q->next->prev = q->prev;
279 else
280 sma->sem_pending_last = q->prev;
281 q->prev = NULL;
282}
283
284
285
286
287
288
289static int try_atomic_semop (struct sem_array * sma, struct sembuf * sops,
290 int nsops, struct sem_undo *un, int pid)
291{
292 int result, sem_op;
293 struct sembuf *sop;
294 struct sem * curr;
295
296 for (sop = sops; sop < sops + nsops; sop++) {
297 curr = sma->sem_base + sop->sem_num;
298 sem_op = sop->sem_op;
299 result = curr->semval;
300
301 if (!sem_op && result)
302 goto would_block;
303
304 result += sem_op;
305 if (result < 0)
306 goto would_block;
307 if (result > SEMVMX)
308 goto out_of_range;
309 if (sop->sem_flg & SEM_UNDO) {
310 int undo = un->semadj[sop->sem_num] - sem_op;
311
312
313
314 if (undo < (-SEMAEM - 1) || undo > SEMAEM)
315 goto out_of_range;
316 }
317 curr->semval = result;
318 }
319
320 sop--;
321 while (sop >= sops) {
322 sma->sem_base[sop->sem_num].sempid = pid;
323 if (sop->sem_flg & SEM_UNDO)
324 un->semadj[sop->sem_num] -= sop->sem_op;
325 sop--;
326 }
327
328 sma->sem_otime = get_seconds();
329 return 0;
330
331out_of_range:
332 result = -ERANGE;
333 goto undo;
334
335would_block:
336 if (sop->sem_flg & IPC_NOWAIT)
337 result = -EAGAIN;
338 else
339 result = 1;
340
341undo:
342 sop--;
343 while (sop >= sops) {
344 sma->sem_base[sop->sem_num].semval -= sop->sem_op;
345 sop--;
346 }
347
348 return result;
349}
350
351
352
353
354static void update_queue (struct sem_array * sma)
355{
356 int error;
357 struct sem_queue * q;
358
359 q = sma->sem_pending;
360 while(q) {
361 error = try_atomic_semop(sma, q->sops, q->nsops,
362 q->undo, q->pid);
363
364
365 if (error <= 0) {
366 struct sem_queue *n;
367 remove_from_queue(sma,q);
368 q->status = IN_WAKEUP;
369
370
371
372
373
374
375
376
377
378
379
380 if (q->alter)
381 n = sma->sem_pending;
382 else
383 n = q->next;
384 wake_up_process(q->sleeper);
385
386
387
388 smp_wmb();
389 q->status = error;
390 q = n;
391 } else {
392 q = q->next;
393 }
394 }
395}
396
397
398
399
400
401
402
403
404
405
406static int count_semncnt (struct sem_array * sma, ushort semnum)
407{
408 int semncnt;
409 struct sem_queue * q;
410
411 semncnt = 0;
412 for (q = sma->sem_pending; q; q = q->next) {
413 struct sembuf * sops = q->sops;
414 int nsops = q->nsops;
415 int i;
416 for (i = 0; i < nsops; i++)
417 if (sops[i].sem_num == semnum
418 && (sops[i].sem_op < 0)
419 && !(sops[i].sem_flg & IPC_NOWAIT))
420 semncnt++;
421 }
422 return semncnt;
423}
424static int count_semzcnt (struct sem_array * sma, ushort semnum)
425{
426 int semzcnt;
427 struct sem_queue * q;
428
429 semzcnt = 0;
430 for (q = sma->sem_pending; q; q = q->next) {
431 struct sembuf * sops = q->sops;
432 int nsops = q->nsops;
433 int i;
434 for (i = 0; i < nsops; i++)
435 if (sops[i].sem_num == semnum
436 && (sops[i].sem_op == 0)
437 && !(sops[i].sem_flg & IPC_NOWAIT))
438 semzcnt++;
439 }
440 return semzcnt;
441}
442
443
444
445
446
447static void freeary (struct sem_array *sma, int id)
448{
449 struct sem_undo *un;
450 struct sem_queue *q;
451 int size;
452
453
454
455
456
457 for (un = sma->undo; un; un = un->id_next)
458 un->semid = -1;
459
460
461 q = sma->sem_pending;
462 while(q) {
463 struct sem_queue *n;
464
465 q->prev = NULL;
466 n = q->next;
467 q->status = IN_WAKEUP;
468 wake_up_process(q->sleeper);
469 smp_wmb();
470 q->status = -EIDRM;
471 q = n;
472 }
473
474
475 sma = sem_rmid(id);
476 sem_unlock(sma);
477
478 used_sems -= sma->sem_nsems;
479 size = sizeof (*sma) + sma->sem_nsems * sizeof (struct sem);
480 security_sem_free(sma);
481 ipc_rcu_putref(sma);
482}
483
484static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, int version)
485{
486 switch(version) {
487 case IPC_64:
488 return copy_to_user(buf, in, sizeof(*in));
489 case IPC_OLD:
490 {
491 struct semid_ds out;
492
493 ipc64_perm_to_ipc_perm(&in->sem_perm, &out.sem_perm);
494
495 out.sem_otime = in->sem_otime;
496 out.sem_ctime = in->sem_ctime;
497 out.sem_nsems = in->sem_nsems;
498
499 return copy_to_user(buf, &out, sizeof(out));
500 }
501 default:
502 return -EINVAL;
503 }
504}
505
506static int semctl_nolock(int semid, int semnum, int cmd, int version, union semun arg)
507{
508 int err = -EINVAL;
509 struct sem_array *sma;
510
511 switch(cmd) {
512 case IPC_INFO:
513 case SEM_INFO:
514 {
515 struct seminfo seminfo;
516 int max_id;
517
518 err = security_sem_semctl(NULL, cmd);
519 if (err)
520 return err;
521
522 memset(&seminfo,0,sizeof(seminfo));
523 seminfo.semmni = sc_semmni;
524 seminfo.semmns = sc_semmns;
525 seminfo.semmsl = sc_semmsl;
526 seminfo.semopm = sc_semopm;
527 seminfo.semvmx = SEMVMX;
528 seminfo.semmnu = SEMMNU;
529 seminfo.semmap = SEMMAP;
530 seminfo.semume = SEMUME;
531 mutex_lock(&sem_ids.mutex);
532 if (cmd == SEM_INFO) {
533 seminfo.semusz = sem_ids.in_use;
534 seminfo.semaem = used_sems;
535 } else {
536 seminfo.semusz = SEMUSZ;
537 seminfo.semaem = SEMAEM;
538 }
539 max_id = sem_ids.max_id;
540 mutex_unlock(&sem_ids.mutex);
541 if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo)))
542 return -EFAULT;
543 return (max_id < 0) ? 0: max_id;
544 }
545 case SEM_STAT:
546 {
547 struct semid64_ds tbuf;
548 int id;
549
550 if(semid >= sem_ids.entries->size)
551 return -EINVAL;
552
553 memset(&tbuf,0,sizeof(tbuf));
554
555 sma = sem_lock(semid);
556 if(sma == NULL)
557 return -EINVAL;
558
559 err = -EACCES;
560 if (ipcperms (&sma->sem_perm, S_IRUGO))
561 goto out_unlock;
562
563 err = security_sem_semctl(sma, cmd);
564 if (err)
565 goto out_unlock;
566
567 id = sem_buildid(semid, sma->sem_perm.seq);
568
569 kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
570 tbuf.sem_otime = sma->sem_otime;
571 tbuf.sem_ctime = sma->sem_ctime;
572 tbuf.sem_nsems = sma->sem_nsems;
573 sem_unlock(sma);
574 if (copy_semid_to_user (arg.buf, &tbuf, version))
575 return -EFAULT;
576 return id;
577 }
578 default:
579 return -EINVAL;
580 }
581 return err;
582out_unlock:
583 sem_unlock(sma);
584 return err;
585}
586
587static int semctl_main(int semid, int semnum, int cmd, int version, union semun arg)
588{
589 struct sem_array *sma;
590 struct sem* curr;
591 int err;
592 ushort fast_sem_io[SEMMSL_FAST];
593 ushort* sem_io = fast_sem_io;
594 int nsems;
595
596 sma = sem_lock(semid);
597 if(sma==NULL)
598 return -EINVAL;
599
600 nsems = sma->sem_nsems;
601
602 err=-EIDRM;
603 if (sem_checkid(sma,semid))
604 goto out_unlock;
605
606 err = -EACCES;
607 if (ipcperms (&sma->sem_perm, (cmd==SETVAL||cmd==SETALL)?S_IWUGO:S_IRUGO))
608 goto out_unlock;
609
610 err = security_sem_semctl(sma, cmd);
611 if (err)
612 goto out_unlock;
613
614 err = -EACCES;
615 switch (cmd) {
616 case GETALL:
617 {
618 ushort __user *array = arg.array;
619 int i;
620
621 if(nsems > SEMMSL_FAST) {
622 ipc_rcu_getref(sma);
623 sem_unlock(sma);
624
625 sem_io = ipc_alloc(sizeof(ushort)*nsems);
626 if(sem_io == NULL) {
627 ipc_lock_by_ptr(&sma->sem_perm);
628 ipc_rcu_putref(sma);
629 sem_unlock(sma);
630 return -ENOMEM;
631 }
632
633 ipc_lock_by_ptr(&sma->sem_perm);
634 ipc_rcu_putref(sma);
635 if (sma->sem_perm.deleted) {
636 sem_unlock(sma);
637 err = -EIDRM;
638 goto out_free;
639 }
640 }
641
642 for (i = 0; i < sma->sem_nsems; i++)
643 sem_io[i] = sma->sem_base[i].semval;
644 sem_unlock(sma);
645 err = 0;
646 if(copy_to_user(array, sem_io, nsems*sizeof(ushort)))
647 err = -EFAULT;
648 goto out_free;
649 }
650 case SETALL:
651 {
652 int i;
653 struct sem_undo *un;
654
655 ipc_rcu_getref(sma);
656 sem_unlock(sma);
657
658 if(nsems > SEMMSL_FAST) {
659 sem_io = ipc_alloc(sizeof(ushort)*nsems);
660 if(sem_io == NULL) {
661 ipc_lock_by_ptr(&sma->sem_perm);
662 ipc_rcu_putref(sma);
663 sem_unlock(sma);
664 return -ENOMEM;
665 }
666 }
667
668 if (copy_from_user (sem_io, arg.array, nsems*sizeof(ushort))) {
669 ipc_lock_by_ptr(&sma->sem_perm);
670 ipc_rcu_putref(sma);
671 sem_unlock(sma);
672 err = -EFAULT;
673 goto out_free;
674 }
675
676 for (i = 0; i < nsems; i++) {
677 if (sem_io[i] > SEMVMX) {
678 ipc_lock_by_ptr(&sma->sem_perm);
679 ipc_rcu_putref(sma);
680 sem_unlock(sma);
681 err = -ERANGE;
682 goto out_free;
683 }
684 }
685 ipc_lock_by_ptr(&sma->sem_perm);
686 ipc_rcu_putref(sma);
687 if (sma->sem_perm.deleted) {
688 sem_unlock(sma);
689 err = -EIDRM;
690 goto out_free;
691 }
692
693 for (i = 0; i < nsems; i++)
694 sma->sem_base[i].semval = sem_io[i];
695 for (un = sma->undo; un; un = un->id_next)
696 for (i = 0; i < nsems; i++)
697 un->semadj[i] = 0;
698 sma->sem_ctime = get_seconds();
699
700 update_queue(sma);
701 err = 0;
702 goto out_unlock;
703 }
704 case IPC_STAT:
705 {
706 struct semid64_ds tbuf;
707 memset(&tbuf,0,sizeof(tbuf));
708 kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
709 tbuf.sem_otime = sma->sem_otime;
710 tbuf.sem_ctime = sma->sem_ctime;
711 tbuf.sem_nsems = sma->sem_nsems;
712 sem_unlock(sma);
713 if (copy_semid_to_user (arg.buf, &tbuf, version))
714 return -EFAULT;
715 return 0;
716 }
717
718 }
719 err = -EINVAL;
720 if(semnum < 0 || semnum >= nsems)
721 goto out_unlock;
722
723 curr = &sma->sem_base[semnum];
724
725 switch (cmd) {
726 case GETVAL:
727 err = curr->semval;
728 goto out_unlock;
729 case GETPID:
730 err = curr->sempid;
731 goto out_unlock;
732 case GETNCNT:
733 err = count_semncnt(sma,semnum);
734 goto out_unlock;
735 case GETZCNT:
736 err = count_semzcnt(sma,semnum);
737 goto out_unlock;
738 case SETVAL:
739 {
740 int val = arg.val;
741 struct sem_undo *un;
742 err = -ERANGE;
743 if (val > SEMVMX || val < 0)
744 goto out_unlock;
745
746 for (un = sma->undo; un; un = un->id_next)
747 un->semadj[semnum] = 0;
748 curr->semval = val;
749 curr->sempid = current->tgid;
750 sma->sem_ctime = get_seconds();
751
752 update_queue(sma);
753 err = 0;
754 goto out_unlock;
755 }
756 }
757out_unlock:
758 sem_unlock(sma);
759out_free:
760 if(sem_io != fast_sem_io)
761 ipc_free(sem_io, sizeof(ushort)*nsems);
762 return err;
763}
764
765struct sem_setbuf {
766 uid_t uid;
767 gid_t gid;
768 mode_t mode;
769};
770
771static inline unsigned long copy_semid_from_user(struct sem_setbuf *out, void __user *buf, int version)
772{
773 switch(version) {
774 case IPC_64:
775 {
776 struct semid64_ds tbuf;
777
778 if(copy_from_user(&tbuf, buf, sizeof(tbuf)))
779 return -EFAULT;
780
781 out->uid = tbuf.sem_perm.uid;
782 out->gid = tbuf.sem_perm.gid;
783 out->mode = tbuf.sem_perm.mode;
784
785 return 0;
786 }
787 case IPC_OLD:
788 {
789 struct semid_ds tbuf_old;
790
791 if(copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
792 return -EFAULT;
793
794 out->uid = tbuf_old.sem_perm.uid;
795 out->gid = tbuf_old.sem_perm.gid;
796 out->mode = tbuf_old.sem_perm.mode;
797
798 return 0;
799 }
800 default:
801 return -EINVAL;
802 }
803}
804
805static int semctl_down(int semid, int semnum, int cmd, int version, union semun arg)
806{
807 struct sem_array *sma;
808 int err;
809 struct sem_setbuf setbuf;
810 struct kern_ipc_perm *ipcp;
811
812 if(cmd == IPC_SET) {
813 if(copy_semid_from_user (&setbuf, arg.buf, version))
814 return -EFAULT;
815 }
816 sma = sem_lock(semid);
817 if(sma==NULL)
818 return -EINVAL;
819
820 if (sem_checkid(sma,semid)) {
821 err=-EIDRM;
822 goto out_unlock;
823 }
824 ipcp = &sma->sem_perm;
825
826 err = audit_ipc_obj(ipcp);
827 if (err)
828 goto out_unlock;
829
830 if (cmd == IPC_SET) {
831 err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode);
832 if (err)
833 goto out_unlock;
834 }
835 if (current->euid != ipcp->cuid &&
836 current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
837 err=-EPERM;
838 goto out_unlock;
839 }
840
841 err = security_sem_semctl(sma, cmd);
842 if (err)
843 goto out_unlock;
844
845 switch(cmd){
846 case IPC_RMID:
847 freeary(sma, semid);
848 err = 0;
849 break;
850 case IPC_SET:
851 ipcp->uid = setbuf.uid;
852 ipcp->gid = setbuf.gid;
853 ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
854 | (setbuf.mode & S_IRWXUGO);
855 sma->sem_ctime = get_seconds();
856 sem_unlock(sma);
857 err = 0;
858 break;
859 default:
860 sem_unlock(sma);
861 err = -EINVAL;
862 break;
863 }
864 return err;
865
866out_unlock:
867 sem_unlock(sma);
868 return err;
869}
870
871asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
872{
873 int err = -EINVAL;
874 int version;
875
876 if (semid < 0)
877 return -EINVAL;
878
879 version = ipc_parse_version(&cmd);
880
881 switch(cmd) {
882 case IPC_INFO:
883 case SEM_INFO:
884 case SEM_STAT:
885 err = semctl_nolock(semid,semnum,cmd,version,arg);
886 return err;
887 case GETALL:
888 case GETVAL:
889 case GETPID:
890 case GETNCNT:
891 case GETZCNT:
892 case IPC_STAT:
893 case SETVAL:
894 case SETALL:
895 err = semctl_main(semid,semnum,cmd,version,arg);
896 return err;
897 case IPC_RMID:
898 case IPC_SET:
899 mutex_lock(&sem_ids.mutex);
900 err = semctl_down(semid,semnum,cmd,version,arg);
901 mutex_unlock(&sem_ids.mutex);
902 return err;
903 default:
904 return -EINVAL;
905 }
906}
907
908static inline void lock_semundo(void)
909{
910 struct sem_undo_list *undo_list;
911
912 undo_list = current->sysvsem.undo_list;
913 if (undo_list)
914 spin_lock(&undo_list->lock);
915}
916
917
918
919
920
921
922
923
924
925
926
927
928static inline void unlock_semundo(void)
929{
930 struct sem_undo_list *undo_list;
931
932 undo_list = current->sysvsem.undo_list;
933 if (undo_list)
934 spin_unlock(&undo_list->lock);
935}
936
937
938
939
940
941
942
943
944
945
946
947
948
949static inline int get_undo_list(struct sem_undo_list **undo_listp)
950{
951 struct sem_undo_list *undo_list;
952 int size;
953
954 undo_list = current->sysvsem.undo_list;
955 if (!undo_list) {
956 size = sizeof(struct sem_undo_list);
957 undo_list = (struct sem_undo_list *) kmalloc(size, GFP_KERNEL);
958 if (undo_list == NULL)
959 return -ENOMEM;
960 memset(undo_list, 0, size);
961 spin_lock_init(&undo_list->lock);
962 atomic_set(&undo_list->refcnt, 1);
963 current->sysvsem.undo_list = undo_list;
964 }
965 *undo_listp = undo_list;
966 return 0;
967}
968
969static struct sem_undo *lookup_undo(struct sem_undo_list *ulp, int semid)
970{
971 struct sem_undo **last, *un;
972
973 last = &ulp->proc_list;
974 un = *last;
975 while(un != NULL) {
976 if(un->semid==semid)
977 break;
978 if(un->semid==-1) {
979 *last=un->proc_next;
980 kfree(un);
981 } else {
982 last=&un->proc_next;
983 }
984 un=*last;
985 }
986 return un;
987}
988
989static struct sem_undo *find_undo(int semid)
990{
991 struct sem_array *sma;
992 struct sem_undo_list *ulp;
993 struct sem_undo *un, *new;
994 int nsems;
995 int error;
996
997 error = get_undo_list(&ulp);
998 if (error)
999 return ERR_PTR(error);
1000
1001 lock_semundo();
1002 un = lookup_undo(ulp, semid);
1003 unlock_semundo();
1004 if (likely(un!=NULL))
1005 goto out;
1006
1007
1008 sma = sem_lock(semid);
1009 un = ERR_PTR(-EINVAL);
1010 if(sma==NULL)
1011 goto out;
1012 un = ERR_PTR(-EIDRM);
1013 if (sem_checkid(sma,semid)) {
1014 sem_unlock(sma);
1015 goto out;
1016 }
1017 nsems = sma->sem_nsems;
1018 ipc_rcu_getref(sma);
1019 sem_unlock(sma);
1020
1021 new = (struct sem_undo *) kmalloc(sizeof(struct sem_undo) + sizeof(short)*nsems, GFP_KERNEL);
1022 if (!new) {
1023 ipc_lock_by_ptr(&sma->sem_perm);
1024 ipc_rcu_putref(sma);
1025 sem_unlock(sma);
1026 return ERR_PTR(-ENOMEM);
1027 }
1028 memset(new, 0, sizeof(struct sem_undo) + sizeof(short)*nsems);
1029 new->semadj = (short *) &new[1];
1030 new->semid = semid;
1031
1032 lock_semundo();
1033 un = lookup_undo(ulp, semid);
1034 if (un) {
1035 unlock_semundo();
1036 kfree(new);
1037 ipc_lock_by_ptr(&sma->sem_perm);
1038 ipc_rcu_putref(sma);
1039 sem_unlock(sma);
1040 goto out;
1041 }
1042 ipc_lock_by_ptr(&sma->sem_perm);
1043 ipc_rcu_putref(sma);
1044 if (sma->sem_perm.deleted) {
1045 sem_unlock(sma);
1046 unlock_semundo();
1047 kfree(new);
1048 un = ERR_PTR(-EIDRM);
1049 goto out;
1050 }
1051 new->proc_next = ulp->proc_list;
1052 ulp->proc_list = new;
1053 new->id_next = sma->undo;
1054 sma->undo = new;
1055 sem_unlock(sma);
1056 un = new;
1057 unlock_semundo();
1058out:
1059 return un;
1060}
1061
1062asmlinkage long sys_semtimedop(int semid, struct sembuf __user *tsops,
1063 unsigned nsops, const struct timespec __user *timeout)
1064{
1065 int error = -EINVAL;
1066 struct sem_array *sma;
1067 struct sembuf fast_sops[SEMOPM_FAST];
1068 struct sembuf* sops = fast_sops, *sop;
1069 struct sem_undo *un;
1070 int undos = 0, alter = 0, max;
1071 struct sem_queue queue;
1072 unsigned long jiffies_left = 0;
1073
1074 if (nsops < 1 || semid < 0)
1075 return -EINVAL;
1076 if (nsops > sc_semopm)
1077 return -E2BIG;
1078 if(nsops > SEMOPM_FAST) {
1079 sops = kmalloc(sizeof(*sops)*nsops,GFP_KERNEL);
1080 if(sops==NULL)
1081 return -ENOMEM;
1082 }
1083 if (copy_from_user (sops, tsops, nsops * sizeof(*tsops))) {
1084 error=-EFAULT;
1085 goto out_free;
1086 }
1087 if (timeout) {
1088 struct timespec _timeout;
1089 if (copy_from_user(&_timeout, timeout, sizeof(*timeout))) {
1090 error = -EFAULT;
1091 goto out_free;
1092 }
1093 if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 ||
1094 _timeout.tv_nsec >= 1000000000L) {
1095 error = -EINVAL;
1096 goto out_free;
1097 }
1098 jiffies_left = timespec_to_jiffies(&_timeout);
1099 }
1100 max = 0;
1101 for (sop = sops; sop < sops + nsops; sop++) {
1102 if (sop->sem_num >= max)
1103 max = sop->sem_num;
1104 if (sop->sem_flg & SEM_UNDO)
1105 undos = 1;
1106 if (sop->sem_op != 0)
1107 alter = 1;
1108 }
1109
1110retry_undos:
1111 if (undos) {
1112 un = find_undo(semid);
1113 if (IS_ERR(un)) {
1114 error = PTR_ERR(un);
1115 goto out_free;
1116 }
1117 } else
1118 un = NULL;
1119
1120 sma = sem_lock(semid);
1121 error=-EINVAL;
1122 if(sma==NULL)
1123 goto out_free;
1124 error = -EIDRM;
1125 if (sem_checkid(sma,semid))
1126 goto out_unlock_free;
1127
1128
1129
1130
1131
1132 if (un && un->semid == -1) {
1133 sem_unlock(sma);
1134 goto retry_undos;
1135 }
1136 error = -EFBIG;
1137 if (max >= sma->sem_nsems)
1138 goto out_unlock_free;
1139
1140 error = -EACCES;
1141 if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
1142 goto out_unlock_free;
1143
1144 error = security_sem_semop(sma, sops, nsops, alter);
1145 if (error)
1146 goto out_unlock_free;
1147
1148 error = try_atomic_semop (sma, sops, nsops, un, current->tgid);
1149 if (error <= 0) {
1150 if (alter && error == 0)
1151 update_queue (sma);
1152 goto out_unlock_free;
1153 }
1154
1155
1156
1157
1158
1159 queue.sma = sma;
1160 queue.sops = sops;
1161 queue.nsops = nsops;
1162 queue.undo = un;
1163 queue.pid = current->tgid;
1164 queue.id = semid;
1165 queue.alter = alter;
1166 if (alter)
1167 append_to_queue(sma ,&queue);
1168 else
1169 prepend_to_queue(sma ,&queue);
1170
1171 queue.status = -EINTR;
1172 queue.sleeper = current;
1173 current->state = TASK_INTERRUPTIBLE;
1174 sem_unlock(sma);
1175
1176 if (timeout)
1177 jiffies_left = schedule_timeout(jiffies_left);
1178 else
1179 schedule();
1180
1181 error = queue.status;
1182 while(unlikely(error == IN_WAKEUP)) {
1183 cpu_relax();
1184 error = queue.status;
1185 }
1186
1187 if (error != -EINTR) {
1188
1189
1190 goto out_free;
1191 }
1192
1193 sma = sem_lock(semid);
1194 if(sma==NULL) {
1195 BUG_ON(queue.prev != NULL);
1196 error = -EIDRM;
1197 goto out_free;
1198 }
1199
1200
1201
1202
1203 error = queue.status;
1204 if (error != -EINTR) {
1205 goto out_unlock_free;
1206 }
1207
1208
1209
1210
1211 if (timeout && jiffies_left == 0)
1212 error = -EAGAIN;
1213 remove_from_queue(sma,&queue);
1214 goto out_unlock_free;
1215
1216out_unlock_free:
1217 sem_unlock(sma);
1218out_free:
1219 if(sops != fast_sops)
1220 kfree(sops);
1221 return error;
1222}
1223
1224asmlinkage long sys_semop (int semid, struct sembuf __user *tsops, unsigned nsops)
1225{
1226 return sys_semtimedop(semid, tsops, nsops, NULL);
1227}
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237int copy_semundo(unsigned long clone_flags, struct task_struct *tsk)
1238{
1239 struct sem_undo_list *undo_list;
1240 int error;
1241
1242 if (clone_flags & CLONE_SYSVSEM) {
1243 error = get_undo_list(&undo_list);
1244 if (error)
1245 return error;
1246 atomic_inc(&undo_list->refcnt);
1247 tsk->sysvsem.undo_list = undo_list;
1248 } else
1249 tsk->sysvsem.undo_list = NULL;
1250
1251 return 0;
1252}
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266void exit_sem(struct task_struct *tsk)
1267{
1268 struct sem_undo_list *undo_list;
1269 struct sem_undo *u, **up;
1270
1271 undo_list = tsk->sysvsem.undo_list;
1272 if (!undo_list)
1273 return;
1274
1275 if (!atomic_dec_and_test(&undo_list->refcnt))
1276 return;
1277
1278
1279
1280
1281 for (up = &undo_list->proc_list; (u = *up); *up = u->proc_next, kfree(u)) {
1282 struct sem_array *sma;
1283 int nsems, i;
1284 struct sem_undo *un, **unp;
1285 int semid;
1286
1287 semid = u->semid;
1288
1289 if(semid == -1)
1290 continue;
1291 sma = sem_lock(semid);
1292 if (sma == NULL)
1293 continue;
1294
1295 if (u->semid == -1)
1296 goto next_entry;
1297
1298 BUG_ON(sem_checkid(sma,u->semid));
1299
1300
1301 for (unp = &sma->undo; (un = *unp); unp = &un->id_next) {
1302 if (u == un)
1303 goto found;
1304 }
1305 printk ("exit_sem undo list error id=%d\n", u->semid);
1306 goto next_entry;
1307found:
1308 *unp = un->id_next;
1309
1310 nsems = sma->sem_nsems;
1311 for (i = 0; i < nsems; i++) {
1312 struct sem * semaphore = &sma->sem_base[i];
1313 if (u->semadj[i]) {
1314 semaphore->semval += u->semadj[i];
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328 if (semaphore->semval < 0)
1329 semaphore->semval = 0;
1330 if (semaphore->semval > SEMVMX)
1331 semaphore->semval = SEMVMX;
1332 semaphore->sempid = current->tgid;
1333 }
1334 }
1335 sma->sem_otime = get_seconds();
1336
1337 update_queue(sma);
1338next_entry:
1339 sem_unlock(sma);
1340 }
1341 kfree(undo_list);
1342}
1343
1344#ifdef CONFIG_PROC_FS
1345static int sysvipc_sem_proc_show(struct seq_file *s, void *it)
1346{
1347 struct sem_array *sma = it;
1348
1349 return seq_printf(s,
1350 "%10d %10d %4o %10lu %5u %5u %5u %5u %10lu %10lu\n",
1351 sma->sem_perm.key,
1352 sma->sem_id,
1353 sma->sem_perm.mode,
1354 sma->sem_nsems,
1355 sma->sem_perm.uid,
1356 sma->sem_perm.gid,
1357 sma->sem_perm.cuid,
1358 sma->sem_perm.cgid,
1359 sma->sem_otime,
1360 sma->sem_ctime);
1361}
1362#endif
1363