util.c 19 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
7
8
9
/*
 * linux/ipc/util.c
 * Copyright (C) 1992 Krishna Balasubramanian
 *
 * Sep 1997 - Call suser() last after "normal" permission checks so we
 *            get BSD style process accounting right.
 *            Occurs in several places in the IPC code.
 *            Chris Evans, <chris@ferret.lmh.ox.ac.uk>
 * Nov 1999 - ipc helper functions, unified SMP locking
10
 *	      Manfred Spraul <manfred@colorfullife.com>
Linus Torvalds's avatar
Linus Torvalds committed
11
12
 * Oct 2002 - One lock per IPC id. RCU ipc_free for lock-free grow_ary().
 *            Mingming Cao <cmm@us.ibm.com>
Steve Grubb's avatar
Steve Grubb committed
13
14
 * Mar 2006 - support for audit of ipc object properties
 *            Dustin Kirkland <dustin.kirkland@us.ibm.com>
15
16
17
 * Jun 2006 - namespaces ssupport
 *            OpenVZ, SWsoft Inc.
 *            Pavel Emelianov <xemul@openvz.org>
Linus Torvalds's avatar
Linus Torvalds committed
18
19
20
21
22
23
24
25
26
 */

#include <linux/mm.h>
#include <linux/shm.h>
#include <linux/init.h>
#include <linux/msg.h>
#include <linux/smp_lock.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
27
#include <linux/capability.h>
Linus Torvalds's avatar
Linus Torvalds committed
28
29
30
31
#include <linux/highuid.h>
#include <linux/security.h>
#include <linux/rcupdate.h>
#include <linux/workqueue.h>
32
33
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
Steve Grubb's avatar
Steve Grubb committed
34
#include <linux/audit.h>
35
#include <linux/nsproxy.h>
Linus Torvalds's avatar
Linus Torvalds committed
36
37
38
39
40

#include <asm/unistd.h>

#include "util.h"

41
42
43
struct ipc_proc_iface {
	const char *path;
	const char *header;
44
	int ids;
45
46
47
	int (*show)(struct seq_file *, void *);
};

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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
struct ipc_namespace init_ipc_ns = {
	.kref = {
		.refcount	= ATOMIC_INIT(2),
	},
};

#ifdef CONFIG_IPC_NS
static struct ipc_namespace *clone_ipc_ns(struct ipc_namespace *old_ns)
{
	int err;
	struct ipc_namespace *ns;

	err = -ENOMEM;
	ns = kmalloc(sizeof(struct ipc_namespace), GFP_KERNEL);
	if (ns == NULL)
		goto err_mem;

	err = sem_init_ns(ns);
	if (err)
		goto err_sem;
	err = msg_init_ns(ns);
	if (err)
		goto err_msg;
	err = shm_init_ns(ns);
	if (err)
		goto err_shm;

	kref_init(&ns->kref);
	return ns;

err_shm:
	msg_exit_ns(ns);
err_msg:
	sem_exit_ns(ns);
err_sem:
	kfree(ns);
err_mem:
	return ERR_PTR(err);
}

int unshare_ipcs(unsigned long unshare_flags, struct ipc_namespace **new_ipc)
{
	struct ipc_namespace *new;

	if (unshare_flags & CLONE_NEWIPC) {
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

		new = clone_ipc_ns(current->nsproxy->ipc_ns);
		if (IS_ERR(new))
			return PTR_ERR(new);

		*new_ipc = new;
	}

	return 0;
}

int copy_ipcs(unsigned long flags, struct task_struct *tsk)
{
	struct ipc_namespace *old_ns = tsk->nsproxy->ipc_ns;
	struct ipc_namespace *new_ns;
	int err = 0;

	if (!old_ns)
		return 0;

	get_ipc_ns(old_ns);

	if (!(flags & CLONE_NEWIPC))
		return 0;

	if (!capable(CAP_SYS_ADMIN)) {
		err = -EPERM;
		goto out;
	}

	new_ns = clone_ipc_ns(old_ns);
	if (!new_ns) {
		err = -ENOMEM;
		goto out;
	}

	tsk->nsproxy->ipc_ns = new_ns;
out:
	put_ipc_ns(old_ns);
	return err;
}

void free_ipc_ns(struct kref *kref)
{
	struct ipc_namespace *ns;

	ns = container_of(kref, struct ipc_namespace, kref);
	sem_exit_ns(ns);
	msg_exit_ns(ns);
	shm_exit_ns(ns);
	kfree(ns);
}
#endif

Linus Torvalds's avatar
Linus Torvalds committed
149
150
151
152
/**
 *	ipc_init	-	initialise IPC subsystem
 *
 *	The various system5 IPC resources (semaphores, messages and shared
153
 *	memory) are initialised
Linus Torvalds's avatar
Linus Torvalds committed
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
 */
 
static int __init ipc_init(void)
{
	sem_init();
	msg_init();
	shm_init();
	return 0;
}
__initcall(ipc_init);

/**
 *	ipc_init_ids		-	initialise IPC identifiers
 *	@ids: Identifier set
 *	@size: Number of identifiers
 *
 *	Given a size for the ipc identifier range (limited below IPCMNI)
 *	set up the sequence range to use then allocate and initialise the
 *	array itself. 
 */
 
175
void __ipc_init ipc_init_ids(struct ipc_ids* ids, int size)
Linus Torvalds's avatar
Linus Torvalds committed
176
177
{
	int i;
Ingo Molnar's avatar
Ingo Molnar committed
178
179

	mutex_init(&ids->mutex);
Linus Torvalds's avatar
Linus Torvalds committed
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206

	if(size > IPCMNI)
		size = IPCMNI;
	ids->in_use = 0;
	ids->max_id = -1;
	ids->seq = 0;
	{
		int seq_limit = INT_MAX/SEQ_MULTIPLIER;
		if(seq_limit > USHRT_MAX)
			ids->seq_max = USHRT_MAX;
		 else
		 	ids->seq_max = seq_limit;
	}

	ids->entries = ipc_rcu_alloc(sizeof(struct kern_ipc_perm *)*size +
				     sizeof(struct ipc_id_ary));

	if(ids->entries == NULL) {
		printk(KERN_ERR "ipc_init_ids() failed, ipc service disabled.\n");
		size = 0;
		ids->entries = &ids->nullentry;
	}
	ids->entries->size = size;
	for(i=0;i<size;i++)
		ids->entries->p[i] = NULL;
}

207
208
209
#ifdef CONFIG_PROC_FS
static struct file_operations sysvipc_proc_fops;
/**
210
 *	ipc_init_proc_interface	-  Create a proc interface for sysipc types using a seq_file interface.
211
212
213
214
215
216
 *	@path: Path in procfs
 *	@header: Banner to be printed at the beginning of the file.
 *	@ids: ipc id table to iterate.
 *	@show: show routine.
 */
void __init ipc_init_proc_interface(const char *path, const char *header,
217
		int ids, int (*show)(struct seq_file *, void *))
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
{
	struct proc_dir_entry *pde;
	struct ipc_proc_iface *iface;

	iface = kmalloc(sizeof(*iface), GFP_KERNEL);
	if (!iface)
		return;
	iface->path	= path;
	iface->header	= header;
	iface->ids	= ids;
	iface->show	= show;

	pde = create_proc_entry(path,
				S_IRUGO,        /* world readable */
				NULL            /* parent dir */);
	if (pde) {
		pde->data = iface;
		pde->proc_fops = &sysvipc_proc_fops;
	} else {
		kfree(iface);
	}
}
#endif

Linus Torvalds's avatar
Linus Torvalds committed
242
243
244
245
246
/**
 *	ipc_findkey	-	find a key in an ipc identifier set	
 *	@ids: Identifier set
 *	@key: The key to find
 *	
Ingo Molnar's avatar
Ingo Molnar committed
247
 *	Requires ipc_ids.mutex locked.
Linus Torvalds's avatar
Linus Torvalds committed
248
249
250
251
252
253
254
255
256
257
258
 *	Returns the identifier if found or -1 if not.
 */
 
int ipc_findkey(struct ipc_ids* ids, key_t key)
{
	int id;
	struct kern_ipc_perm* p;
	int max_id = ids->max_id;

	/*
	 * rcu_dereference() is not needed here
Ingo Molnar's avatar
Ingo Molnar committed
259
	 * since ipc_ids.mutex is held
Linus Torvalds's avatar
Linus Torvalds committed
260
261
262
263
264
265
266
267
268
269
270
271
	 */
	for (id = 0; id <= max_id; id++) {
		p = ids->entries->p[id];
		if(p==NULL)
			continue;
		if (key == p->key)
			return id;
	}
	return -1;
}

/*
Ingo Molnar's avatar
Ingo Molnar committed
272
 * Requires ipc_ids.mutex locked
Linus Torvalds's avatar
Linus Torvalds committed
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
 */
static int grow_ary(struct ipc_ids* ids, int newsize)
{
	struct ipc_id_ary* new;
	struct ipc_id_ary* old;
	int i;
	int size = ids->entries->size;

	if(newsize > IPCMNI)
		newsize = IPCMNI;
	if(newsize <= size)
		return newsize;

	new = ipc_rcu_alloc(sizeof(struct kern_ipc_perm *)*newsize +
			    sizeof(struct ipc_id_ary));
	if(new == NULL)
		return size;
	new->size = newsize;
291
	memcpy(new->p, ids->entries->p, sizeof(struct kern_ipc_perm *)*size);
Linus Torvalds's avatar
Linus Torvalds committed
292
293
294
295
296
297
298
299
300
301
302
	for(i=size;i<newsize;i++) {
		new->p[i] = NULL;
	}
	old = ids->entries;

	/*
	 * Use rcu_assign_pointer() to make sure the memcpyed contents
	 * of the new array are visible before the new array becomes visible.
	 */
	rcu_assign_pointer(ids->entries, new);

303
	__ipc_fini_ids(ids, old);
Linus Torvalds's avatar
Linus Torvalds committed
304
305
306
307
308
309
310
311
312
313
314
315
316
317
	return newsize;
}

/**
 *	ipc_addid 	-	add an IPC identifier
 *	@ids: IPC identifier set
 *	@new: new IPC permission set
 *	@size: new size limit for the id array
 *
 *	Add an entry 'new' to the IPC arrays. The permissions object is
 *	initialised and the first free entry is set up and the id assigned
 *	is returned. The list is returned in a locked state on success.
 *	On failure the list is not locked and -1 is returned.
 *
Ingo Molnar's avatar
Ingo Molnar committed
318
 *	Called with ipc_ids.mutex held.
Linus Torvalds's avatar
Linus Torvalds committed
319
320
321
322
323
324
325
326
327
328
 */
 
int ipc_addid(struct ipc_ids* ids, struct kern_ipc_perm* new, int size)
{
	int id;

	size = grow_ary(ids,size);

	/*
	 * rcu_dereference()() is not needed here since
Ingo Molnar's avatar
Ingo Molnar committed
329
	 * ipc_ids.mutex is held
Linus Torvalds's avatar
Linus Torvalds committed
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
	 */
	for (id = 0; id < size; id++) {
		if(ids->entries->p[id] == NULL)
			goto found;
	}
	return -1;
found:
	ids->in_use++;
	if (id > ids->max_id)
		ids->max_id = id;

	new->cuid = new->uid = current->euid;
	new->gid = new->cgid = current->egid;

	new->seq = ids->seq++;
	if(ids->seq > ids->seq_max)
		ids->seq = 0;

	spin_lock_init(&new->lock);
	new->deleted = 0;
	rcu_read_lock();
	spin_lock(&new->lock);
	ids->entries->p[id] = new;
	return id;
}

/**
 *	ipc_rmid	-	remove an IPC identifier
 *	@ids: identifier set
 *	@id: Identifier to remove
 *
 *	The identifier must be valid, and in use. The kernel will panic if
 *	fed an invalid identifier. The entry is removed and internal
 *	variables recomputed. The object associated with the identifier
 *	is returned.
Ingo Molnar's avatar
Ingo Molnar committed
365
 *	ipc_ids.mutex and the spinlock for this ID is hold before this function
Linus Torvalds's avatar
Linus Torvalds committed
366
367
368
369
370
371
372
 *	is called, and remain locked on the exit.
 */
 
struct kern_ipc_perm* ipc_rmid(struct ipc_ids* ids, int id)
{
	struct kern_ipc_perm* p;
	int lid = id % SEQ_MULTIPLIER;
373
	BUG_ON(lid >= ids->entries->size);
Linus Torvalds's avatar
Linus Torvalds committed
374
375
376

	/* 
	 * do not need a rcu_dereference()() here to force ordering
Ingo Molnar's avatar
Ingo Molnar committed
377
	 * on Alpha, since the ipc_ids.mutex is held.
Linus Torvalds's avatar
Linus Torvalds committed
378
379
380
	 */	
	p = ids->entries->p[lid];
	ids->entries->p[lid] = NULL;
381
	BUG_ON(p==NULL);
Linus Torvalds's avatar
Linus Torvalds committed
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
	ids->in_use--;

	if (lid == ids->max_id) {
		do {
			lid--;
			if(lid == -1)
				break;
		} while (ids->entries->p[lid] == NULL);
		ids->max_id = lid;
	}
	p->deleted = 1;
	return p;
}

/**
 *	ipc_alloc	-	allocate ipc space
 *	@size: size desired
 *
 *	Allocate memory from the appropriate pools and return a pointer to it.
 *	NULL is returned if the allocation fails
 */
 
void* ipc_alloc(int size)
{
	void* out;
	if(size > PAGE_SIZE)
		out = vmalloc(size);
	else
		out = kmalloc(size, GFP_KERNEL);
	return out;
}

/**
 *	ipc_free        -       free ipc space
 *	@ptr: pointer returned by ipc_alloc
 *	@size: size of block
 *
419
 *	Free a block created with ipc_alloc(). The caller must know the size
Linus Torvalds's avatar
Linus Torvalds committed
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
 *	used in the allocation call.
 */

void ipc_free(void* ptr, int size)
{
	if(size > PAGE_SIZE)
		vfree(ptr);
	else
		kfree(ptr);
}

/*
 * rcu allocations:
 * There are three headers that are prepended to the actual allocation:
 * - during use: ipc_rcu_hdr.
 * - during the rcu grace period: ipc_rcu_grace.
 * - [only if vmalloc]: ipc_rcu_sched.
 * Their lifetime doesn't overlap, thus the headers share the same memory.
 * Unlike a normal union, they are right-aligned, thus some container_of
 * forward/backward casting is necessary:
 */
struct ipc_rcu_hdr
{
	int refcount;
	int is_vmalloc;
	void *data[0];
};


struct ipc_rcu_grace
{
	struct rcu_head rcu;
	/* "void *" makes sure alignment of following data is sane. */
	void *data[0];
};

struct ipc_rcu_sched
{
	struct work_struct work;
	/* "void *" makes sure alignment of following data is sane. */
	void *data[0];
};

#define HDRLEN_KMALLOC		(sizeof(struct ipc_rcu_grace) > sizeof(struct ipc_rcu_hdr) ? \
					sizeof(struct ipc_rcu_grace) : sizeof(struct ipc_rcu_hdr))
#define HDRLEN_VMALLOC		(sizeof(struct ipc_rcu_sched) > HDRLEN_KMALLOC ? \
					sizeof(struct ipc_rcu_sched) : HDRLEN_KMALLOC)

static inline int rcu_use_vmalloc(int size)
{
	/* Too big for a single page? */
	if (HDRLEN_KMALLOC + size > PAGE_SIZE)
		return 1;
	return 0;
}

/**
 *	ipc_rcu_alloc	-	allocate ipc and rcu space 
 *	@size: size desired
 *
 *	Allocate memory for the rcu header structure +  the object.
 *	Returns the pointer to the object.
 *	NULL is returned if the allocation fails. 
 */
 
void* ipc_rcu_alloc(int size)
{
	void* out;
	/* 
	 * We prepend the allocation with the rcu struct, and
	 * workqueue if necessary (for vmalloc). 
	 */
	if (rcu_use_vmalloc(size)) {
		out = vmalloc(HDRLEN_VMALLOC + size);
		if (out) {
			out += HDRLEN_VMALLOC;
			container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 1;
			container_of(out, struct ipc_rcu_hdr, data)->refcount = 1;
		}
	} else {
		out = kmalloc(HDRLEN_KMALLOC + size, GFP_KERNEL);
		if (out) {
			out += HDRLEN_KMALLOC;
			container_of(out, struct ipc_rcu_hdr, data)->is_vmalloc = 0;
			container_of(out, struct ipc_rcu_hdr, data)->refcount = 1;
		}
	}

	return out;
}

void ipc_rcu_getref(void *ptr)
{
	container_of(ptr, struct ipc_rcu_hdr, data)->refcount++;
}

516
517
518
519
520
static void ipc_do_vfree(struct work_struct *work)
{
	vfree(container_of(work, struct ipc_rcu_sched, work));
}

Linus Torvalds's avatar
Linus Torvalds committed
521
/**
522
523
 * ipc_schedule_free - free ipc + rcu space
 * @head: RCU callback structure for queued work
Linus Torvalds's avatar
Linus Torvalds committed
524
525
 * 
 * Since RCU callback function is called in bh,
526
 * we need to defer the vfree to schedule_work().
Linus Torvalds's avatar
Linus Torvalds committed
527
528
529
530
531
532
533
534
 */
static void ipc_schedule_free(struct rcu_head *head)
{
	struct ipc_rcu_grace *grace =
		container_of(head, struct ipc_rcu_grace, rcu);
	struct ipc_rcu_sched *sched =
			container_of(&(grace->data[0]), struct ipc_rcu_sched, data[0]);

535
	INIT_WORK(&sched->work, ipc_do_vfree);
Linus Torvalds's avatar
Linus Torvalds committed
536
537
538
539
	schedule_work(&sched->work);
}

/**
540
541
 * ipc_immediate_free - free ipc + rcu space
 * @head: RCU callback structure that contains pointer to be freed
Linus Torvalds's avatar
Linus Torvalds committed
542
 *
543
 * Free from the RCU callback context.
Linus Torvalds's avatar
Linus Torvalds committed
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
 */
static void ipc_immediate_free(struct rcu_head *head)
{
	struct ipc_rcu_grace *free =
		container_of(head, struct ipc_rcu_grace, rcu);
	kfree(free);
}

void ipc_rcu_putref(void *ptr)
{
	if (--container_of(ptr, struct ipc_rcu_hdr, data)->refcount > 0)
		return;

	if (container_of(ptr, struct ipc_rcu_hdr, data)->is_vmalloc) {
		call_rcu(&container_of(ptr, struct ipc_rcu_grace, data)->rcu,
				ipc_schedule_free);
	} else {
		call_rcu(&container_of(ptr, struct ipc_rcu_grace, data)->rcu,
				ipc_immediate_free);
	}
}

/**
 *	ipcperms	-	check IPC permissions
 *	@ipcp: IPC permission set
 *	@flag: desired permission set.
 *
 *	Check user, group, other permissions for access
 *	to ipc resources. return 0 if allowed
 */
 
int ipcperms (struct kern_ipc_perm *ipcp, short flag)
{	/* flag will most probably be 0 or S_...UGO from <linux/stat.h> */
Steve Grubb's avatar
Steve Grubb committed
577
	int requested_mode, granted_mode, err;
Linus Torvalds's avatar
Linus Torvalds committed
578

Steve Grubb's avatar
Steve Grubb committed
579
580
	if (unlikely((err = audit_ipc_obj(ipcp))))
		return err;
Linus Torvalds's avatar
Linus Torvalds committed
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
	requested_mode = (flag >> 6) | (flag >> 3) | flag;
	granted_mode = ipcp->mode;
	if (current->euid == ipcp->cuid || current->euid == ipcp->uid)
		granted_mode >>= 6;
	else if (in_group_p(ipcp->cgid) || in_group_p(ipcp->gid))
		granted_mode >>= 3;
	/* is there some bit set in requested_mode but not in granted_mode? */
	if ((requested_mode & ~granted_mode & 0007) && 
	    !capable(CAP_IPC_OWNER))
		return -1;

	return security_ipc_permission(ipcp, flag);
}

/*
 * Functions to convert between the kern_ipc_perm structure and the
 * old/new ipc_perm structures
 */

/**
 *	kernel_to_ipc64_perm	-	convert kernel ipc permissions to user
 *	@in: kernel permissions
 *	@out: new style IPC permissions
 *
605
606
 *	Turn the kernel object @in into a set of permissions descriptions
 *	for returning to userspace (@out).
Linus Torvalds's avatar
Linus Torvalds committed
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
 */
 

void kernel_to_ipc64_perm (struct kern_ipc_perm *in, struct ipc64_perm *out)
{
	out->key	= in->key;
	out->uid	= in->uid;
	out->gid	= in->gid;
	out->cuid	= in->cuid;
	out->cgid	= in->cgid;
	out->mode	= in->mode;
	out->seq	= in->seq;
}

/**
 *	ipc64_perm_to_ipc_perm	-	convert old ipc permissions to new
 *	@in: new style IPC permissions
 *	@out: old style IPC permissions
 *
626
627
 *	Turn the new style permissions object @in into a compatibility
 *	object and store it into the @out pointer.
Linus Torvalds's avatar
Linus Torvalds committed
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
 */
 
void ipc64_perm_to_ipc_perm (struct ipc64_perm *in, struct ipc_perm *out)
{
	out->key	= in->key;
	SET_UID(out->uid, in->uid);
	SET_GID(out->gid, in->gid);
	SET_UID(out->cuid, in->cuid);
	SET_GID(out->cgid, in->cgid);
	out->mode	= in->mode;
	out->seq	= in->seq;
}

/*
 * So far only shm_get_stat() calls ipc_get() via shm_get(), so ipc_get()
Ingo Molnar's avatar
Ingo Molnar committed
643
644
 * is called with shm_ids.mutex locked.  Since grow_ary() is also called with
 * shm_ids.mutex down(for Shared Memory), there is no need to add read
Linus Torvalds's avatar
Linus Torvalds committed
645
646
647
 * barriers here to gurantee the writes in grow_ary() are seen in order 
 * here (for Alpha).
 *
Ingo Molnar's avatar
Ingo Molnar committed
648
649
 * However ipc_get() itself does not necessary require ipc_ids.mutex down. So
 * if in the future ipc_get() is used by other places without ipc_ids.mutex
Linus Torvalds's avatar
Linus Torvalds committed
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
 * down, then ipc_get() needs read memery barriers as ipc_lock() does.
 */
struct kern_ipc_perm* ipc_get(struct ipc_ids* ids, int id)
{
	struct kern_ipc_perm* out;
	int lid = id % SEQ_MULTIPLIER;
	if(lid >= ids->entries->size)
		return NULL;
	out = ids->entries->p[lid];
	return out;
}

struct kern_ipc_perm* ipc_lock(struct ipc_ids* ids, int id)
{
	struct kern_ipc_perm* out;
	int lid = id % SEQ_MULTIPLIER;
	struct ipc_id_ary* entries;

	rcu_read_lock();
	entries = rcu_dereference(ids->entries);
	if(lid >= entries->size) {
		rcu_read_unlock();
		return NULL;
	}
	out = entries->p[lid];
	if(out == NULL) {
		rcu_read_unlock();
		return NULL;
	}
	spin_lock(&out->lock);
	
	/* ipc_rmid() may have already freed the ID while ipc_lock
	 * was spinning: here verify that the structure is still valid
	 */
	if (out->deleted) {
		spin_unlock(&out->lock);
		rcu_read_unlock();
		return NULL;
	}
	return out;
}

void ipc_lock_by_ptr(struct kern_ipc_perm *perm)
{
	rcu_read_lock();
	spin_lock(&perm->lock);
}

void ipc_unlock(struct kern_ipc_perm* perm)
{
	spin_unlock(&perm->lock);
	rcu_read_unlock();
}

int ipc_buildid(struct ipc_ids* ids, int id, int seq)
{
	return SEQ_MULTIPLIER*seq + id;
}

int ipc_checkid(struct ipc_ids* ids, struct kern_ipc_perm* ipcp, int uid)
{
	if(uid/SEQ_MULTIPLIER != ipcp->seq)
		return 1;
	return 0;
}

#ifdef __ARCH_WANT_IPC_PARSE_VERSION


/**
 *	ipc_parse_version	-	IPC call version
 *	@cmd: pointer to command
 *
 *	Return IPC_64 for new style IPC and IPC_OLD for old style IPC. 
724
 *	The @cmd value is turned from an encoding command and version into
Linus Torvalds's avatar
Linus Torvalds committed
725
726
727
728
729
730
731
732
733
734
735
736
737
738
 *	just the command code.
 */
 
int ipc_parse_version (int *cmd)
{
	if (*cmd & IPC_64) {
		*cmd ^= IPC_64;
		return IPC_64;
	} else {
		return IPC_OLD;
	}
}

#endif /* __ARCH_WANT_IPC_PARSE_VERSION */
739
740
741
742
743
744
745

#ifdef CONFIG_PROC_FS
static void *sysvipc_proc_next(struct seq_file *s, void *it, loff_t *pos)
{
	struct ipc_proc_iface *iface = s->private;
	struct kern_ipc_perm *ipc = it;
	loff_t p;
746
747
748
	struct ipc_ids *ids;

	ids = current->nsproxy->ipc_ns->ids[iface->ids];
749
750
751
752
753
754
755
756
757

	/* If we had an ipc id locked before, unlock it */
	if (ipc && ipc != SEQ_START_TOKEN)
		ipc_unlock(ipc);

	/*
	 * p = *pos - 1 (because id 0 starts at position 1)
	 *          + 1 (because we increment the position by one)
	 */
758
759
	for (p = *pos; p <= ids->max_id; p++) {
		if ((ipc = ipc_lock(ids, p)) != NULL) {
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
			*pos = p + 1;
			return ipc;
		}
	}

	/* Out of range - return NULL to terminate iteration */
	return NULL;
}

/*
 * File positions: pos 0 -> header, pos n -> ipc id + 1.
 * SeqFile iterator: iterator value locked shp or SEQ_TOKEN_START.
 */
static void *sysvipc_proc_start(struct seq_file *s, loff_t *pos)
{
	struct ipc_proc_iface *iface = s->private;
	struct kern_ipc_perm *ipc;
	loff_t p;
778
779
780
	struct ipc_ids *ids;

	ids = current->nsproxy->ipc_ns->ids[iface->ids];
781
782
783
784
785

	/*
	 * Take the lock - this will be released by the corresponding
	 * call to stop().
	 */
786
	mutex_lock(&ids->mutex);
787
788
789
790
791
792
793
794
795
796

	/* pos < 0 is invalid */
	if (*pos < 0)
		return NULL;

	/* pos == 0 means header */
	if (*pos == 0)
		return SEQ_START_TOKEN;

	/* Find the (pos-1)th ipc */
797
798
	for (p = *pos - 1; p <= ids->max_id; p++) {
		if ((ipc = ipc_lock(ids, p)) != NULL) {
799
800
801
802
803
804
805
806
807
808
809
			*pos = p + 1;
			return ipc;
		}
	}
	return NULL;
}

static void sysvipc_proc_stop(struct seq_file *s, void *it)
{
	struct kern_ipc_perm *ipc = it;
	struct ipc_proc_iface *iface = s->private;
810
	struct ipc_ids *ids;
811
812
813
814
815

	/* If we had a locked segment, release it */
	if (ipc && ipc != SEQ_START_TOKEN)
		ipc_unlock(ipc);

816
	ids = current->nsproxy->ipc_ns->ids[iface->ids];
817
	/* Release the lock we took in start() */
818
	mutex_unlock(&ids->mutex);
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
}

static int sysvipc_proc_show(struct seq_file *s, void *it)
{
	struct ipc_proc_iface *iface = s->private;

	if (it == SEQ_START_TOKEN)
		return seq_puts(s, iface->header);

	return iface->show(s, it);
}

static struct seq_operations sysvipc_proc_seqops = {
	.start = sysvipc_proc_start,
	.stop  = sysvipc_proc_stop,
	.next  = sysvipc_proc_next,
	.show  = sysvipc_proc_show,
};

static int sysvipc_proc_open(struct inode *inode, struct file *file) {
	int ret;
	struct seq_file *seq;

	ret = seq_open(file, &sysvipc_proc_seqops);
	if (!ret) {
		seq = file->private_data;
		seq->private = PDE(inode)->data;
	}
	return ret;
}

static struct file_operations sysvipc_proc_fops = {
	.open    = sysvipc_proc_open,
	.read    = seq_read,
	.llseek  = seq_lseek,
	.release = seq_release,
};
#endif /* CONFIG_PROC_FS */