1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include <linux/config.h>
16#include <linux/mm.h>
17#include <linux/shm.h>
18#include <linux/init.h>
19#include <linux/msg.h>
20#include <linux/smp_lock.h>
21#include <linux/vmalloc.h>
22#include <linux/slab.h>
23#include <linux/highuid.h>
24#include <linux/security.h>
25#include <linux/rcupdate.h>
26#include <linux/workqueue.h>
27
28#include <asm/unistd.h>
29
30#include "util.h"
31
32
33
34
35
36
37
38
39static int __init ipc_init(void)
40{
41 sem_init();
42 msg_init();
43 shm_init();
44 return 0;
45}
46__initcall(ipc_init);
47
48
49
50
51
52
53
54
55
56
57
58void __init ipc_init_ids(struct ipc_ids* ids, int size)
59{
60 int i;
61 sema_init(&ids->sem,1);
62
63 if(size > IPCMNI)
64 size = IPCMNI;
65 ids->size = size;
66 ids->in_use = 0;
67 ids->max_id = -1;
68 ids->seq = 0;
69 {
70 int seq_limit = INT_MAX/SEQ_MULTIPLIER;
71 if(seq_limit > USHRT_MAX)
72 ids->seq_max = USHRT_MAX;
73 else
74 ids->seq_max = seq_limit;
75 }
76
77 ids->entries = ipc_rcu_alloc(sizeof(struct ipc_id)*size);
78
79 if(ids->entries == NULL) {
80 printk(KERN_ERR "ipc_init_ids() failed, ipc service disabled.\n");
81 ids->size = 0;
82 }
83 for(i=0;i<ids->size;i++)
84 ids->entries[i].p = NULL;
85}
86
87
88
89
90
91
92
93
94
95
96int ipc_findkey(struct ipc_ids* ids, key_t key)
97{
98 int id;
99 struct kern_ipc_perm* p;
100 int max_id = ids->max_id;
101
102
103
104
105
106 for (id = 0; id <= max_id; id++) {
107 p = ids->entries[id].p;
108 if(p==NULL)
109 continue;
110 if (key == p->key)
111 return id;
112 }
113 return -1;
114}
115
116
117
118
119static int grow_ary(struct ipc_ids* ids, int newsize)
120{
121 struct ipc_id* new;
122 struct ipc_id* old;
123 int i;
124
125 if(newsize > IPCMNI)
126 newsize = IPCMNI;
127 if(newsize <= ids->size)
128 return newsize;
129
130 new = ipc_rcu_alloc(sizeof(struct ipc_id)*newsize);
131 if(new == NULL)
132 return ids->size;
133 memcpy(new, ids->entries, sizeof(struct ipc_id)*ids->size);
134 for(i=ids->size;i<newsize;i++) {
135 new[i].p = NULL;
136 }
137 old = ids->entries;
138
139
140
141
142
143
144 smp_wmb();
145 ids->entries = new;
146 smp_wmb();
147 ids->size = newsize;
148
149 ipc_rcu_putref(old);
150 return ids->size;
151}
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
168{
169 int id;
170
171 size = grow_ary(ids,size);
172
173
174
175
176
177 for (id = 0; id < size; id++) {
178 if(ids->entries[id].p == NULL)
179 goto found;
180 }
181 return -1;
182found:
183 ids->in_use++;
184 if (id > ids->max_id)
185 ids->max_id = id;
186
187 new->cuid = new->uid = current->euid;
188 new->gid = new->cgid = current->egid;
189
190 new->seq = ids->seq++;
191 if(ids->seq > ids->seq_max)
192 ids->seq = 0;
193
194 new->lock = SPIN_LOCK_UNLOCKED;
195 new->deleted = 0;
196 rcu_read_lock();
197 spin_lock(&new->lock);
198 ids->entries[id].p = new;
199 return id;
200}
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id)
216{
217 struct kern_ipc_perm* p;
218 int lid = id % SEQ_MULTIPLIER;
219 if(lid >= ids->size)
220 BUG();
221
222
223
224
225
226 p = ids->entries[lid].p;
227 ids->entries[lid].p = NULL;
228 if(p==NULL)
229 BUG();
230 ids->in_use--;
231
232 if (lid == ids->max_id) {
233 do {
234 lid--;
235 if(lid == -1)
236 break;
237 } while (ids->entries[lid].p == NULL);
238 ids->max_id = lid;
239 }
240 p->deleted = 1;
241 return p;
242}
243
244
245
246
247
248
249
250
251
252void* ipc_alloc(int size)
253{
254 void* out;
255 if(size > PAGE_SIZE)
256 out = vmalloc(size);
257 else
258 out = kmalloc(size, GFP_KERNEL);
259 return out;
260}
261
262
263
264
265
266
267
268
269
270
271void ipc_free(void* ptr, int size)
272{
273 if(size > PAGE_SIZE)
274 vfree(ptr);
275 else
276 kfree(ptr);
277}
278
279
280
281
282
283
284
285
286
287
288
289struct ipc_rcu_hdr
290{
291 int refcount;
292 int is_vmalloc;
293 void *data[0];
294};
295
296
297struct ipc_rcu_grace
298{
299 struct rcu_head rcu;
300
301 void *data[0];
302};
303
304struct ipc_rcu_sched
305{
306 struct work_struct work;
307
308 void *data[0];
309};
310
311#define HDRLEN_KMALLOC (sizeof(struct ipc_rcu_grace) > sizeof(struct ipc_rcu_hdr) ? \
312 sizeof(struct ipc_rcu_grace) : sizeof(struct ipc_rcu_hdr))
313#define HDRLEN_VMALLOC (sizeof(struct ipc_rcu_sched) > HDRLEN_KMALLOC ? \
314 sizeof(struct ipc_rcu_sched) : HDRLEN_KMALLOC)
315
316static inline int rcu_use_vmalloc(int size)
317{
318
319 if (HDRLEN_KMALLOC + size > PAGE_SIZE)
320 return 1;
321 return 0;
322}
323
324
325
326
327
328
329
330
331
332
333void* ipc_rcu_alloc(int size)
334{
335 void* out;
336
337
338
339
340 if (rcu_use_vmalloc(size)) {
341 out = vmalloc(HDRLEN_VMALLOC + size);
342 if (out) {
343 out += HDRLEN_VMALLOC;
344 container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 1;
345 container_of(out, struct ipc_rcu_hdr, data)->refcount = 1;
346 }
347 } else {
348 out = kmalloc(HDRLEN_KMALLOC + size, GFP_KERNEL);
349 if (out) {
350 out += HDRLEN_KMALLOC;
351 container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 0;
352 container_of(out, struct ipc_rcu_hdr, data)->refcount = 1;
353 }
354 }
355
356 return out;
357}
358
359void ipc_rcu_getref(void *ptr)
360{
361 container_of(ptr, struct ipc_rcu_hdr, data)->refcount++;
362}
363
364
365
366
367
368
369
370static void ipc_schedule_free(struct rcu_head *head)
371{
372 struct ipc_rcu_grace *grace =
373 container_of(head, struct ipc_rcu_grace, rcu);
374 struct ipc_rcu_sched *sched =
375 container_of(&(grace->data[0]), struct ipc_rcu_sched, data[0]);
376
377 INIT_WORK(&sched->work, vfree, sched);
378 schedule_work(&sched->work);
379}
380
381
382
383
384
385
386
387static void ipc_immediate_free(struct rcu_head *head)
388{
389 struct ipc_rcu_grace *free =
390 container_of(head, struct ipc_rcu_grace, rcu);
391 kfree(free);
392}
393
394void ipc_rcu_putref(void *ptr)
395{
396 if (--container_of(ptr, struct ipc_rcu_hdr, data)->refcount > 0)
397 return;
398
399 if (container_of(ptr, struct ipc_rcu_hdr, data)->is_vmalloc) {
400 call_rcu(&container_of(ptr, struct ipc_rcu_grace, data)->rcu,
401 ipc_schedule_free);
402 } else {
403 call_rcu(&container_of(ptr, struct ipc_rcu_grace, data)->rcu,
404 ipc_immediate_free);
405 }
406}
407
408
409
410
411
412
413
414
415
416
417int ipcperms (struct kern_ipc_perm *ipcp, short flag)
418{
419 int requested_mode, granted_mode;
420
421 requested_mode = (flag >> 6) | (flag >> 3) | flag;
422 granted_mode = ipcp->mode;
423 if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
424 granted_mode >>= 6;
425 else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid))
426 granted_mode >>= 3;
427
428 if ((requested_mode & ~granted_mode & 0007) &&
429 !capable(CAP_IPC_OWNER))
430 return -1;
431
432 return security_ipc_permission(ipcp, flag);
433}
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out)
451{
452 out->key = in->key;
453 out->uid = in->uid;
454 out->gid = in->gid;
455 out->cuid = in->cuid;
456 out->cgid = in->cgid;
457 out->mode = in->mode;
458 out->seq = in->seq;
459}
460
461
462
463
464
465
466
467
468
469
470void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out)
471{
472 out->key = in->key;
473 SET_UID(out->uid, in->uid);
474 SET_GID(out->gid, in->gid);
475 SET_UID(out->cuid, in->cuid);
476 SET_GID(out->cgid, in->cgid);
477 out->mode = in->mode;
478 out->seq = in->seq;
479}
480
481
482
483
484
485
486
487
488
489
490
491
492struct kern_ipc_perm* ipc_get(struct ipc_ids* ids, int id)
493{
494 struct kern_ipc_perm* out;
495 int lid = id % SEQ_MULTIPLIER;
496 if(lid >= ids->size)
497 return NULL;
498 out = ids->entries[lid].p;
499 return out;
500}
501
502struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id)
503{
504 struct kern_ipc_perm* out;
505 int lid = id % SEQ_MULTIPLIER;
506 struct ipc_id* entries;
507
508 rcu_read_lock();
509 if(lid >= ids->size) {
510 rcu_read_unlock();
511 return NULL;
512 }
513
514
515
516
517
518
519
520
521
522 smp_rmb();
523 entries = rcu_dereference(ids->entries);
524 out = entries[lid].p;
525 if(out == NULL) {
526 rcu_read_unlock();
527 return NULL;
528 }
529 spin_lock(&out->lock);
530
531
532
533
534 if (out->deleted) {
535 spin_unlock(&out->lock);
536 rcu_read_unlock();
537 return NULL;
538 }
539 return out;
540}
541
542void ipc_lock_by_ptr(struct kern_ipc_perm *perm)
543{
544 rcu_read_lock();
545 spin_lock(&perm->lock);
546}
547
548void ipc_unlock(struct kern_ipc_perm* perm)
549{
550 spin_unlock(&perm->lock);
551 rcu_read_unlock();
552}
553
554int ipc_buildid(struct ipc_ids* ids, int id, int seq)
555{
556 return SEQ_MULTIPLIER*seq + id;
557}
558
559int ipc_checkid(struct ipc_ids* ids, struct kern_ipc_perm* ipcp, int uid)
560{
561 if(uid/SEQ_MULTIPLIER != ipcp->seq)
562 return 1;
563 return 0;
564}
565
566#ifdef __ARCH_WANT_IPC_PARSE_VERSION
567
568
569
570
571
572
573
574
575
576
577
578int ipc_parse_version (int *cmd)
579{
580 if (*cmd & IPC_64) {
581 *cmd ^= IPC_64;
582 return IPC_64;
583 } else {
584 return IPC_OLD;
585 }
586}
587
588#endif
589