Blame view

ipc/shm.c 25.7 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * linux/ipc/shm.c
   * Copyright (C) 1992, 1993 Krishna Balasubramanian
   *	 Many improvements/fixes by Bruno Haible.
   * Replaced `struct shm_desc' by `struct vm_area_struct', July 1994.
   * Fixed the shm swap deallocation (shm_unuse()), August 1998 Andrea Arcangeli.
   *
   * /proc/sysvipc/shm support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
   * BIGMEM support, Andrea Arcangeli <andrea@suse.de>
   * SMP thread shm, Jean-Luc Boyard <jean-luc.boyard@siemens.fr>
   * HIGHMEM support, Ingo Molnar <mingo@redhat.com>
   * Make shmmax, shmall, shmmni sysctl'able, Christoph Rohland <cr@sap.com>
   * Shared /dev/zero support, Kanoj Sarcar <kanoj@sgi.com>
   * Move the mm functionality over to mm/shmem.c, Christoph Rohland <cr@sap.com>
   *
073115d6b   Steve Grubb   [PATCH] Rework of...
16
17
   * support for audit of ipc object properties and permission changes
   * Dustin Kirkland <dustin.kirkland@us.ibm.com>
4e9823111   Kirill Korotaev   [PATCH] IPC names...
18
19
20
21
   *
   * namespaces support
   * OpenVZ, SWsoft Inc.
   * Pavel Emelianov <xemul@openvz.org>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
23
24
25
26
27
28
29
  #include <linux/slab.h>
  #include <linux/mm.h>
  #include <linux/hugetlb.h>
  #include <linux/shm.h>
  #include <linux/init.h>
  #include <linux/file.h>
  #include <linux/mman.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
33
  #include <linux/shmem_fs.h>
  #include <linux/security.h>
  #include <linux/syscalls.h>
  #include <linux/audit.h>
c59ede7b7   Randy.Dunlap   [PATCH] move capa...
34
  #include <linux/capability.h>
7d87e14c2   Stephen Rothwell   [PATCH] consolida...
35
  #include <linux/ptrace.h>
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
36
  #include <linux/seq_file.h>
5f921ae96   Ingo Molnar   [PATCH] sem2mutex...
37
  #include <linux/mutex.h>
4e9823111   Kirill Korotaev   [PATCH] IPC names...
38
  #include <linux/nsproxy.h>
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
39
  #include <linux/mount.h>
7d87e14c2   Stephen Rothwell   [PATCH] consolida...
40

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
43
  #include <asm/uaccess.h>
  
  #include "util.h"
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
44
45
46
47
48
49
50
51
  struct shm_file_data {
  	int id;
  	struct ipc_namespace *ns;
  	struct file *file;
  	const struct vm_operations_struct *vm_ops;
  };
  
  #define shm_file_data(file) (*((struct shm_file_data **)&(file)->private_data))
9a32144e9   Arjan van de Ven   [PATCH] mark stru...
52
  static const struct file_operations shm_file_operations;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
53
  static struct vm_operations_struct shm_vm_ops;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
54
55
56
  static struct ipc_ids init_shm_ids;
  
  #define shm_ids(ns)	(*((ns)->ids[IPC_SHM_IDS]))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
57

4e9823111   Kirill Korotaev   [PATCH] IPC names...
58
59
60
61
62
63
64
65
  #define shm_lock(ns, id)		\
  	((struct shmid_kernel*)ipc_lock(&shm_ids(ns),id))
  #define shm_unlock(shp)			\
  	ipc_unlock(&(shp)->shm_perm)
  #define shm_get(ns, id)			\
  	((struct shmid_kernel*)ipc_get(&shm_ids(ns),id))
  #define shm_buildid(ns, id, seq)	\
  	ipc_buildid(&shm_ids(ns), id, seq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66

4e9823111   Kirill Korotaev   [PATCH] IPC names...
67
68
  static int newseg (struct ipc_namespace *ns, key_t key,
  		int shmflg, size_t size);
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
69
70
  static void shm_open(struct vm_area_struct *vma);
  static void shm_close(struct vm_area_struct *vma);
4e9823111   Kirill Korotaev   [PATCH] IPC names...
71
  static void shm_destroy (struct ipc_namespace *ns, struct shmid_kernel *shp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72
  #ifdef CONFIG_PROC_FS
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
73
  static int sysvipc_shm_proc_show(struct seq_file *s, void *it);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
74
  #endif
4e9823111   Kirill Korotaev   [PATCH] IPC names...
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
  static void __ipc_init __shm_init_ns(struct ipc_namespace *ns, struct ipc_ids *ids)
  {
  	ns->ids[IPC_SHM_IDS] = ids;
  	ns->shm_ctlmax = SHMMAX;
  	ns->shm_ctlall = SHMALL;
  	ns->shm_ctlmni = SHMMNI;
  	ns->shm_tot = 0;
  	ipc_init_ids(ids, 1);
  }
  
  static void do_shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *shp)
  {
  	if (shp->shm_nattch){
  		shp->shm_perm.mode |= SHM_DEST;
  		/* Do not find it any more */
  		shp->shm_perm.key = IPC_PRIVATE;
  		shm_unlock(shp);
  	} else
  		shm_destroy(ns, shp);
  }
  
  #ifdef CONFIG_IPC_NS
  int shm_init_ns(struct ipc_namespace *ns)
  {
  	struct ipc_ids *ids;
  
  	ids = kmalloc(sizeof(struct ipc_ids), GFP_KERNEL);
  	if (ids == NULL)
  		return -ENOMEM;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104

4e9823111   Kirill Korotaev   [PATCH] IPC names...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  	__shm_init_ns(ns, ids);
  	return 0;
  }
  
  void shm_exit_ns(struct ipc_namespace *ns)
  {
  	int i;
  	struct shmid_kernel *shp;
  
  	mutex_lock(&shm_ids(ns).mutex);
  	for (i = 0; i <= shm_ids(ns).max_id; i++) {
  		shp = shm_lock(ns, i);
  		if (shp == NULL)
  			continue;
  
  		do_shm_rmid(ns, shp);
  	}
  	mutex_unlock(&shm_ids(ns).mutex);
c7e12b838   Pavel Emelianov   [PATCH] Fix ipc e...
123
  	ipc_fini_ids(ns->ids[IPC_SHM_IDS]);
4e9823111   Kirill Korotaev   [PATCH] IPC names...
124
125
126
127
  	kfree(ns->ids[IPC_SHM_IDS]);
  	ns->ids[IPC_SHM_IDS] = NULL;
  }
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
  
  void __init shm_init (void)
  {
4e9823111   Kirill Korotaev   [PATCH] IPC names...
131
  	__shm_init_ns(&init_ipc_ns, &init_shm_ids);
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
132
133
134
  	ipc_init_proc_interface("sysvipc/shm",
  				"       key      shmid perms       size  cpid  lpid nattch   uid   gid  cuid  cgid      atime      dtime      ctime
  ",
4e9823111   Kirill Korotaev   [PATCH] IPC names...
135
  				IPC_SHM_IDS, sysvipc_shm_proc_show);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
  }
4e9823111   Kirill Korotaev   [PATCH] IPC names...
137
138
  static inline int shm_checkid(struct ipc_namespace *ns,
  		struct shmid_kernel *s, int id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
139
  {
4e9823111   Kirill Korotaev   [PATCH] IPC names...
140
  	if (ipc_checkid(&shm_ids(ns), &s->shm_perm, id))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
  		return -EIDRM;
  	return 0;
  }
4e9823111   Kirill Korotaev   [PATCH] IPC names...
144
  static inline struct shmid_kernel *shm_rmid(struct ipc_namespace *ns, int id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
145
  {
4e9823111   Kirill Korotaev   [PATCH] IPC names...
146
  	return (struct shmid_kernel *)ipc_rmid(&shm_ids(ns), id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
147
  }
4e9823111   Kirill Korotaev   [PATCH] IPC names...
148
  static inline int shm_addid(struct ipc_namespace *ns, struct shmid_kernel *shp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
  {
4e9823111   Kirill Korotaev   [PATCH] IPC names...
150
  	return ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
  }
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
152
153
  /* This is called by fork, once for every shm attach. */
  static void shm_open(struct vm_area_struct *vma)
4e9823111   Kirill Korotaev   [PATCH] IPC names...
154
  {
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
155
156
  	struct file *file = vma->vm_file;
  	struct shm_file_data *sfd = shm_file_data(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
  	struct shmid_kernel *shp;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
158
  	shp = shm_lock(sfd->ns, sfd->id);
9ba025f10   Eric Sesterhenn   BUG_ON() Conversi...
159
  	BUG_ON(!shp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
160
161
162
163
164
  	shp->shm_atim = get_seconds();
  	shp->shm_lprid = current->tgid;
  	shp->shm_nattch++;
  	shm_unlock(shp);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
166
167
168
169
  /*
   * shm_destroy - free the struct shmid_kernel
   *
   * @shp: struct to free
   *
5f921ae96   Ingo Molnar   [PATCH] sem2mutex...
170
   * It has to be called with shp and shm_ids.mutex locked,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
   * but returns with shp unlocked and freed.
   */
4e9823111   Kirill Korotaev   [PATCH] IPC names...
173
  static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
174
  {
4e9823111   Kirill Korotaev   [PATCH] IPC names...
175
176
  	ns->shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT;
  	shm_rmid(ns, shp->id);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177
178
179
180
  	shm_unlock(shp);
  	if (!is_file_hugepages(shp->shm_file))
  		shmem_lock(shp->shm_file, 0, shp->mlock_user);
  	else
6d63079ad   Josef Sipek   [PATCH] struct pa...
181
  		user_shm_unlock(shp->shm_file->f_path.dentry->d_inode->i_size,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
182
183
184
185
186
187
188
  						shp->mlock_user);
  	fput (shp->shm_file);
  	security_shm_free(shp);
  	ipc_rcu_putref(shp);
  }
  
  /*
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
189
   * remove the attach descriptor vma.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
191
192
193
   * free memory for segment if it is marked destroyed.
   * The descriptor has already been removed from the current->mm->mmap list
   * and will later be kfree()d.
   */
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
194
  static void shm_close(struct vm_area_struct *vma)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
  {
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
196
197
  	struct file * file = vma->vm_file;
  	struct shm_file_data *sfd = shm_file_data(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
  	struct shmid_kernel *shp;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
199
  	struct ipc_namespace *ns = sfd->ns;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
200
201
  
  	mutex_lock(&shm_ids(ns).mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
  	/* remove from the list of attaches of the shm segment */
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
203
  	shp = shm_lock(ns, sfd->id);
9ba025f10   Eric Sesterhenn   BUG_ON() Conversi...
204
  	BUG_ON(!shp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
206
207
208
  	shp->shm_lprid = current->tgid;
  	shp->shm_dtim = get_seconds();
  	shp->shm_nattch--;
  	if(shp->shm_nattch == 0 &&
b33291c0b   Andrew Morton   [PATCH] ipc: expa...
209
  	   shp->shm_perm.mode & SHM_DEST)
4e9823111   Kirill Korotaev   [PATCH] IPC names...
210
  		shm_destroy(ns, shp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
212
  	else
  		shm_unlock(shp);
4e9823111   Kirill Korotaev   [PATCH] IPC names...
213
  	mutex_unlock(&shm_ids(ns).mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
  }
de01bad2f   Adrian Bunk   [PATCH] make ipc/...
215
216
  static struct page *shm_nopage(struct vm_area_struct *vma,
  			       unsigned long address, int *type)
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
  {
  	struct file *file = vma->vm_file;
  	struct shm_file_data *sfd = shm_file_data(file);
  
  	return sfd->vm_ops->nopage(vma, address, type);
  }
  
  #ifdef CONFIG_NUMA
  int shm_set_policy(struct vm_area_struct *vma, struct mempolicy *new)
  {
  	struct file *file = vma->vm_file;
  	struct shm_file_data *sfd = shm_file_data(file);
  	int err = 0;
  	if (sfd->vm_ops->set_policy)
  		err = sfd->vm_ops->set_policy(vma, new);
  	return err;
  }
  
  struct mempolicy *shm_get_policy(struct vm_area_struct *vma, unsigned long addr)
  {
  	struct file *file = vma->vm_file;
  	struct shm_file_data *sfd = shm_file_data(file);
  	struct mempolicy *pol = NULL;
  
  	if (sfd->vm_ops->get_policy)
  		pol = sfd->vm_ops->get_policy(vma, addr);
  	else
  		pol = vma->vm_policy;
  	return pol;
  }
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
249
  static int shm_mmap(struct file * file, struct vm_area_struct * vma)
  {
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
250
  	struct shm_file_data *sfd = shm_file_data(file);
b0e15190e   David Howells   [PATCH] NOMMU: Ma...
251
  	int ret;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
252
253
254
255
256
257
  	ret = sfd->file->f_op->mmap(sfd->file, vma);
  	if (ret != 0)
  		return ret;
  	sfd->vm_ops = vma->vm_ops;
  	vma->vm_ops = &shm_vm_ops;
  	shm_open(vma);
b0e15190e   David Howells   [PATCH] NOMMU: Ma...
258
259
  
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
  }
4e9823111   Kirill Korotaev   [PATCH] IPC names...
261
262
  static int shm_release(struct inode *ino, struct file *file)
  {
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
263
  	struct shm_file_data *sfd = shm_file_data(file);
4e9823111   Kirill Korotaev   [PATCH] IPC names...
264

bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
265
266
267
  	put_ipc_ns(sfd->ns);
  	shm_file_data(file) = NULL;
  	kfree(sfd);
4e9823111   Kirill Korotaev   [PATCH] IPC names...
268
269
  	return 0;
  }
516dffdcd   Adam Litke   [PATCH] Fix get_u...
270
271
272
273
274
275
276
277
278
279
280
  static int shm_fsync(struct file *file, struct dentry *dentry, int datasync)
  {
  	int (*fsync) (struct file *, struct dentry *, int datasync);
  	struct shm_file_data *sfd = shm_file_data(file);
  	int ret = -EINVAL;
  
  	fsync = sfd->file->f_op->fsync;
  	if (fsync)
  		ret = fsync(sfd->file, sfd->file->f_path.dentry, datasync);
  	return ret;
  }
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
281
282
283
284
285
  static unsigned long shm_get_unmapped_area(struct file *file,
  	unsigned long addr, unsigned long len, unsigned long pgoff,
  	unsigned long flags)
  {
  	struct shm_file_data *sfd = shm_file_data(file);
516dffdcd   Adam Litke   [PATCH] Fix get_u...
286
287
288
289
290
291
292
293
294
295
296
297
298
  	return get_unmapped_area(sfd->file, addr, len, pgoff, flags);
  }
  
  int is_file_shm_hugepages(struct file *file)
  {
  	int ret = 0;
  
  	if (file->f_op == &shm_file_operations) {
  		struct shm_file_data *sfd;
  		sfd = shm_file_data(file);
  		ret = is_file_hugepages(sfd->file);
  	}
  	return ret;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
299
  }
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
300

9a32144e9   Arjan van de Ven   [PATCH] mark stru...
301
  static const struct file_operations shm_file_operations = {
4e9823111   Kirill Korotaev   [PATCH] IPC names...
302
  	.mmap		= shm_mmap,
516dffdcd   Adam Litke   [PATCH] Fix get_u...
303
  	.fsync		= shm_fsync,
4e9823111   Kirill Korotaev   [PATCH] IPC names...
304
  	.release	= shm_release,
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
305
  	.get_unmapped_area	= shm_get_unmapped_area,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
306
307
308
309
310
  };
  
  static struct vm_operations_struct shm_vm_ops = {
  	.open	= shm_open,	/* callback for a new vm-area open */
  	.close	= shm_close,	/* callback for when the vm-area is released */
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
311
312
313
314
  	.nopage	= shm_nopage,
  #if defined(CONFIG_NUMA)
  	.set_policy = shm_set_policy,
  	.get_policy = shm_get_policy,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
315
316
  #endif
  };
4e9823111   Kirill Korotaev   [PATCH] IPC names...
317
  static int newseg (struct ipc_namespace *ns, key_t key, int shmflg, size_t size)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
318
319
320
321
322
323
324
  {
  	int error;
  	struct shmid_kernel *shp;
  	int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT;
  	struct file * file;
  	char name[13];
  	int id;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
325
  	if (size < SHMMIN || size > ns->shm_ctlmax)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
  		return -EINVAL;
f66d45e99   Guy Streeter   [PATCH] correct s...
327
  	if (ns->shm_tot + numpages > ns->shm_ctlall)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
328
329
330
331
332
333
334
  		return -ENOSPC;
  
  	shp = ipc_rcu_alloc(sizeof(*shp));
  	if (!shp)
  		return -ENOMEM;
  
  	shp->shm_perm.key = key;
b33291c0b   Andrew Morton   [PATCH] ipc: expa...
335
  	shp->shm_perm.mode = (shmflg & S_IRWXUGO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
336
337
338
339
340
341
342
343
344
345
346
347
348
349
  	shp->mlock_user = NULL;
  
  	shp->shm_perm.security = NULL;
  	error = security_shm_alloc(shp);
  	if (error) {
  		ipc_rcu_putref(shp);
  		return error;
  	}
  
  	if (shmflg & SHM_HUGETLB) {
  		/* hugetlb_zero_setup takes care of mlock user accounting */
  		file = hugetlb_zero_setup(size);
  		shp->mlock_user = current->user;
  	} else {
bf8f972d3   Badari Pulavarty   [PATCH] SHM_NORES...
350
351
352
353
354
355
356
357
  		int acctflag = VM_ACCOUNT;
  		/*
  		 * Do not allow no accounting for OVERCOMMIT_NEVER, even
  	 	 * if it's asked for.
  		 */
  		if  ((shmflg & SHM_NORESERVE) &&
  				sysctl_overcommit_memory != OVERCOMMIT_NEVER)
  			acctflag = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
358
  		sprintf (name, "SYSV%08x", key);
bf8f972d3   Badari Pulavarty   [PATCH] SHM_NORES...
359
  		file = shmem_file_setup(name, size, acctflag);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
360
361
362
363
364
365
  	}
  	error = PTR_ERR(file);
  	if (IS_ERR(file))
  		goto no_file;
  
  	error = -ENOSPC;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
366
  	id = shm_addid(ns, shp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
368
369
370
371
372
373
374
375
  	if(id == -1) 
  		goto no_id;
  
  	shp->shm_cprid = current->tgid;
  	shp->shm_lprid = 0;
  	shp->shm_atim = shp->shm_dtim = 0;
  	shp->shm_ctim = get_seconds();
  	shp->shm_segsz = size;
  	shp->shm_nattch = 0;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
376
  	shp->id = shm_buildid(ns, id, shp->shm_perm.seq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
377
  	shp->shm_file = file;
551110a94   Krishnakumar R   [PATCH] hugetlb: ...
378

4e9823111   Kirill Korotaev   [PATCH] IPC names...
379
  	ns->shm_tot += numpages;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
  	shm_unlock(shp);
  	return shp->id;
  
  no_id:
  	fput(file);
  no_file:
  	security_shm_free(shp);
  	ipc_rcu_putref(shp);
  	return error;
  }
  
  asmlinkage long sys_shmget (key_t key, size_t size, int shmflg)
  {
  	struct shmid_kernel *shp;
  	int err, id = 0;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
395
396
397
  	struct ipc_namespace *ns;
  
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
398

4e9823111   Kirill Korotaev   [PATCH] IPC names...
399
  	mutex_lock(&shm_ids(ns).mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
400
  	if (key == IPC_PRIVATE) {
4e9823111   Kirill Korotaev   [PATCH] IPC names...
401
402
  		err = newseg(ns, key, shmflg, size);
  	} else if ((id = ipc_findkey(&shm_ids(ns), key)) == -1) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
404
405
  		if (!(shmflg & IPC_CREAT))
  			err = -ENOENT;
  		else
4e9823111   Kirill Korotaev   [PATCH] IPC names...
406
  			err = newseg(ns, key, shmflg, size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
408
409
  	} else if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL)) {
  		err = -EEXIST;
  	} else {
4e9823111   Kirill Korotaev   [PATCH] IPC names...
410
  		shp = shm_lock(ns, id);
9ba025f10   Eric Sesterhenn   BUG_ON() Conversi...
411
  		BUG_ON(shp==NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
412
413
414
415
416
  		if (shp->shm_segsz < size)
  			err = -EINVAL;
  		else if (ipcperms(&shp->shm_perm, shmflg))
  			err = -EACCES;
  		else {
4e9823111   Kirill Korotaev   [PATCH] IPC names...
417
  			int shmid = shm_buildid(ns, id, shp->shm_perm.seq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
419
420
421
422
423
  			err = security_shm_associate(shp, shmflg);
  			if (!err)
  				err = shmid;
  		}
  		shm_unlock(shp);
  	}
4e9823111   Kirill Korotaev   [PATCH] IPC names...
424
  	mutex_unlock(&shm_ids(ns).mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  
  	return err;
  }
  
  static inline unsigned long copy_shmid_to_user(void __user *buf, struct shmid64_ds *in, int version)
  {
  	switch(version) {
  	case IPC_64:
  		return copy_to_user(buf, in, sizeof(*in));
  	case IPC_OLD:
  	    {
  		struct shmid_ds out;
  
  		ipc64_perm_to_ipc_perm(&in->shm_perm, &out.shm_perm);
  		out.shm_segsz	= in->shm_segsz;
  		out.shm_atime	= in->shm_atime;
  		out.shm_dtime	= in->shm_dtime;
  		out.shm_ctime	= in->shm_ctime;
  		out.shm_cpid	= in->shm_cpid;
  		out.shm_lpid	= in->shm_lpid;
  		out.shm_nattch	= in->shm_nattch;
  
  		return copy_to_user(buf, &out, sizeof(out));
  	    }
  	default:
  		return -EINVAL;
  	}
  }
  
  struct shm_setbuf {
  	uid_t	uid;
  	gid_t	gid;
  	mode_t	mode;
  };	
  
  static inline unsigned long copy_shmid_from_user(struct shm_setbuf *out, void __user *buf, int version)
  {
  	switch(version) {
  	case IPC_64:
  	    {
  		struct shmid64_ds tbuf;
  
  		if (copy_from_user(&tbuf, buf, sizeof(tbuf)))
  			return -EFAULT;
  
  		out->uid	= tbuf.shm_perm.uid;
  		out->gid	= tbuf.shm_perm.gid;
b33291c0b   Andrew Morton   [PATCH] ipc: expa...
472
  		out->mode	= tbuf.shm_perm.mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
473
474
475
476
477
478
479
480
481
482
483
484
  
  		return 0;
  	    }
  	case IPC_OLD:
  	    {
  		struct shmid_ds tbuf_old;
  
  		if (copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
  			return -EFAULT;
  
  		out->uid	= tbuf_old.shm_perm.uid;
  		out->gid	= tbuf_old.shm_perm.gid;
b33291c0b   Andrew Morton   [PATCH] ipc: expa...
485
  		out->mode	= tbuf_old.shm_perm.mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
516
517
518
  
  		return 0;
  	    }
  	default:
  		return -EINVAL;
  	}
  }
  
  static inline unsigned long copy_shminfo_to_user(void __user *buf, struct shminfo64 *in, int version)
  {
  	switch(version) {
  	case IPC_64:
  		return copy_to_user(buf, in, sizeof(*in));
  	case IPC_OLD:
  	    {
  		struct shminfo out;
  
  		if(in->shmmax > INT_MAX)
  			out.shmmax = INT_MAX;
  		else
  			out.shmmax = (int)in->shmmax;
  
  		out.shmmin	= in->shmmin;
  		out.shmmni	= in->shmmni;
  		out.shmseg	= in->shmseg;
  		out.shmall	= in->shmall; 
  
  		return copy_to_user(buf, &out, sizeof(out));
  	    }
  	default:
  		return -EINVAL;
  	}
  }
4e9823111   Kirill Korotaev   [PATCH] IPC names...
519
520
  static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss,
  		unsigned long *swp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
521
522
523
524
525
  {
  	int i;
  
  	*rss = 0;
  	*swp = 0;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
526
  	for (i = 0; i <= shm_ids(ns).max_id; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
527
528
  		struct shmid_kernel *shp;
  		struct inode *inode;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
529
  		shp = shm_get(ns, i);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
530
531
  		if(!shp)
  			continue;
6d63079ad   Josef Sipek   [PATCH] struct pa...
532
  		inode = shp->shm_file->f_path.dentry->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
  
  		if (is_file_hugepages(shp->shm_file)) {
  			struct address_space *mapping = inode->i_mapping;
  			*rss += (HPAGE_SIZE/PAGE_SIZE)*mapping->nrpages;
  		} else {
  			struct shmem_inode_info *info = SHMEM_I(inode);
  			spin_lock(&info->lock);
  			*rss += inode->i_mapping->nrpages;
  			*swp += info->swapped;
  			spin_unlock(&info->lock);
  		}
  	}
  }
  
  asmlinkage long sys_shmctl (int shmid, int cmd, struct shmid_ds __user *buf)
  {
  	struct shm_setbuf setbuf;
  	struct shmid_kernel *shp;
  	int err, version;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
552
  	struct ipc_namespace *ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
554
555
556
557
558
559
  
  	if (cmd < 0 || shmid < 0) {
  		err = -EINVAL;
  		goto out;
  	}
  
  	version = ipc_parse_version(&cmd);
4e9823111   Kirill Korotaev   [PATCH] IPC names...
560
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
562
563
564
565
566
567
568
569
570
571
  
  	switch (cmd) { /* replace with proc interface ? */
  	case IPC_INFO:
  	{
  		struct shminfo64 shminfo;
  
  		err = security_shm_shmctl(NULL, cmd);
  		if (err)
  			return err;
  
  		memset(&shminfo,0,sizeof(shminfo));
4e9823111   Kirill Korotaev   [PATCH] IPC names...
572
573
574
  		shminfo.shmmni = shminfo.shmseg = ns->shm_ctlmni;
  		shminfo.shmmax = ns->shm_ctlmax;
  		shminfo.shmall = ns->shm_ctlall;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
575
576
577
578
579
  
  		shminfo.shmmin = SHMMIN;
  		if(copy_shminfo_to_user (buf, &shminfo, version))
  			return -EFAULT;
  		/* reading a integer is always atomic */
4e9823111   Kirill Korotaev   [PATCH] IPC names...
580
  		err= shm_ids(ns).max_id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581
582
583
584
585
586
587
588
589
590
591
592
593
  		if(err<0)
  			err = 0;
  		goto out;
  	}
  	case SHM_INFO:
  	{
  		struct shm_info shm_info;
  
  		err = security_shm_shmctl(NULL, cmd);
  		if (err)
  			return err;
  
  		memset(&shm_info,0,sizeof(shm_info));
4e9823111   Kirill Korotaev   [PATCH] IPC names...
594
595
596
597
  		mutex_lock(&shm_ids(ns).mutex);
  		shm_info.used_ids = shm_ids(ns).in_use;
  		shm_get_stat (ns, &shm_info.shm_rss, &shm_info.shm_swp);
  		shm_info.shm_tot = ns->shm_tot;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
599
  		shm_info.swap_attempts = 0;
  		shm_info.swap_successes = 0;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
600
601
  		err = shm_ids(ns).max_id;
  		mutex_unlock(&shm_ids(ns).mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
602
603
604
605
606
607
608
609
610
611
612
613
614
615
  		if(copy_to_user (buf, &shm_info, sizeof(shm_info))) {
  			err = -EFAULT;
  			goto out;
  		}
  
  		err = err < 0 ? 0 : err;
  		goto out;
  	}
  	case SHM_STAT:
  	case IPC_STAT:
  	{
  		struct shmid64_ds tbuf;
  		int result;
  		memset(&tbuf, 0, sizeof(tbuf));
4e9823111   Kirill Korotaev   [PATCH] IPC names...
616
  		shp = shm_lock(ns, shmid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
617
618
619
620
621
  		if(shp==NULL) {
  			err = -EINVAL;
  			goto out;
  		} else if(cmd==SHM_STAT) {
  			err = -EINVAL;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
622
  			if (shmid > shm_ids(ns).max_id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
623
  				goto out_unlock;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
624
  			result = shm_buildid(ns, shmid, shp->shm_perm.seq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
625
  		} else {
4e9823111   Kirill Korotaev   [PATCH] IPC names...
626
  			err = shm_checkid(ns, shp,shmid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
  			if(err)
  				goto out_unlock;
  			result = 0;
  		}
  		err=-EACCES;
  		if (ipcperms (&shp->shm_perm, S_IRUGO))
  			goto out_unlock;
  		err = security_shm_shmctl(shp, cmd);
  		if (err)
  			goto out_unlock;
  		kernel_to_ipc64_perm(&shp->shm_perm, &tbuf.shm_perm);
  		tbuf.shm_segsz	= shp->shm_segsz;
  		tbuf.shm_atime	= shp->shm_atim;
  		tbuf.shm_dtime	= shp->shm_dtim;
  		tbuf.shm_ctime	= shp->shm_ctim;
  		tbuf.shm_cpid	= shp->shm_cprid;
  		tbuf.shm_lpid	= shp->shm_lprid;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
644
  		tbuf.shm_nattch	= shp->shm_nattch;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
645
646
647
648
649
650
651
652
653
654
  		shm_unlock(shp);
  		if(copy_shmid_to_user (buf, &tbuf, version))
  			err = -EFAULT;
  		else
  			err = result;
  		goto out;
  	}
  	case SHM_LOCK:
  	case SHM_UNLOCK:
  	{
4e9823111   Kirill Korotaev   [PATCH] IPC names...
655
  		shp = shm_lock(ns, shmid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
656
657
658
659
  		if(shp==NULL) {
  			err = -EINVAL;
  			goto out;
  		}
4e9823111   Kirill Korotaev   [PATCH] IPC names...
660
  		err = shm_checkid(ns, shp,shmid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
661
662
  		if(err)
  			goto out_unlock;
073115d6b   Steve Grubb   [PATCH] Rework of...
663
664
665
  		err = audit_ipc_obj(&(shp->shm_perm));
  		if (err)
  			goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
  		if (!capable(CAP_IPC_LOCK)) {
  			err = -EPERM;
  			if (current->euid != shp->shm_perm.uid &&
  			    current->euid != shp->shm_perm.cuid)
  				goto out_unlock;
  			if (cmd == SHM_LOCK &&
  			    !current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur)
  				goto out_unlock;
  		}
  
  		err = security_shm_shmctl(shp, cmd);
  		if (err)
  			goto out_unlock;
  		
  		if(cmd==SHM_LOCK) {
  			struct user_struct * user = current->user;
  			if (!is_file_hugepages(shp->shm_file)) {
  				err = shmem_lock(shp->shm_file, 1, user);
  				if (!err) {
b33291c0b   Andrew Morton   [PATCH] ipc: expa...
685
  					shp->shm_perm.mode |= SHM_LOCKED;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
686
687
688
689
690
  					shp->mlock_user = user;
  				}
  			}
  		} else if (!is_file_hugepages(shp->shm_file)) {
  			shmem_lock(shp->shm_file, 0, shp->mlock_user);
b33291c0b   Andrew Morton   [PATCH] ipc: expa...
691
  			shp->shm_perm.mode &= ~SHM_LOCKED;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
  			shp->mlock_user = NULL;
  		}
  		shm_unlock(shp);
  		goto out;
  	}
  	case IPC_RMID:
  	{
  		/*
  		 *	We cannot simply remove the file. The SVID states
  		 *	that the block remains until the last person
  		 *	detaches from it, then is deleted. A shmat() on
  		 *	an RMID segment is legal in older Linux and if 
  		 *	we change it apps break...
  		 *
  		 *	Instead we set a destroyed flag, and then blow
  		 *	the name away when the usage hits zero.
  		 */
4e9823111   Kirill Korotaev   [PATCH] IPC names...
709
710
  		mutex_lock(&shm_ids(ns).mutex);
  		shp = shm_lock(ns, shmid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
711
712
713
  		err = -EINVAL;
  		if (shp == NULL) 
  			goto out_up;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
714
  		err = shm_checkid(ns, shp, shmid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
715
716
  		if(err)
  			goto out_unlock_up;
073115d6b   Steve Grubb   [PATCH] Rework of...
717
718
719
  		err = audit_ipc_obj(&(shp->shm_perm));
  		if (err)
  			goto out_unlock_up;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
720
721
722
723
724
725
726
727
728
729
  		if (current->euid != shp->shm_perm.uid &&
  		    current->euid != shp->shm_perm.cuid && 
  		    !capable(CAP_SYS_ADMIN)) {
  			err=-EPERM;
  			goto out_unlock_up;
  		}
  
  		err = security_shm_shmctl(shp, cmd);
  		if (err)
  			goto out_unlock_up;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
730
731
  		do_shm_rmid(ns, shp);
  		mutex_unlock(&shm_ids(ns).mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
732
733
734
735
736
737
738
739
740
  		goto out;
  	}
  
  	case IPC_SET:
  	{
  		if (copy_shmid_from_user (&setbuf, buf, version)) {
  			err = -EFAULT;
  			goto out;
  		}
4e9823111   Kirill Korotaev   [PATCH] IPC names...
741
742
  		mutex_lock(&shm_ids(ns).mutex);
  		shp = shm_lock(ns, shmid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
743
744
745
  		err=-EINVAL;
  		if(shp==NULL)
  			goto out_up;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
746
  		err = shm_checkid(ns, shp,shmid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
747
748
  		if(err)
  			goto out_unlock_up;
073115d6b   Steve Grubb   [PATCH] Rework of...
749
750
751
  		err = audit_ipc_obj(&(shp->shm_perm));
  		if (err)
  			goto out_unlock_up;
ac03221a4   Linda Knippers   [PATCH] update of...
752
  		err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode);
073115d6b   Steve Grubb   [PATCH] Rework of...
753
754
  		if (err)
  			goto out_unlock_up;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
755
756
757
758
759
760
761
762
763
764
765
766
767
  		err=-EPERM;
  		if (current->euid != shp->shm_perm.uid &&
  		    current->euid != shp->shm_perm.cuid && 
  		    !capable(CAP_SYS_ADMIN)) {
  			goto out_unlock_up;
  		}
  
  		err = security_shm_shmctl(shp, cmd);
  		if (err)
  			goto out_unlock_up;
  		
  		shp->shm_perm.uid = setbuf.uid;
  		shp->shm_perm.gid = setbuf.gid;
b33291c0b   Andrew Morton   [PATCH] ipc: expa...
768
  		shp->shm_perm.mode = (shp->shm_perm.mode & ~S_IRWXUGO)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
769
770
771
772
773
774
775
776
777
778
779
780
781
782
  			| (setbuf.mode & S_IRWXUGO);
  		shp->shm_ctim = get_seconds();
  		break;
  	}
  
  	default:
  		err = -EINVAL;
  		goto out;
  	}
  
  	err = 0;
  out_unlock_up:
  	shm_unlock(shp);
  out_up:
4e9823111   Kirill Korotaev   [PATCH] IPC names...
783
  	mutex_unlock(&shm_ids(ns).mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
  	goto out;
  out_unlock:
  	shm_unlock(shp);
  out:
  	return err;
  }
  
  /*
   * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists.
   *
   * NOTE! Despite the name, this is NOT a direct system call entrypoint. The
   * "raddr" thing points to kernel space, and there has to be a wrapper around
   * this.
   */
  long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr)
  {
  	struct shmid_kernel *shp;
  	unsigned long addr;
  	unsigned long size;
  	struct file * file;
  	int    err;
  	unsigned long flags;
  	unsigned long prot;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
807
  	int acc_mode;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
808
  	unsigned long user_addr;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
809
  	struct ipc_namespace *ns;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
810
811
812
  	struct shm_file_data *sfd;
  	struct path path;
  	mode_t f_mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
813

bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
814
815
  	err = -EINVAL;
  	if (shmid < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
816
  		goto out;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
817
  	else if ((addr = (ulong)shmaddr)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
818
819
820
821
822
823
824
  		if (addr & (SHMLBA-1)) {
  			if (shmflg & SHM_RND)
  				addr &= ~(SHMLBA-1);	   /* round down */
  			else
  #ifndef __ARCH_FORCE_SHMLBA
  				if (addr & ~PAGE_MASK)
  #endif
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
825
  					goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
826
827
828
829
  		}
  		flags = MAP_SHARED | MAP_FIXED;
  	} else {
  		if ((shmflg & SHM_REMAP))
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
830
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
831
832
833
834
835
836
  
  		flags = MAP_SHARED;
  	}
  
  	if (shmflg & SHM_RDONLY) {
  		prot = PROT_READ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
837
  		acc_mode = S_IRUGO;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
838
  		f_mode = FMODE_READ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
839
840
  	} else {
  		prot = PROT_READ | PROT_WRITE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
841
  		acc_mode = S_IRUGO | S_IWUGO;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
842
  		f_mode = FMODE_READ | FMODE_WRITE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
843
844
845
846
847
848
849
850
851
852
  	}
  	if (shmflg & SHM_EXEC) {
  		prot |= PROT_EXEC;
  		acc_mode |= S_IXUGO;
  	}
  
  	/*
  	 * We cannot rely on the fs check since SYSV IPC does have an
  	 * additional creator id...
  	 */
4e9823111   Kirill Korotaev   [PATCH] IPC names...
853
854
  	ns = current->nsproxy->ipc_ns;
  	shp = shm_lock(ns, shmid);
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
855
  	if(shp == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
856
  		goto out;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
857

4e9823111   Kirill Korotaev   [PATCH] IPC names...
858
  	err = shm_checkid(ns, shp,shmid);
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
859
860
861
862
863
864
  	if (err)
  		goto out_unlock;
  
  	err = -EACCES;
  	if (ipcperms(&shp->shm_perm, acc_mode))
  		goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
865
866
  
  	err = security_shm_shmat(shp, shmaddr, shmflg);
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
867
868
869
870
871
  	if (err)
  		goto out_unlock;
  
  	path.dentry = dget(shp->shm_file->f_path.dentry);
  	path.mnt    = mntget(shp->shm_file->f_path.mnt);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
872
  	shp->shm_nattch++;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
873
  	size = i_size_read(path.dentry->d_inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
874
  	shm_unlock(shp);
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
  	err = -ENOMEM;
  	sfd = kzalloc(sizeof(*sfd), GFP_KERNEL);
  	if (!sfd)
  		goto out_put_path;
  
  	err = -ENOMEM;
  	file = get_empty_filp();
  	if (!file)
  		goto out_free;
  
  	file->f_op = &shm_file_operations;
  	file->private_data = sfd;
  	file->f_path = path;
  	file->f_mapping = shp->shm_file->f_mapping;
  	file->f_mode = f_mode;
  	sfd->id = shp->id;
  	sfd->ns = get_ipc_ns(ns);
  	sfd->file = shp->shm_file;
  	sfd->vm_ops = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
894
895
  	down_write(&current->mm->mmap_sem);
  	if (addr && !(shmflg & SHM_REMAP)) {
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
896
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
897
898
899
900
901
902
903
904
905
906
907
  		if (find_vma_intersection(current->mm, addr, addr + size))
  			goto invalid;
  		/*
  		 * If shm segment goes below stack, make sure there is some
  		 * space left for the stack to grow (at least 4 pages).
  		 */
  		if (addr < current->mm->start_stack &&
  		    addr > current->mm->start_stack - size - PAGE_SIZE * 5)
  			goto invalid;
  	}
  		
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
908
909
910
911
912
  	user_addr = do_mmap (file, addr, size, prot, flags, 0);
  	*raddr = user_addr;
  	err = 0;
  	if (IS_ERR_VALUE(user_addr))
  		err = (long)user_addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
913
914
  invalid:
  	up_write(&current->mm->mmap_sem);
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
915
916
917
  	fput(file);
  
  out_nattch:
4e9823111   Kirill Korotaev   [PATCH] IPC names...
918
919
  	mutex_lock(&shm_ids(ns).mutex);
  	shp = shm_lock(ns, shmid);
9ba025f10   Eric Sesterhenn   BUG_ON() Conversi...
920
  	BUG_ON(!shp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
921
922
  	shp->shm_nattch--;
  	if(shp->shm_nattch == 0 &&
b33291c0b   Andrew Morton   [PATCH] ipc: expa...
923
  	   shp->shm_perm.mode & SHM_DEST)
4e9823111   Kirill Korotaev   [PATCH] IPC names...
924
  		shm_destroy(ns, shp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
925
926
  	else
  		shm_unlock(shp);
4e9823111   Kirill Korotaev   [PATCH] IPC names...
927
  	mutex_unlock(&shm_ids(ns).mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
928

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
929
930
  out:
  	return err;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
931
932
933
934
935
936
937
938
939
940
941
  
  out_unlock:
  	shm_unlock(shp);
  	goto out;
  
  out_free:
  	kfree(sfd);
  out_put_path:
  	dput(path.dentry);
  	mntput(path.mnt);
  	goto out_nattch;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
942
  }
7d87e14c2   Stephen Rothwell   [PATCH] consolida...
943
944
945
946
947
948
949
950
951
952
953
  asmlinkage long sys_shmat(int shmid, char __user *shmaddr, int shmflg)
  {
  	unsigned long ret;
  	long err;
  
  	err = do_shmat(shmid, shmaddr, shmflg, &ret);
  	if (err)
  		return err;
  	force_successful_syscall_return();
  	return (long)ret;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
954
955
956
957
958
959
960
961
962
963
964
  /*
   * detach and kill segment if marked destroyed.
   * The work is done in shm_close.
   */
  asmlinkage long sys_shmdt(char __user *shmaddr)
  {
  	struct mm_struct *mm = current->mm;
  	struct vm_area_struct *vma, *next;
  	unsigned long addr = (unsigned long)shmaddr;
  	loff_t size = 0;
  	int retval = -EINVAL;
df1e2fb54   Hugh Dickins   [PATCH] shmdt: ch...
965
966
  	if (addr & ~PAGE_MASK)
  		return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
  	down_write(&mm->mmap_sem);
  
  	/*
  	 * This function tries to be smart and unmap shm segments that
  	 * were modified by partial mlock or munmap calls:
  	 * - It first determines the size of the shm segment that should be
  	 *   unmapped: It searches for a vma that is backed by shm and that
  	 *   started at address shmaddr. It records it's size and then unmaps
  	 *   it.
  	 * - Then it unmaps all shm vmas that started at shmaddr and that
  	 *   are within the initially determined size.
  	 * Errors from do_munmap are ignored: the function only fails if
  	 * it's called with invalid parameters or if it's called to unmap
  	 * a part of a vma. Both calls in this function are for full vmas,
  	 * the parameters are directly copied from the vma itself and always
  	 * valid - therefore do_munmap cannot fail. (famous last words?)
  	 */
  	/*
  	 * If it had been mremap()'d, the starting address would not
  	 * match the usual checks anyway. So assume all vma's are
  	 * above the starting address given.
  	 */
  	vma = find_vma(mm, addr);
  
  	while (vma) {
  		next = vma->vm_next;
  
  		/*
  		 * Check if the starting address would match, i.e. it's
  		 * a fragment created by mprotect() and/or munmap(), or it
  		 * otherwise it starts at this address with no hassles.
  		 */
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
999
  		if ((vma->vm_ops == &shm_vm_ops) &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1000
  			(vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff) {
6d63079ad   Josef Sipek   [PATCH] struct pa...
1001
  			size = vma->vm_file->f_path.dentry->d_inode->i_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
  			do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start);
  			/*
  			 * We discovered the size of the shm segment, so
  			 * break out of here and fall through to the next
  			 * loop that uses the size information to stop
  			 * searching for matching vma's.
  			 */
  			retval = 0;
  			vma = next;
  			break;
  		}
  		vma = next;
  	}
  
  	/*
  	 * We need look no further than the maximum address a fragment
  	 * could possibly have landed at. Also cast things to loff_t to
  	 * prevent overflows and make comparisions vs. equal-width types.
  	 */
8e36709d8   KAMEZAWA Hiroyuki   [PATCH] shmdt can...
1021
  	size = PAGE_ALIGN(size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1022
1023
1024
1025
  	while (vma && (loff_t)(vma->vm_end - addr) <= size) {
  		next = vma->vm_next;
  
  		/* finding a matching vma now does not alter retval */
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
1026
  		if ((vma->vm_ops == &shm_vm_ops) &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
  			(vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff)
  
  			do_munmap(mm, vma->vm_start, vma->vm_end - vma->vm_start);
  		vma = next;
  	}
  
  	up_write(&mm->mmap_sem);
  	return retval;
  }
  
  #ifdef CONFIG_PROC_FS
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1038
  static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1039
  {
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1040
1041
  	struct shmid_kernel *shp = it;
  	char *format;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1042

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1043
1044
1045
1046
  #define SMALL_STRING "%10d %10d  %4o %10u %5u %5u  %5d %5u %5u %5u %5u %10lu %10lu %10lu
  "
  #define BIG_STRING   "%10d %10d  %4o %21u %5u %5u  %5d %5u %5u %5u %5u %10lu %10lu %10lu
  "
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1047

19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1048
1049
1050
1051
1052
1053
1054
  	if (sizeof(size_t) <= sizeof(int))
  		format = SMALL_STRING;
  	else
  		format = BIG_STRING;
  	return seq_printf(s, format,
  			  shp->shm_perm.key,
  			  shp->id,
b33291c0b   Andrew Morton   [PATCH] ipc: expa...
1055
  			  shp->shm_perm.mode,
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1056
1057
1058
  			  shp->shm_segsz,
  			  shp->shm_cprid,
  			  shp->shm_lprid,
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
1059
  			  shp->shm_nattch,
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1060
1061
1062
1063
1064
1065
1066
  			  shp->shm_perm.uid,
  			  shp->shm_perm.gid,
  			  shp->shm_perm.cuid,
  			  shp->shm_perm.cgid,
  			  shp->shm_atim,
  			  shp->shm_dtim,
  			  shp->shm_ctim);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1067
1068
  }
  #endif