diff options
Diffstat (limited to 'fs/proc/base.c')
| -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 |
