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 i = ids->size;
139
140
141
142
143
144
145 smp_wmb();
146 ids->entries = new;
147 smp_wmb();
148 ids->size = newsize;
149
150 ipc_rcu_free(old, sizeof(struct ipc_id)*i);
151 return ids->size;
152}
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
169{
170 int id;
171
172 size = grow_ary(ids,size);
173
174
175
176
177
178 for (id = 0; id < size; id++) {
179 if(ids->entries[id].p == NULL)
180 goto found;
181 }
182 return -1;
183found:
184 ids->in_use++;
185 if (id > ids->max_id)
186 ids->max_id = id;
187
188 new->cuid = new->uid = current->euid;
189 new->gid = new->cgid = current->egid;
190
191 new->seq = ids->seq++;
192 if(ids->seq > ids->seq_max)
193 ids->seq = 0;
194
195 new->lock = SPIN_LOCK_UNLOCKED;
196 new->deleted = 0;
197 rcu_read_lock();
198 spin_lock(&new->lock);
199 ids->entries[id].p = new;
200 return id;
201}
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id)
217{
218 struct kern_ipc_perm* p;
219 int lid = id % SEQ_MULTIPLIER;
220 if(lid >= ids->size)
221 BUG();
222
223
224
225
226
227 p = ids->entries[lid].p;
228 ids->entries[lid].p = NULL;
229 if(p==NULL)
230 BUG();
231 ids->in_use--;
232
233 if (lid == ids->max_id) {
234 do {
235 lid--;
236 if(lid == -1)
237 break;
238 } while (ids->entries[lid].p == NULL);
239 ids->max_id = lid;
240 }
241 p->deleted = 1;
242 return p;
243}
244
245
246
247
248
249
250
251
252
253void* ipc_alloc(int size)
254{
255 void* out;
256 if(size > PAGE_SIZE)
257 out = vmalloc(size);
258 else
259 out = kmalloc(size, GFP_KERNEL);
260 return out;
261}
262
263
264
265
266
267
268
269
270
271
272void ipc_free(void* ptr, int size)
273{
274 if(size > PAGE_SIZE)
275 vfree(ptr);
276 else
277 kfree(ptr);
278}
279
280struct ipc_rcu_kmalloc
281{
282 struct rcu_head rcu;
283
284 void *data[0];
285};
286
287struct ipc_rcu_vmalloc
288{
289 struct rcu_head rcu;
290 struct work_struct work;
291
292 void *data[0];
293};
294
295static inline int rcu_use_vmalloc(int size)
296{
297
298 if (sizeof(struct ipc_rcu_kmalloc) + size > PAGE_SIZE)
299 return 1;
300 return 0;
301}
302
303
304
305
306
307
308
309
310
311
312void* ipc_rcu_alloc(int size)
313{
314 void* out;
315
316
317
318
319 if (rcu_use_vmalloc(size)) {
320 out = vmalloc(sizeof(struct ipc_rcu_vmalloc) + size);
321 if (out) out += sizeof(struct ipc_rcu_vmalloc);
322 } else {
323 out = kmalloc(sizeof(struct ipc_rcu_kmalloc)+size, GFP_KERNEL);
324 if (out) out += sizeof(struct ipc_rcu_kmalloc);
325 }
326
327 return out;
328}
329
330
331
332
333
334
335
336static void ipc_schedule_free(struct rcu_head *head)
337{
338 struct ipc_rcu_vmalloc *free =
339 container_of(head, struct ipc_rcu_vmalloc, rcu);
340
341 INIT_WORK(&free->work, vfree, free);
342 schedule_work(&free->work);
343}
344
345
346
347
348
349
350
351static void ipc_immediate_free(struct rcu_head *head)
352{
353 struct ipc_rcu_kmalloc *free =
354 container_of(head, struct ipc_rcu_kmalloc, rcu);
355 kfree(free);
356}
357
358
359
360void ipc_rcu_free(void* ptr, int size)
361{
362 if (rcu_use_vmalloc(size)) {
363 struct ipc_rcu_vmalloc *free;
364 free = ptr - sizeof(*free);
365 call_rcu(&free->rcu, ipc_schedule_free);
366 } else {
367 struct ipc_rcu_kmalloc *free;
368 free = ptr - sizeof(*free);
369 call_rcu(&free->rcu, ipc_immediate_free);
370 }
371
372}
373
374
375
376
377
378
379
380
381
382
383int ipcperms (struct kern_ipc_perm *ipcp, short flag)
384{
385 int requested_mode, granted_mode;
386
387 requested_mode = (flag >> 6) | (flag >> 3) | flag;
388 granted_mode = ipcp->mode;
389 if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
390 granted_mode >>= 6;
391 else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid))
392 granted_mode >>= 3;
393
394 if ((requested_mode & ~granted_mode & 0007) &&
395 !capable(CAP_IPC_OWNER))
396 return -1;
397
398 return security_ipc_permission(ipcp, flag);
399}
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out)
417{
418 out->key = in->key;
419 out->uid = in->uid;
420 out->gid = in->gid;
421 out->cuid = in->cuid;
422 out->cgid = in->cgid;
423 out->mode = in->mode;
424 out->seq = in->seq;
425}
426
427
428
429
430
431
432
433
434
435
436void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out)
437{
438 out->key = in->key;
439 SET_UID(out->uid, in->uid);
440 SET_GID(out->gid, in->gid);
441 SET_UID(out->cuid, in->cuid);
442 SET_GID(out->cgid, in->cgid);
443 out->mode = in->mode;
444 out->seq = in->seq;
445}
446
447
448
449
450
451
452
453
454
455
456
457
458struct kern_ipc_perm* ipc_get(struct ipc_ids* ids, int id)
459{
460 struct kern_ipc_perm* out;
461 int lid = id % SEQ_MULTIPLIER;
462 if(lid >= ids->size)
463 return NULL;
464 out = ids->entries[lid].p;
465 return out;
466}
467
468struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id)
469{
470 struct kern_ipc_perm* out;
471 int lid = id % SEQ_MULTIPLIER;
472 struct ipc_id* entries;
473
474 rcu_read_lock();
475 if(lid >= ids->size) {
476 rcu_read_unlock();
477 return NULL;
478 }
479
480
481
482
483
484
485
486
487
488 smp_rmb();
489 entries = ids->entries;
490 read_barrier_depends();
491 out = entries[lid].p;
492 if(out == NULL) {
493 rcu_read_unlock();
494 return NULL;
495 }
496 spin_lock(&out->lock);
497
498
499
500
501 if (out->deleted) {
502 spin_unlock(&out->lock);
503 rcu_read_unlock();
504 return NULL;
505 }
506 return out;
507}
508
509void ipc_unlock(struct kern_ipc_perm* perm)
510{
511 spin_unlock(&perm->lock);
512 rcu_read_unlock();
513}
514
515int ipc_buildid(struct ipc_ids* ids, int id, int seq)
516{
517 return SEQ_MULTIPLIER*seq + id;
518}
519
520int ipc_checkid(struct ipc_ids* ids, struct kern_ipc_perm* ipcp, int uid)
521{
522 if(uid/SEQ_MULTIPLIER != ipcp->seq)
523 return 1;
524 return 0;
525}
526
527#ifdef __ARCH_WANT_IPC_PARSE_VERSION
528
529
530
531
532
533
534
535
536
537
538
539int ipc_parse_version (int *cmd)
540{
541 if (*cmd & IPC_64) {
542 *cmd ^= IPC_64;
543 return IPC_64;
544 } else {
545 return IPC_OLD;
546 }
547}
548
549#endif
550