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