diff options
Diffstat (limited to 'fs/proc')
-rw-r--r-- | fs/proc/base.c | 84 |
1 files changed, 77 insertions, 7 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index 23db452ab428..fb34f88a4a74 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -340,6 +340,52 @@ static int proc_root_link(struct inode *inode, struct dentry **dentry, struct vf | |||
340 | return result; | 340 | return result; |
341 | } | 341 | } |
342 | 342 | ||
343 | |||
344 | /* Same as proc_root_link, but this addionally tries to get fs from other | ||
345 | * threads in the group */ | ||
346 | static int proc_task_root_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt) | ||
347 | { | ||
348 | struct fs_struct *fs; | ||
349 | int result = -ENOENT; | ||
350 | struct task_struct *leader = proc_task(inode); | ||
351 | |||
352 | task_lock(leader); | ||
353 | fs = leader->fs; | ||
354 | if (fs) { | ||
355 | atomic_inc(&fs->count); | ||
356 | task_unlock(leader); | ||
357 | } else { | ||
358 | /* Try to get fs from other threads */ | ||
359 | task_unlock(leader); | ||
360 | struct task_struct *task = leader; | ||
361 | read_lock(&tasklist_lock); | ||
362 | if (pid_alive(task)) { | ||
363 | while ((task = next_thread(task)) != leader) { | ||
364 | task_lock(task); | ||
365 | fs = task->fs; | ||
366 | if (fs) { | ||
367 | atomic_inc(&fs->count); | ||
368 | task_unlock(task); | ||
369 | break; | ||
370 | } | ||
371 | task_unlock(task); | ||
372 | } | ||
373 | } | ||
374 | read_unlock(&tasklist_lock); | ||
375 | } | ||
376 | |||
377 | if (fs) { | ||
378 | read_lock(&fs->lock); | ||
379 | *mnt = mntget(fs->rootmnt); | ||
380 | *dentry = dget(fs->root); | ||
381 | read_unlock(&fs->lock); | ||
382 | result = 0; | ||
383 | put_fs_struct(fs); | ||
384 | } | ||
385 | return result; | ||
386 | } | ||
387 | |||
388 | |||
343 | #define MAY_PTRACE(task) \ | 389 | #define MAY_PTRACE(task) \ |
344 | (task == current || \ | 390 | (task == current || \ |
345 | (task->parent == current && \ | 391 | (task->parent == current && \ |
@@ -471,14 +517,14 @@ static int proc_oom_score(struct task_struct *task, char *buffer) | |||
471 | 517 | ||
472 | /* permission checks */ | 518 | /* permission checks */ |
473 | 519 | ||
474 | static int proc_check_root(struct inode *inode) | 520 | /* If the process being read is separated by chroot from the reading process, |
521 | * don't let the reader access the threads. | ||
522 | */ | ||
523 | static int proc_check_chroot(struct dentry *root, struct vfsmount *vfsmnt) | ||
475 | { | 524 | { |
476 | struct dentry *de, *base, *root; | 525 | struct dentry *de, *base; |
477 | struct vfsmount *our_vfsmnt, *vfsmnt, *mnt; | 526 | struct vfsmount *our_vfsmnt, *mnt; |
478 | int res = 0; | 527 | int res = 0; |
479 | |||
480 | if (proc_root_link(inode, &root, &vfsmnt)) /* Ewww... */ | ||
481 | return -ENOENT; | ||
482 | read_lock(¤t->fs->lock); | 528 | read_lock(¤t->fs->lock); |
483 | our_vfsmnt = mntget(current->fs->rootmnt); | 529 | our_vfsmnt = mntget(current->fs->rootmnt); |
484 | base = dget(current->fs->root); | 530 | base = dget(current->fs->root); |
@@ -511,6 +557,16 @@ out: | |||
511 | goto exit; | 557 | goto exit; |
512 | } | 558 | } |
513 | 559 | ||
560 | static int proc_check_root(struct inode *inode) | ||
561 | { | ||
562 | struct dentry *root; | ||
563 | struct vfsmount *vfsmnt; | ||
564 | |||
565 | if (proc_root_link(inode, &root, &vfsmnt)) /* Ewww... */ | ||
566 | return -ENOENT; | ||
567 | return proc_check_chroot(root, vfsmnt); | ||
568 | } | ||
569 | |||
514 | static int proc_permission(struct inode *inode, int mask, struct nameidata *nd) | 570 | static int proc_permission(struct inode *inode, int mask, struct nameidata *nd) |
515 | { | 571 | { |
516 | if (generic_permission(inode, mask, NULL) != 0) | 572 | if (generic_permission(inode, mask, NULL) != 0) |
@@ -518,6 +574,20 @@ static int proc_permission(struct inode *inode, int mask, struct nameidata *nd) | |||
518 | return proc_check_root(inode); | 574 | return proc_check_root(inode); |
519 | } | 575 | } |
520 | 576 | ||
577 | static int proc_task_permission(struct inode *inode, int mask, struct nameidata *nd) | ||
578 | { | ||
579 | struct dentry *root; | ||
580 | struct vfsmount *vfsmnt; | ||
581 | |||
582 | if (generic_permission(inode, mask, NULL) != 0) | ||
583 | return -EACCES; | ||
584 | |||
585 | if (proc_task_root_link(inode, &root, &vfsmnt)) | ||
586 | return -ENOENT; | ||
587 | |||
588 | return proc_check_chroot(root, vfsmnt); | ||
589 | } | ||
590 | |||
521 | extern struct seq_operations proc_pid_maps_op; | 591 | extern struct seq_operations proc_pid_maps_op; |
522 | static int maps_open(struct inode *inode, struct file *file) | 592 | static int maps_open(struct inode *inode, struct file *file) |
523 | { | 593 | { |
@@ -1419,7 +1489,7 @@ static struct inode_operations proc_fd_inode_operations = { | |||
1419 | 1489 | ||
1420 | static struct inode_operations proc_task_inode_operations = { | 1490 | static struct inode_operations proc_task_inode_operations = { |
1421 | .lookup = proc_task_lookup, | 1491 | .lookup = proc_task_lookup, |
1422 | .permission = proc_permission, | 1492 | .permission = proc_task_permission, |
1423 | }; | 1493 | }; |
1424 | 1494 | ||
1425 | #ifdef CONFIG_SECURITY | 1495 | #ifdef CONFIG_SECURITY |