Blame view

ipc/shm.c 27.1 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>
3e148c799   Nadia Derbey   fix idr_find() lo...
37
  #include <linux/rwsem.h>
4e9823111   Kirill Korotaev   [PATCH] IPC names...
38
  #include <linux/nsproxy.h>
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
39
  #include <linux/mount.h>
ae5e1b22f   Pavel Emelyanov   namespaces: move ...
40
  #include <linux/ipc_namespace.h>
7d87e14c2   Stephen Rothwell   [PATCH] consolida...
41

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
44
  #include <asm/uaccess.h>
  
  #include "util.h"
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
45
46
47
48
49
50
51
52
  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...
53
  static const struct file_operations shm_file_operations;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
54
  static struct vm_operations_struct shm_vm_ops;
ed2ddbf88   Pierre Peiffer   IPC: make struct ...
55
  #define shm_ids(ns)	((ns)->ids[IPC_SHM_IDS])
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56

4e9823111   Kirill Korotaev   [PATCH] IPC names...
57
58
  #define shm_unlock(shp)			\
  	ipc_unlock(&(shp)->shm_perm)
1b531f213   Nadia Derbey   ipc: remove unnee...
59
  #define shm_buildid(id, seq)	ipc_buildid(id, seq)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
60

7748dbfaa   Nadia Derbey   ipc: unify the sy...
61
  static int newseg(struct ipc_namespace *, struct ipc_params *);
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
62
63
  static void shm_open(struct vm_area_struct *vma);
  static void shm_close(struct vm_area_struct *vma);
4e9823111   Kirill Korotaev   [PATCH] IPC names...
64
  static void shm_destroy (struct ipc_namespace *ns, struct shmid_kernel *shp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
  #ifdef CONFIG_PROC_FS
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
66
  static int sysvipc_shm_proc_show(struct seq_file *s, void *it);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
67
  #endif
ed2ddbf88   Pierre Peiffer   IPC: make struct ...
68
  void shm_init_ns(struct ipc_namespace *ns)
4e9823111   Kirill Korotaev   [PATCH] IPC names...
69
  {
4e9823111   Kirill Korotaev   [PATCH] IPC names...
70
71
72
73
  	ns->shm_ctlmax = SHMMAX;
  	ns->shm_ctlall = SHMALL;
  	ns->shm_ctlmni = SHMMNI;
  	ns->shm_tot = 0;
ed2ddbf88   Pierre Peiffer   IPC: make struct ...
74
  	ipc_init_ids(&ns->ids[IPC_SHM_IDS]);
4e9823111   Kirill Korotaev   [PATCH] IPC names...
75
  }
f4566f048   Nadia Derbey   ipc: fix wrong co...
76
  /*
3e148c799   Nadia Derbey   fix idr_find() lo...
77
78
   * Called with shm_ids.rw_mutex (writer) and the shp structure locked.
   * Only shm_ids.rw_mutex remains locked on exit.
f4566f048   Nadia Derbey   ipc: fix wrong co...
79
   */
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
80
  static void do_shm_rmid(struct ipc_namespace *ns, struct kern_ipc_perm *ipcp)
4e9823111   Kirill Korotaev   [PATCH] IPC names...
81
  {
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
82
83
  	struct shmid_kernel *shp;
  	shp = container_of(ipcp, struct shmid_kernel, shm_perm);
4e9823111   Kirill Korotaev   [PATCH] IPC names...
84
85
86
87
88
89
90
91
  	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);
  }
ae5e1b22f   Pavel Emelyanov   namespaces: move ...
92
  #ifdef CONFIG_IPC_NS
4e9823111   Kirill Korotaev   [PATCH] IPC names...
93
94
  void shm_exit_ns(struct ipc_namespace *ns)
  {
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
95
  	free_ipcs(ns, &shm_ids(ns), do_shm_rmid);
4e9823111   Kirill Korotaev   [PATCH] IPC names...
96
  }
ae5e1b22f   Pavel Emelyanov   namespaces: move ...
97
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
98
99
100
  
  void __init shm_init (void)
  {
ed2ddbf88   Pierre Peiffer   IPC: make struct ...
101
  	shm_init_ns(&init_ipc_ns);
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
102
103
104
  	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...
105
  				IPC_SHM_IDS, sysvipc_shm_proc_show);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  }
3e148c799   Nadia Derbey   fix idr_find() lo...
107
108
109
110
111
112
113
114
  /*
   * shm_lock_(check_)down routines are called in the paths where the rw_mutex
   * is held to protect access to the idr tree.
   */
  static inline struct shmid_kernel *shm_lock_down(struct ipc_namespace *ns,
  						int id)
  {
  	struct kern_ipc_perm *ipcp = ipc_lock_down(&shm_ids(ns), id);
b1ed88b47   Pierre Peiffer   IPC: fix error ch...
115
116
  	if (IS_ERR(ipcp))
  		return (struct shmid_kernel *)ipcp;
3e148c799   Nadia Derbey   fix idr_find() lo...
117
118
119
120
121
122
123
124
  	return container_of(ipcp, struct shmid_kernel, shm_perm);
  }
  
  static inline struct shmid_kernel *shm_lock_check_down(
  						struct ipc_namespace *ns,
  						int id)
  {
  	struct kern_ipc_perm *ipcp = ipc_lock_check_down(&shm_ids(ns), id);
b1ed88b47   Pierre Peiffer   IPC: fix error ch...
125
126
  	if (IS_ERR(ipcp))
  		return (struct shmid_kernel *)ipcp;
3e148c799   Nadia Derbey   fix idr_find() lo...
127
128
129
130
131
132
133
  	return container_of(ipcp, struct shmid_kernel, shm_perm);
  }
  
  /*
   * shm_lock_(check_) routines are called in the paths where the rw_mutex
   * is not held.
   */
023a53557   Nadia Derbey   ipc: integrate ip...
134
  static inline struct shmid_kernel *shm_lock(struct ipc_namespace *ns, int id)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
  {
03f02c765   Nadia Derbey   Storing ipcs into...
136
  	struct kern_ipc_perm *ipcp = ipc_lock(&shm_ids(ns), id);
b1ed88b47   Pierre Peiffer   IPC: fix error ch...
137
138
  	if (IS_ERR(ipcp))
  		return (struct shmid_kernel *)ipcp;
03f02c765   Nadia Derbey   Storing ipcs into...
139
  	return container_of(ipcp, struct shmid_kernel, shm_perm);
023a53557   Nadia Derbey   ipc: integrate ip...
140
141
142
143
144
  }
  
  static inline struct shmid_kernel *shm_lock_check(struct ipc_namespace *ns,
  						int id)
  {
03f02c765   Nadia Derbey   Storing ipcs into...
145
  	struct kern_ipc_perm *ipcp = ipc_lock_check(&shm_ids(ns), id);
b1ed88b47   Pierre Peiffer   IPC: fix error ch...
146
147
  	if (IS_ERR(ipcp))
  		return (struct shmid_kernel *)ipcp;
03f02c765   Nadia Derbey   Storing ipcs into...
148
  	return container_of(ipcp, struct shmid_kernel, shm_perm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
149
  }
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
150
  static inline void shm_rmid(struct ipc_namespace *ns, struct shmid_kernel *s)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
  {
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
152
  	ipc_rmid(&shm_ids(ns), &s->shm_perm);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
153
  }
4e9823111   Kirill Korotaev   [PATCH] IPC names...
154
  static inline int shm_addid(struct ipc_namespace *ns, struct shmid_kernel *shp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
  {
4e9823111   Kirill Korotaev   [PATCH] IPC names...
156
  	return ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
157
  }
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
158
159
  /* 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...
160
  {
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
161
162
  	struct file *file = vma->vm_file;
  	struct shm_file_data *sfd = shm_file_data(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
  	struct shmid_kernel *shp;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
164
  	shp = shm_lock(sfd->ns, sfd->id);
023a53557   Nadia Derbey   ipc: integrate ip...
165
  	BUG_ON(IS_ERR(shp));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
  	shp->shm_atim = get_seconds();
b488893a3   Pavel Emelyanov   pid namespaces: c...
167
  	shp->shm_lprid = task_tgid_vnr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
168
169
170
  	shp->shm_nattch++;
  	shm_unlock(shp);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
173
  /*
   * shm_destroy - free the struct shmid_kernel
   *
f4566f048   Nadia Derbey   ipc: fix wrong co...
174
   * @ns: namespace
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
   * @shp: struct to free
   *
3e148c799   Nadia Derbey   fix idr_find() lo...
177
   * It has to be called with shp and shm_ids.rw_mutex (writer) locked,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
178
179
   * but returns with shp unlocked and freed.
   */
4e9823111   Kirill Korotaev   [PATCH] IPC names...
180
  static void shm_destroy(struct ipc_namespace *ns, struct shmid_kernel *shp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
181
  {
4e9823111   Kirill Korotaev   [PATCH] IPC names...
182
  	ns->shm_tot -= (shp->shm_segsz + PAGE_SIZE - 1) >> PAGE_SHIFT;
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
183
  	shm_rmid(ns, shp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
184
185
186
187
  	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...
188
  		user_shm_unlock(shp->shm_file->f_path.dentry->d_inode->i_size,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
190
191
192
193
194
195
  						shp->mlock_user);
  	fput (shp->shm_file);
  	security_shm_free(shp);
  	ipc_rcu_putref(shp);
  }
  
  /*
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
196
   * remove the attach descriptor vma.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
197
198
199
200
   * 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...
201
  static void shm_close(struct vm_area_struct *vma)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
202
  {
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
203
204
  	struct file * file = vma->vm_file;
  	struct shm_file_data *sfd = shm_file_data(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
205
  	struct shmid_kernel *shp;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
206
  	struct ipc_namespace *ns = sfd->ns;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
207

3e148c799   Nadia Derbey   fix idr_find() lo...
208
  	down_write(&shm_ids(ns).rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
209
  	/* remove from the list of attaches of the shm segment */
3e148c799   Nadia Derbey   fix idr_find() lo...
210
  	shp = shm_lock_down(ns, sfd->id);
023a53557   Nadia Derbey   ipc: integrate ip...
211
  	BUG_ON(IS_ERR(shp));
b488893a3   Pavel Emelyanov   pid namespaces: c...
212
  	shp->shm_lprid = task_tgid_vnr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
213
214
215
  	shp->shm_dtim = get_seconds();
  	shp->shm_nattch--;
  	if(shp->shm_nattch == 0 &&
b33291c0b   Andrew Morton   [PATCH] ipc: expa...
216
  	   shp->shm_perm.mode & SHM_DEST)
4e9823111   Kirill Korotaev   [PATCH] IPC names...
217
  		shm_destroy(ns, shp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
219
  	else
  		shm_unlock(shp);
3e148c799   Nadia Derbey   fix idr_find() lo...
220
  	up_write(&shm_ids(ns).rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
221
  }
d0217ac04   Nick Piggin   mm: fault feedbac...
222
  static int shm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
223
224
225
  {
  	struct file *file = vma->vm_file;
  	struct shm_file_data *sfd = shm_file_data(file);
d0217ac04   Nick Piggin   mm: fault feedbac...
226
  	return sfd->vm_ops->fault(vma, vmf);
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
227
228
229
  }
  
  #ifdef CONFIG_NUMA
d823e3e75   Adrian Bunk   ipc/shm.c: make 2...
230
  static int shm_set_policy(struct vm_area_struct *vma, struct mempolicy *new)
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
231
232
233
234
235
236
237
238
  {
  	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;
  }
d823e3e75   Adrian Bunk   ipc/shm.c: make 2...
239
240
  static struct mempolicy *shm_get_policy(struct vm_area_struct *vma,
  					unsigned long addr)
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
241
242
243
244
245
246
247
  {
  	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);
22741925d   Adam Litke   hugetlb: fix get_...
248
  	else if (vma->vm_policy)
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
249
  		pol = vma->vm_policy;
22741925d   Adam Litke   hugetlb: fix get_...
250
251
  	else
  		pol = current->mempolicy;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
252
253
254
  	return pol;
  }
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
255
256
  static int shm_mmap(struct file * file, struct vm_area_struct * vma)
  {
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
257
  	struct shm_file_data *sfd = shm_file_data(file);
b0e15190e   David Howells   [PATCH] NOMMU: Ma...
258
  	int ret;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
259
260
261
262
  	ret = sfd->file->f_op->mmap(sfd->file, vma);
  	if (ret != 0)
  		return ret;
  	sfd->vm_ops = vma->vm_ops;
2e92a3bae   David Howells   NOMMU: Fix SYSV I...
263
  #ifdef CONFIG_MMU
54cb8821d   Nick Piggin   mm: merge populat...
264
  	BUG_ON(!sfd->vm_ops->fault);
2e92a3bae   David Howells   NOMMU: Fix SYSV I...
265
  #endif
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
266
267
  	vma->vm_ops = &shm_vm_ops;
  	shm_open(vma);
b0e15190e   David Howells   [PATCH] NOMMU: Ma...
268
269
  
  	return ret;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
  }
4e9823111   Kirill Korotaev   [PATCH] IPC names...
271
272
  static int shm_release(struct inode *ino, struct file *file)
  {
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
273
  	struct shm_file_data *sfd = shm_file_data(file);
4e9823111   Kirill Korotaev   [PATCH] IPC names...
274

bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
275
276
277
  	put_ipc_ns(sfd->ns);
  	shm_file_data(file) = NULL;
  	kfree(sfd);
4e9823111   Kirill Korotaev   [PATCH] IPC names...
278
279
  	return 0;
  }
516dffdcd   Adam Litke   [PATCH] Fix get_u...
280
281
282
283
284
285
286
287
288
289
290
  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...
291
292
293
294
295
  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...
296
297
298
299
300
301
302
303
304
305
306
307
308
  	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...
309
  }
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
310

9a32144e9   Arjan van de Ven   [PATCH] mark stru...
311
  static const struct file_operations shm_file_operations = {
4e9823111   Kirill Korotaev   [PATCH] IPC names...
312
  	.mmap		= shm_mmap,
516dffdcd   Adam Litke   [PATCH] Fix get_u...
313
  	.fsync		= shm_fsync,
4e9823111   Kirill Korotaev   [PATCH] IPC names...
314
  	.release	= shm_release,
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
315
  	.get_unmapped_area	= shm_get_unmapped_area,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
316
317
318
319
320
  };
  
  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 */
54cb8821d   Nick Piggin   mm: merge populat...
321
  	.fault	= shm_fault,
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
322
323
324
  #if defined(CONFIG_NUMA)
  	.set_policy = shm_set_policy,
  	.get_policy = shm_get_policy,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325
326
  #endif
  };
f4566f048   Nadia Derbey   ipc: fix wrong co...
327
328
329
330
331
  /**
   * newseg - Create a new shared memory segment
   * @ns: namespace
   * @params: ptr to the structure that contains key, size and shmflg
   *
3e148c799   Nadia Derbey   fix idr_find() lo...
332
   * Called with shm_ids.rw_mutex held as a writer.
f4566f048   Nadia Derbey   ipc: fix wrong co...
333
   */
7748dbfaa   Nadia Derbey   ipc: unify the sy...
334
  static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
335
  {
7748dbfaa   Nadia Derbey   ipc: unify the sy...
336
337
338
  	key_t key = params->key;
  	int shmflg = params->flg;
  	size_t size = params->u.size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
340
341
342
343
344
  	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...
345
  	if (size < SHMMIN || size > ns->shm_ctlmax)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
  		return -EINVAL;
f66d45e99   Guy Streeter   [PATCH] correct s...
347
  	if (ns->shm_tot + numpages > ns->shm_ctlall)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
349
350
351
352
353
354
  		return -ENOSPC;
  
  	shp = ipc_rcu_alloc(sizeof(*shp));
  	if (!shp)
  		return -ENOMEM;
  
  	shp->shm_perm.key = key;
b33291c0b   Andrew Morton   [PATCH] ipc: expa...
355
  	shp->shm_perm.mode = (shmflg & S_IRWXUGO);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
356
357
358
359
360
361
362
363
  	shp->mlock_user = NULL;
  
  	shp->shm_perm.security = NULL;
  	error = security_shm_alloc(shp);
  	if (error) {
  		ipc_rcu_putref(shp);
  		return error;
  	}
9d66586f7   Eric W. Biederman   shm: fix the file...
364
  	sprintf (name, "SYSV%08x", key);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
365
  	if (shmflg & SHM_HUGETLB) {
9d66586f7   Eric W. Biederman   shm: fix the file...
366
367
  		/* hugetlb_file_setup takes care of mlock user accounting */
  		file = hugetlb_file_setup(name, size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
368
369
  		shp->mlock_user = current->user;
  	} else {
bf8f972d3   Badari Pulavarty   [PATCH] SHM_NORES...
370
371
372
373
374
375
376
377
  		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;
bf8f972d3   Badari Pulavarty   [PATCH] SHM_NORES...
378
  		file = shmem_file_setup(name, size, acctflag);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
379
380
381
382
  	}
  	error = PTR_ERR(file);
  	if (IS_ERR(file))
  		goto no_file;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
383
  	id = shm_addid(ns, shp);
283bb7fad   Pierre Peiffer   IPC: fix error ca...
384
385
  	if (id < 0) {
  		error = id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
386
  		goto no_id;
283bb7fad   Pierre Peiffer   IPC: fix error ca...
387
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
388

b488893a3   Pavel Emelyanov   pid namespaces: c...
389
  	shp->shm_cprid = task_tgid_vnr(current);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
390
391
392
393
394
  	shp->shm_lprid = 0;
  	shp->shm_atim = shp->shm_dtim = 0;
  	shp->shm_ctim = get_seconds();
  	shp->shm_segsz = size;
  	shp->shm_nattch = 0;
1b531f213   Nadia Derbey   ipc: remove unnee...
395
  	shp->shm_perm.id = shm_buildid(id, shp->shm_perm.seq);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
396
  	shp->shm_file = file;
30475cc12   Badari Pulavarty   Restore shmid as ...
397
398
399
400
  	/*
  	 * shmid gets reported as "inode#" in /proc/pid/maps.
  	 * proc-ps tools use this. Changing this will break them.
  	 */
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
401
  	file->f_dentry->d_inode->i_ino = shp->shm_perm.id;
551110a94   Krishnakumar R   [PATCH] hugetlb: ...
402

4e9823111   Kirill Korotaev   [PATCH] IPC names...
403
  	ns->shm_tot += numpages;
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
404
  	error = shp->shm_perm.id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
405
  	shm_unlock(shp);
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
406
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
407
408
409
410
411
412
413
414
  
  no_id:
  	fput(file);
  no_file:
  	security_shm_free(shp);
  	ipc_rcu_putref(shp);
  	return error;
  }
f4566f048   Nadia Derbey   ipc: fix wrong co...
415
  /*
3e148c799   Nadia Derbey   fix idr_find() lo...
416
   * Called with shm_ids.rw_mutex and ipcp locked.
f4566f048   Nadia Derbey   ipc: fix wrong co...
417
   */
03f02c765   Nadia Derbey   Storing ipcs into...
418
  static inline int shm_security(struct kern_ipc_perm *ipcp, int shmflg)
7748dbfaa   Nadia Derbey   ipc: unify the sy...
419
  {
03f02c765   Nadia Derbey   Storing ipcs into...
420
421
422
423
  	struct shmid_kernel *shp;
  
  	shp = container_of(ipcp, struct shmid_kernel, shm_perm);
  	return security_shm_associate(shp, shmflg);
7748dbfaa   Nadia Derbey   ipc: unify the sy...
424
  }
f4566f048   Nadia Derbey   ipc: fix wrong co...
425
  /*
3e148c799   Nadia Derbey   fix idr_find() lo...
426
   * Called with shm_ids.rw_mutex and ipcp locked.
f4566f048   Nadia Derbey   ipc: fix wrong co...
427
   */
03f02c765   Nadia Derbey   Storing ipcs into...
428
429
  static inline int shm_more_checks(struct kern_ipc_perm *ipcp,
  				struct ipc_params *params)
7748dbfaa   Nadia Derbey   ipc: unify the sy...
430
  {
03f02c765   Nadia Derbey   Storing ipcs into...
431
432
433
434
  	struct shmid_kernel *shp;
  
  	shp = container_of(ipcp, struct shmid_kernel, shm_perm);
  	if (shp->shm_segsz < params->u.size)
7748dbfaa   Nadia Derbey   ipc: unify the sy...
435
436
437
438
  		return -EINVAL;
  
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439
440
  asmlinkage long sys_shmget (key_t key, size_t size, int shmflg)
  {
4e9823111   Kirill Korotaev   [PATCH] IPC names...
441
  	struct ipc_namespace *ns;
7748dbfaa   Nadia Derbey   ipc: unify the sy...
442
443
  	struct ipc_ops shm_ops;
  	struct ipc_params shm_params;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
444
445
  
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446

7748dbfaa   Nadia Derbey   ipc: unify the sy...
447
448
449
  	shm_ops.getnew = newseg;
  	shm_ops.associate = shm_security;
  	shm_ops.more_checks = shm_more_checks;
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
450

7748dbfaa   Nadia Derbey   ipc: unify the sy...
451
452
453
  	shm_params.key = key;
  	shm_params.flg = shmflg;
  	shm_params.u.size = size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
454

7748dbfaa   Nadia Derbey   ipc: unify the sy...
455
  	return ipcget(ns, &shm_ids(ns), &shm_ops, &shm_params);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
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
  }
  
  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...
501
  		out->mode	= tbuf.shm_perm.mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
502
503
504
505
506
507
508
509
510
511
512
513
  
  		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...
514
  		out->mode	= tbuf_old.shm_perm.mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
  
  		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;
  	}
  }
f4566f048   Nadia Derbey   ipc: fix wrong co...
548
  /*
3e148c799   Nadia Derbey   fix idr_find() lo...
549
   * Called with shm_ids.rw_mutex held as a reader
f4566f048   Nadia Derbey   ipc: fix wrong co...
550
   */
4e9823111   Kirill Korotaev   [PATCH] IPC names...
551
552
  static void shm_get_stat(struct ipc_namespace *ns, unsigned long *rss,
  		unsigned long *swp)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
553
  {
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
554
555
  	int next_id;
  	int total, in_use;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
557
558
  
  	*rss = 0;
  	*swp = 0;
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
559
560
561
  	in_use = shm_ids(ns).in_use;
  
  	for (total = 0, next_id = 0; total < in_use; next_id++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
562
563
  		struct shmid_kernel *shp;
  		struct inode *inode;
637c36634   Nadia Derbey   ipc: remove the i...
564
  		shp = idr_find(&shm_ids(ns).ipcs_idr, next_id);
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
565
  		if (shp == NULL)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
566
  			continue;
6d63079ad   Josef Sipek   [PATCH] struct pa...
567
  		inode = shp->shm_file->f_path.dentry->d_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
568
569
570
571
572
573
574
575
576
577
578
  
  		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);
  		}
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
579
580
  
  		total++;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
581
582
583
584
585
586
587
588
  	}
  }
  
  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...
589
  	struct ipc_namespace *ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
590
591
592
593
594
595
596
  
  	if (cmd < 0 || shmid < 0) {
  		err = -EINVAL;
  		goto out;
  	}
  
  	version = ipc_parse_version(&cmd);
4e9823111   Kirill Korotaev   [PATCH] IPC names...
597
  	ns = current->nsproxy->ipc_ns;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
598
599
600
601
602
603
604
605
606
607
608
  
  	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...
609
610
611
  		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
612
613
614
615
  
  		shminfo.shmmin = SHMMIN;
  		if(copy_shminfo_to_user (buf, &shminfo, version))
  			return -EFAULT;
f4566f048   Nadia Derbey   ipc: fix wrong co...
616

3e148c799   Nadia Derbey   fix idr_find() lo...
617
  		down_read(&shm_ids(ns).rw_mutex);
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
618
  		err = ipc_get_maxid(&shm_ids(ns));
3e148c799   Nadia Derbey   fix idr_find() lo...
619
  		up_read(&shm_ids(ns).rw_mutex);
f4566f048   Nadia Derbey   ipc: fix wrong co...
620

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
621
622
623
624
625
626
627
628
629
630
631
632
633
  		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));
3e148c799   Nadia Derbey   fix idr_find() lo...
634
  		down_read(&shm_ids(ns).rw_mutex);
4e9823111   Kirill Korotaev   [PATCH] IPC names...
635
636
637
  		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
638
639
  		shm_info.swap_attempts = 0;
  		shm_info.swap_successes = 0;
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
640
  		err = ipc_get_maxid(&shm_ids(ns));
3e148c799   Nadia Derbey   fix idr_find() lo...
641
  		up_read(&shm_ids(ns).rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
642
643
644
645
646
647
648
649
650
651
652
653
654
  		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;
023a53557   Nadia Derbey   ipc: integrate ip...
655
656
657
  
  		if (!buf) {
  			err = -EFAULT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
658
  			goto out;
023a53557   Nadia Derbey   ipc: integrate ip...
659
660
661
662
663
664
665
666
  		}
  
  		if (cmd == SHM_STAT) {
  			shp = shm_lock(ns, shmid);
  			if (IS_ERR(shp)) {
  				err = PTR_ERR(shp);
  				goto out;
  			}
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
667
  			result = shp->shm_perm.id;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
668
  		} else {
023a53557   Nadia Derbey   ipc: integrate ip...
669
670
671
672
673
  			shp = shm_lock_check(ns, shmid);
  			if (IS_ERR(shp)) {
  				err = PTR_ERR(shp);
  				goto out;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
674
675
676
677
678
679
680
681
  			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;
023a53557   Nadia Derbey   ipc: integrate ip...
682
  		memset(&tbuf, 0, sizeof(tbuf));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
683
684
685
686
687
688
689
  		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...
690
  		tbuf.shm_nattch	= shp->shm_nattch;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
691
692
693
694
695
696
697
698
699
700
  		shm_unlock(shp);
  		if(copy_shmid_to_user (buf, &tbuf, version))
  			err = -EFAULT;
  		else
  			err = result;
  		goto out;
  	}
  	case SHM_LOCK:
  	case SHM_UNLOCK:
  	{
023a53557   Nadia Derbey   ipc: integrate ip...
701
702
703
  		shp = shm_lock_check(ns, shmid);
  		if (IS_ERR(shp)) {
  			err = PTR_ERR(shp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
704
705
  			goto out;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
706

073115d6b   Steve Grubb   [PATCH] Rework of...
707
708
709
  		err = audit_ipc_obj(&(shp->shm_perm));
  		if (err)
  			goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
  		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);
7be77e20d   Pavel Emelianov   Fix user struct l...
728
  				if (!err && !(shp->shm_perm.mode & SHM_LOCKED)){
b33291c0b   Andrew Morton   [PATCH] ipc: expa...
729
  					shp->shm_perm.mode |= SHM_LOCKED;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
730
731
732
733
734
  					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...
735
  			shp->shm_perm.mode &= ~SHM_LOCKED;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
  			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.
  		 */
3e148c799   Nadia Derbey   fix idr_find() lo...
753
754
  		down_write(&shm_ids(ns).rw_mutex);
  		shp = shm_lock_check_down(ns, shmid);
023a53557   Nadia Derbey   ipc: integrate ip...
755
756
  		if (IS_ERR(shp)) {
  			err = PTR_ERR(shp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
757
  			goto out_up;
023a53557   Nadia Derbey   ipc: integrate ip...
758
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
759

073115d6b   Steve Grubb   [PATCH] Rework of...
760
761
762
  		err = audit_ipc_obj(&(shp->shm_perm));
  		if (err)
  			goto out_unlock_up;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
763
764
765
766
767
768
769
770
771
772
  		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;
01b8b07a5   Pierre Peiffer   IPC: consolidate ...
773
  		do_shm_rmid(ns, &shp->shm_perm);
3e148c799   Nadia Derbey   fix idr_find() lo...
774
  		up_write(&shm_ids(ns).rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
775
776
777
778
779
  		goto out;
  	}
  
  	case IPC_SET:
  	{
023a53557   Nadia Derbey   ipc: integrate ip...
780
781
782
783
  		if (!buf) {
  			err = -EFAULT;
  			goto out;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
784
785
786
787
  		if (copy_shmid_from_user (&setbuf, buf, version)) {
  			err = -EFAULT;
  			goto out;
  		}
3e148c799   Nadia Derbey   fix idr_find() lo...
788
789
  		down_write(&shm_ids(ns).rw_mutex);
  		shp = shm_lock_check_down(ns, shmid);
023a53557   Nadia Derbey   ipc: integrate ip...
790
791
  		if (IS_ERR(shp)) {
  			err = PTR_ERR(shp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
792
  			goto out_up;
023a53557   Nadia Derbey   ipc: integrate ip...
793
  		}
073115d6b   Steve Grubb   [PATCH] Rework of...
794
795
796
  		err = audit_ipc_obj(&(shp->shm_perm));
  		if (err)
  			goto out_unlock_up;
ac03221a4   Linda Knippers   [PATCH] update of...
797
  		err = audit_ipc_set_perm(0, setbuf.uid, setbuf.gid, setbuf.mode);
073115d6b   Steve Grubb   [PATCH] Rework of...
798
799
  		if (err)
  			goto out_unlock_up;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
800
801
802
803
804
805
806
807
808
809
810
811
812
  		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...
813
  		shp->shm_perm.mode = (shp->shm_perm.mode & ~S_IRWXUGO)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
814
815
816
817
818
819
820
821
822
823
824
825
826
827
  			| (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:
3e148c799   Nadia Derbey   fix idr_find() lo...
828
  	up_write(&shm_ids(ns).rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
  	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
852
  	int acc_mode;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
853
  	unsigned long user_addr;
4e9823111   Kirill Korotaev   [PATCH] IPC names...
854
  	struct ipc_namespace *ns;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
855
856
857
  	struct shm_file_data *sfd;
  	struct path path;
  	mode_t f_mode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
858

bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
859
860
  	err = -EINVAL;
  	if (shmid < 0)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
861
  		goto out;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
862
  	else if ((addr = (ulong)shmaddr)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
863
864
865
866
867
868
869
  		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...
870
  					goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
871
872
873
874
  		}
  		flags = MAP_SHARED | MAP_FIXED;
  	} else {
  		if ((shmflg & SHM_REMAP))
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
875
  			goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
876
877
878
879
880
881
  
  		flags = MAP_SHARED;
  	}
  
  	if (shmflg & SHM_RDONLY) {
  		prot = PROT_READ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
882
  		acc_mode = S_IRUGO;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
883
  		f_mode = FMODE_READ;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
884
885
  	} else {
  		prot = PROT_READ | PROT_WRITE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
886
  		acc_mode = S_IRUGO | S_IWUGO;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
887
  		f_mode = FMODE_READ | FMODE_WRITE;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
888
889
890
891
892
893
894
895
896
897
  	}
  	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...
898
  	ns = current->nsproxy->ipc_ns;
023a53557   Nadia Derbey   ipc: integrate ip...
899
900
901
  	shp = shm_lock_check(ns, shmid);
  	if (IS_ERR(shp)) {
  		err = PTR_ERR(shp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
902
  		goto out;
023a53557   Nadia Derbey   ipc: integrate ip...
903
  	}
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
904
905
906
907
  
  	err = -EACCES;
  	if (ipcperms(&shp->shm_perm, acc_mode))
  		goto out_unlock;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
908
909
  
  	err = security_shm_shmat(shp, shmaddr, shmflg);
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
910
911
912
913
  	if (err)
  		goto out_unlock;
  
  	path.dentry = dget(shp->shm_file->f_path.dentry);
ce8d2cdf3   Dave Hansen   r/o bind mounts: ...
914
  	path.mnt    = shp->shm_file->f_path.mnt;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
915
  	shp->shm_nattch++;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
916
  	size = i_size_read(path.dentry->d_inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
917
  	shm_unlock(shp);
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
918
919
920
  	err = -ENOMEM;
  	sfd = kzalloc(sizeof(*sfd), GFP_KERNEL);
  	if (!sfd)
ce8d2cdf3   Dave Hansen   r/o bind mounts: ...
921
  		goto out_put_dentry;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
922
923
  
  	err = -ENOMEM;
ce8d2cdf3   Dave Hansen   r/o bind mounts: ...
924
925
  
  	file = alloc_file(path.mnt, path.dentry, f_mode, &shm_file_operations);
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
926
927
  	if (!file)
  		goto out_free;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
928
  	file->private_data = sfd;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
929
  	file->f_mapping = shp->shm_file->f_mapping;
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
930
  	sfd->id = shp->shm_perm.id;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
931
932
933
  	sfd->ns = get_ipc_ns(ns);
  	sfd->file = shp->shm_file;
  	sfd->vm_ops = NULL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
934
935
  	down_write(&current->mm->mmap_sem);
  	if (addr && !(shmflg & SHM_REMAP)) {
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
936
  		err = -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
937
938
939
940
941
942
943
944
945
946
947
  		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...
948
949
950
951
952
  	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
953
954
  invalid:
  	up_write(&current->mm->mmap_sem);
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
955
956
957
  	fput(file);
  
  out_nattch:
3e148c799   Nadia Derbey   fix idr_find() lo...
958
959
  	down_write(&shm_ids(ns).rw_mutex);
  	shp = shm_lock_down(ns, shmid);
023a53557   Nadia Derbey   ipc: integrate ip...
960
  	BUG_ON(IS_ERR(shp));
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
961
962
  	shp->shm_nattch--;
  	if(shp->shm_nattch == 0 &&
b33291c0b   Andrew Morton   [PATCH] ipc: expa...
963
  	   shp->shm_perm.mode & SHM_DEST)
4e9823111   Kirill Korotaev   [PATCH] IPC names...
964
  		shm_destroy(ns, shp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
965
966
  	else
  		shm_unlock(shp);
3e148c799   Nadia Derbey   fix idr_find() lo...
967
  	up_write(&shm_ids(ns).rw_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
968

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
969
970
  out:
  	return err;
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
971
972
973
974
975
976
977
  
  out_unlock:
  	shm_unlock(shp);
  	goto out;
  
  out_free:
  	kfree(sfd);
ce8d2cdf3   Dave Hansen   r/o bind mounts: ...
978
  out_put_dentry:
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
979
  	dput(path.dentry);
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
980
  	goto out_nattch;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
981
  }
7d87e14c2   Stephen Rothwell   [PATCH] consolida...
982
983
984
985
986
987
988
989
990
991
992
  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
993
994
995
996
997
998
999
1000
1001
1002
1003
  /*
   * 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...
1004
1005
  	if (addr & ~PAGE_MASK)
  		return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
  	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...
1038
  		if ((vma->vm_ops == &shm_vm_ops) &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1039
  			(vma->vm_start - addr)/PAGE_SIZE == vma->vm_pgoff) {
6d63079ad   Josef Sipek   [PATCH] struct pa...
1040
  			size = vma->vm_file->f_path.dentry->d_inode->i_size;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
  			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...
1060
  	size = PAGE_ALIGN(size);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1061
1062
1063
1064
  	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...
1065
  		if ((vma->vm_ops == &shm_vm_ops) &&
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
  			(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...
1077
  static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1078
  {
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1079
1080
  	struct shmid_kernel *shp = it;
  	char *format;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1081

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1082
1083
1084
1085
  #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
1086

19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1087
1088
1089
1090
1091
1092
  	if (sizeof(size_t) <= sizeof(int))
  		format = SMALL_STRING;
  	else
  		format = BIG_STRING;
  	return seq_printf(s, format,
  			  shp->shm_perm.key,
7ca7e564e   Nadia Derbey   ipc: store ipcs i...
1093
  			  shp->shm_perm.id,
b33291c0b   Andrew Morton   [PATCH] ipc: expa...
1094
  			  shp->shm_perm.mode,
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1095
1096
1097
  			  shp->shm_segsz,
  			  shp->shm_cprid,
  			  shp->shm_lprid,
bc56bba8f   Eric W. Biederman   [PATCH] shm: make...
1098
  			  shp->shm_nattch,
19b4946ca   Mike Waychison   [PATCH] ipc: conv...
1099
1100
1101
1102
1103
1104
1105
  			  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
1106
1107
  }
  #endif