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