diff options
| author | Hugh Dickins <hugh@veritas.com> | 2009-03-28 19:21:27 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-03-28 20:30:00 -0400 |
| commit | 7c2c7d993044cddc5010f6f429b100c63bc7dffb (patch) | |
| tree | b92a6daf7c11f9a53de6fed07512fe02cd5b4a68 | |
| parent | e426b64c412aaa3e9eb3e4b261dc5be0d5a83e78 (diff) | |
fix setuid sometimes wouldn't
check_unsafe_exec() also notes whether the fs_struct is being
shared by more threads than will get killed by the exec, and if so
sets LSM_UNSAFE_SHARE to make bprm_set_creds() careful about euid.
But /proc/<pid>/cwd and /proc/<pid>/root lookups make transient
use of get_fs_struct(), which also raises that sharing count.
This might occasionally cause a setuid program not to change euid,
in the same way as happened with files->count (check_unsafe_exec
also looks at sighand->count, but /proc doesn't raise that one).
We'd prefer exec not to unshare fs_struct: so fix this in procfs,
replacing get_fs_struct() by get_fs_path(), which does path_get
while still holding task_lock, instead of raising fs->count.
Signed-off-by: Hugh Dickins <hugh@veritas.com>
Cc: stable@kernel.org
___
fs/proc/base.c | 50 +++++++++++++++--------------------------------
1 file changed, 16 insertions(+), 34 deletions(-)
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | fs/proc/base.c | 50 |
1 files changed, 16 insertions, 34 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c index aef6d55b7de6..e0afd326b688 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
| @@ -146,15 +146,22 @@ static unsigned int pid_entry_count_dirs(const struct pid_entry *entries, | |||
| 146 | return count; | 146 | return count; |
| 147 | } | 147 | } |
| 148 | 148 | ||
| 149 | static struct fs_struct *get_fs_struct(struct task_struct *task) | 149 | static int get_fs_path(struct task_struct *task, struct path *path, bool root) |
| 150 | { | 150 | { |
| 151 | struct fs_struct *fs; | 151 | struct fs_struct *fs; |
| 152 | int result = -ENOENT; | ||
| 153 | |||
| 152 | task_lock(task); | 154 | task_lock(task); |
| 153 | fs = task->fs; | 155 | fs = task->fs; |
| 154 | if(fs) | 156 | if (fs) { |
| 155 | atomic_inc(&fs->count); | 157 | read_lock(&fs->lock); |
| 158 | *path = root ? fs->root : fs->pwd; | ||
| 159 | path_get(path); | ||
| 160 | read_unlock(&fs->lock); | ||
| 161 | result = 0; | ||
| 162 | } | ||
| 156 | task_unlock(task); | 163 | task_unlock(task); |
| 157 | return fs; | 164 | return result; |
| 158 | } | 165 | } |
| 159 | 166 | ||
| 160 | static int get_nr_threads(struct task_struct *tsk) | 167 | static int get_nr_threads(struct task_struct *tsk) |
| @@ -172,42 +179,24 @@ static int get_nr_threads(struct task_struct *tsk) | |||
| 172 | static int proc_cwd_link(struct inode *inode, struct path *path) | 179 | static int proc_cwd_link(struct inode *inode, struct path *path) |
| 173 | { | 180 | { |
| 174 | struct task_struct *task = get_proc_task(inode); | 181 | struct task_struct *task = get_proc_task(inode); |
| 175 | struct fs_struct *fs = NULL; | ||
| 176 | int result = -ENOENT; | 182 | int result = -ENOENT; |
| 177 | 183 | ||
| 178 | if (task) { | 184 | if (task) { |
| 179 | fs = get_fs_struct(task); | 185 | result = get_fs_path(task, path, 0); |
| 180 | put_task_struct(task); | 186 | put_task_struct(task); |
| 181 | } | 187 | } |
| 182 | if (fs) { | ||
| 183 | read_lock(&fs->lock); | ||
| 184 | *path = fs->pwd; | ||
| 185 | path_get(&fs->pwd); | ||
| 186 | read_unlock(&fs->lock); | ||
| 187 | result = 0; | ||
| 188 | put_fs_struct(fs); | ||
| 189 | } | ||
| 190 | return result; | 188 | return result; |
| 191 | } | 189 | } |
| 192 | 190 | ||
| 193 | static int proc_root_link(struct inode *inode, struct path *path) | 191 | static int proc_root_link(struct inode *inode, struct path *path) |
| 194 | { | 192 | { |
| 195 | struct task_struct *task = get_proc_task(inode); | 193 | struct task_struct *task = get_proc_task(inode); |
| 196 | struct fs_struct *fs = NULL; | ||
| 197 | int result = -ENOENT; | 194 | int result = -ENOENT; |
| 198 | 195 | ||
| 199 | if (task) { | 196 | if (task) { |
| 200 | fs = get_fs_struct(task); | 197 | result = get_fs_path(task, path, 1); |
| 201 | put_task_struct(task); | 198 | put_task_struct(task); |
| 202 | } | 199 | } |
| 203 | if (fs) { | ||
| 204 | read_lock(&fs->lock); | ||
| 205 | *path = fs->root; | ||
| 206 | path_get(&fs->root); | ||
| 207 | read_unlock(&fs->lock); | ||
| 208 | result = 0; | ||
| 209 | put_fs_struct(fs); | ||
| 210 | } | ||
| 211 | return result; | 200 | return result; |
| 212 | } | 201 | } |
| 213 | 202 | ||
| @@ -596,7 +585,6 @@ static int mounts_open_common(struct inode *inode, struct file *file, | |||
| 596 | struct task_struct *task = get_proc_task(inode); | 585 | struct task_struct *task = get_proc_task(inode); |
| 597 | struct nsproxy *nsp; | 586 | struct nsproxy *nsp; |
| 598 | struct mnt_namespace *ns = NULL; | 587 | struct mnt_namespace *ns = NULL; |
| 599 | struct fs_struct *fs = NULL; | ||
| 600 | struct path root; | 588 | struct path root; |
| 601 | struct proc_mounts *p; | 589 | struct proc_mounts *p; |
| 602 | int ret = -EINVAL; | 590 | int ret = -EINVAL; |
| @@ -610,22 +598,16 @@ static int mounts_open_common(struct inode *inode, struct file *file, | |||
| 610 | get_mnt_ns(ns); | 598 | get_mnt_ns(ns); |
| 611 | } | 599 | } |
| 612 | rcu_read_unlock(); | 600 | rcu_read_unlock(); |
| 613 | if (ns) | 601 | if (ns && get_fs_path(task, &root, 1) == 0) |
| 614 | fs = get_fs_struct(task); | 602 | ret = 0; |
| 615 | put_task_struct(task); | 603 | put_task_struct(task); |
| 616 | } | 604 | } |
| 617 | 605 | ||
| 618 | if (!ns) | 606 | if (!ns) |
| 619 | goto err; | 607 | goto err; |
| 620 | if (!fs) | 608 | if (ret) |
| 621 | goto err_put_ns; | 609 | goto err_put_ns; |
| 622 | 610 | ||
| 623 | read_lock(&fs->lock); | ||
| 624 | root = fs->root; | ||
| 625 | path_get(&root); | ||
| 626 | read_unlock(&fs->lock); | ||
| 627 | put_fs_struct(fs); | ||
| 628 | |||
| 629 | ret = -ENOMEM; | 611 | ret = -ENOMEM; |
| 630 | p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL); | 612 | p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL); |
| 631 | if (!p) | 613 | if (!p) |
