aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorEric W. Biederman <ebiederm@aristanetworks.com>2009-03-04 14:57:20 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2009-03-24 19:38:26 -0400
commite0edd3c65aa5b53e20280565a7ce11675eb7ed6b (patch)
treee9d7a8834eba3f19635f770f5d19bfe88a2485e4 /fs
parentae1b41715ee2aae356fbcca032838b71d70b855f (diff)
sysfs: don't block indefinitely for unmapped files.
Modify sysfs bin files so that we can remove the bin file while they are still mapped. When the kobject is removed we unmap the bin file and arrange for future accesses to the mapping to receive SIGBUS. Implementing this prevents a nasty DOS when pci devices are hot plugged and unplugged. Where if any of their resources were mmaped the kernel could not free up their pci resources or release their pci data structures. [akpm@linux-foundation.org: remove unused var] Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com> Cc: Jesse Barnes <jbarnes@virtuousgeek.org> Acked-by: Tejun Heo <tj@kernel.org> Cc: Kay Sievers <kay.sievers@vrfy.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'fs')
-rw-r--r--fs/sysfs/bin.c184
-rw-r--r--fs/sysfs/dir.c1
-rw-r--r--fs/sysfs/sysfs.h2
3 files changed, 174 insertions, 13 deletions
diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c
index f2c478c3424e..96cc2bf6a84e 100644
--- a/fs/sysfs/bin.c
+++ b/fs/sysfs/bin.c
@@ -21,15 +21,28 @@
21#include <linux/module.h> 21#include <linux/module.h>
22#include <linux/slab.h> 22#include <linux/slab.h>
23#include <linux/mutex.h> 23#include <linux/mutex.h>
24#include <linux/mm.h>
24 25
25#include <asm/uaccess.h> 26#include <asm/uaccess.h>
26 27
27#include "sysfs.h" 28#include "sysfs.h"
28 29
30/*
31 * There's one bin_buffer for each open file.
32 *
33 * filp->private_data points to bin_buffer and
34 * sysfs_dirent->s_bin_attr.buffers points to a the bin_buffer s
35 * sysfs_dirent->s_bin_attr.buffers is protected by sysfs_bin_lock
36 */
37static DEFINE_MUTEX(sysfs_bin_lock);
38
29struct bin_buffer { 39struct bin_buffer {
30 struct mutex mutex; 40 struct mutex mutex;
31 void *buffer; 41 void *buffer;
32 int mmapped; 42 int mmapped;
43 struct vm_operations_struct *vm_ops;
44 struct file *file;
45 struct hlist_node list;
33}; 46};
34 47
35static int 48static int
@@ -168,29 +181,148 @@ out_free:
168 return count; 181 return count;
169} 182}
170 183
184static void bin_vma_open(struct vm_area_struct *vma)
185{
186 struct file *file = vma->vm_file;
187 struct bin_buffer *bb = file->private_data;
188 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
189
190 if (!bb->vm_ops || !bb->vm_ops->open)
191 return;
192
193 if (!sysfs_get_active_two(attr_sd))
194 return;
195
196 bb->vm_ops->open(vma);
197
198 sysfs_put_active_two(attr_sd);
199}
200
201static void bin_vma_close(struct vm_area_struct *vma)
202{
203 struct file *file = vma->vm_file;
204 struct bin_buffer *bb = file->private_data;
205 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
206
207 if (!bb->vm_ops || !bb->vm_ops->close)
208 return;
209
210 if (!sysfs_get_active_two(attr_sd))
211 return;
212
213 bb->vm_ops->close(vma);
214
215 sysfs_put_active_two(attr_sd);
216}
217
218static int bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
219{
220 struct file *file = vma->vm_file;
221 struct bin_buffer *bb = file->private_data;
222 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
223 int ret;
224
225 if (!bb->vm_ops || !bb->vm_ops->fault)
226 return VM_FAULT_SIGBUS;
227
228 if (!sysfs_get_active_two(attr_sd))
229 return VM_FAULT_SIGBUS;
230
231 ret = bb->vm_ops->fault(vma, vmf);
232
233 sysfs_put_active_two(attr_sd);
234 return ret;
235}
236
237static int bin_page_mkwrite(struct vm_area_struct *vma, struct page *page)
238{
239 struct file *file = vma->vm_file;
240 struct bin_buffer *bb = file->private_data;
241 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
242 int ret;
243
244 if (!bb->vm_ops || !bb->vm_ops->page_mkwrite)
245 return -EINVAL;
246
247 if (!sysfs_get_active_two(attr_sd))
248 return -EINVAL;
249
250 ret = bb->vm_ops->page_mkwrite(vma, page);
251
252 sysfs_put_active_two(attr_sd);
253 return ret;
254}
255
256static int bin_access(struct vm_area_struct *vma, unsigned long addr,
257 void *buf, int len, int write)
258{
259 struct file *file = vma->vm_file;
260 struct bin_buffer *bb = file->private_data;
261 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
262 int ret;
263
264 if (!bb->vm_ops || !bb->vm_ops->access)
265 return -EINVAL;
266
267 if (!sysfs_get_active_two(attr_sd))
268 return -EINVAL;
269
270 ret = bb->vm_ops->access(vma, addr, buf, len, write);
271
272 sysfs_put_active_two(attr_sd);
273 return ret;
274}
275
276static struct vm_operations_struct bin_vm_ops = {
277 .open = bin_vma_open,
278 .close = bin_vma_close,
279 .fault = bin_fault,
280 .page_mkwrite = bin_page_mkwrite,
281 .access = bin_access,
282};
283
171static int mmap(struct file *file, struct vm_area_struct *vma) 284static int mmap(struct file *file, struct vm_area_struct *vma)
172{ 285{
173 struct bin_buffer *bb = file->private_data; 286 struct bin_buffer *bb = file->private_data;
174 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; 287 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
175 struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr; 288 struct bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
176 struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; 289 struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
290 struct vm_operations_struct *vm_ops;
177 int rc; 291 int rc;
178 292
179 mutex_lock(&bb->mutex); 293 mutex_lock(&bb->mutex);
180 294
181 /* need attr_sd for attr, its parent for kobj */ 295 /* need attr_sd for attr, its parent for kobj */
296 rc = -ENODEV;
182 if (!sysfs_get_active_two(attr_sd)) 297 if (!sysfs_get_active_two(attr_sd))
183 return -ENODEV; 298 goto out_unlock;
184 299
185 rc = -EINVAL; 300 rc = -EINVAL;
186 if (attr->mmap) 301 if (!attr->mmap)
187 rc = attr->mmap(kobj, attr, vma); 302 goto out_put;
188 303
189 if (rc == 0 && !bb->mmapped) 304 rc = attr->mmap(kobj, attr, vma);
190 bb->mmapped = 1; 305 vm_ops = vma->vm_ops;
191 else 306 vma->vm_ops = &bin_vm_ops;
192 sysfs_put_active_two(attr_sd); 307 if (rc)
308 goto out_put;
193 309
310 rc = -EINVAL;
311 if (bb->mmapped && bb->vm_ops != vma->vm_ops)
312 goto out_put;
313
314#ifdef CONFIG_NUMA
315 rc = -EINVAL;
316 if (vm_ops && ((vm_ops->set_policy || vm_ops->get_policy || vm_ops->migrate)))
317 goto out_put;
318#endif
319
320 rc = 0;
321 bb->mmapped = 1;
322 bb->vm_ops = vm_ops;
323out_put:
324 sysfs_put_active_two(attr_sd);
325out_unlock:
194 mutex_unlock(&bb->mutex); 326 mutex_unlock(&bb->mutex);
195 327
196 return rc; 328 return rc;
@@ -223,8 +355,13 @@ static int open(struct inode * inode, struct file * file)
223 goto err_out; 355 goto err_out;
224 356
225 mutex_init(&bb->mutex); 357 mutex_init(&bb->mutex);
358 bb->file = file;
226 file->private_data = bb; 359 file->private_data = bb;
227 360
361 mutex_lock(&sysfs_bin_lock);
362 hlist_add_head(&bb->list, &attr_sd->s_bin_attr.buffers);
363 mutex_unlock(&sysfs_bin_lock);
364
228 /* open succeeded, put active references */ 365 /* open succeeded, put active references */
229 sysfs_put_active_two(attr_sd); 366 sysfs_put_active_two(attr_sd);
230 return 0; 367 return 0;
@@ -237,11 +374,12 @@ static int open(struct inode * inode, struct file * file)
237 374
238static int release(struct inode * inode, struct file * file) 375static int release(struct inode * inode, struct file * file)
239{ 376{
240 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
241 struct bin_buffer *bb = file->private_data; 377 struct bin_buffer *bb = file->private_data;
242 378
243 if (bb->mmapped) 379 mutex_lock(&sysfs_bin_lock);
244 sysfs_put_active_two(attr_sd); 380 hlist_del(&bb->list);
381 mutex_unlock(&sysfs_bin_lock);
382
245 kfree(bb->buffer); 383 kfree(bb->buffer);
246 kfree(bb); 384 kfree(bb);
247 return 0; 385 return 0;
@@ -256,6 +394,26 @@ const struct file_operations bin_fops = {
256 .release = release, 394 .release = release,
257}; 395};
258 396
397
398void unmap_bin_file(struct sysfs_dirent *attr_sd)
399{
400 struct bin_buffer *bb;
401 struct hlist_node *tmp;
402
403 if (sysfs_type(attr_sd) != SYSFS_KOBJ_BIN_ATTR)
404 return;
405
406 mutex_lock(&sysfs_bin_lock);
407
408 hlist_for_each_entry(bb, tmp, &attr_sd->s_bin_attr.buffers, list) {
409 struct inode *inode = bb->file->f_path.dentry->d_inode;
410
411 unmap_mapping_range(inode->i_mapping, 0, 0, 1);
412 }
413
414 mutex_unlock(&sysfs_bin_lock);
415}
416
259/** 417/**
260 * sysfs_create_bin_file - create binary file for object. 418 * sysfs_create_bin_file - create binary file for object.
261 * @kobj: object. 419 * @kobj: object.
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index f13d852ab3c1..66aeb4fff0c3 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -609,6 +609,7 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
609 609
610 sysfs_drop_dentry(sd); 610 sysfs_drop_dentry(sd);
611 sysfs_deactivate(sd); 611 sysfs_deactivate(sd);
612 unmap_bin_file(sd);
612 sysfs_put(sd); 613 sysfs_put(sd);
613 } 614 }
614} 615}
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index 9055d04e4ab0..3fa0d98481e2 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -28,6 +28,7 @@ struct sysfs_elem_attr {
28 28
29struct sysfs_elem_bin_attr { 29struct sysfs_elem_bin_attr {
30 struct bin_attribute *bin_attr; 30 struct bin_attribute *bin_attr;
31 struct hlist_head buffers;
31}; 32};
32 33
33/* 34/*
@@ -164,6 +165,7 @@ int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,
164 * bin.c 165 * bin.c
165 */ 166 */
166extern const struct file_operations bin_fops; 167extern const struct file_operations bin_fops;
168void unmap_bin_file(struct sysfs_dirent *attr_sd);
167 169
168/* 170/*
169 * symlink.c 171 * symlink.c