Skip to content

Commit bfedb58

Browse files
committed
mm: Add a user_ns owner to mm_struct and fix ptrace permission checks
During exec dumpable is cleared if the file that is being executed is not readable by the user executing the file. A bug in ptrace_may_access allows reading the file if the executable happens to enter into a subordinate user namespace (aka clone(CLONE_NEWUSER), unshare(CLONE_NEWUSER), or setns(fd, CLONE_NEWUSER). This problem is fixed with only necessary userspace breakage by adding a user namespace owner to mm_struct, captured at the time of exec, so it is clear in which user namespace CAP_SYS_PTRACE must be present in to be able to safely give read permission to the executable. The function ptrace_may_access is modified to verify that the ptracer has CAP_SYS_ADMIN in task->mm->user_ns instead of task->cred->user_ns. This ensures that if the task changes it's cred into a subordinate user namespace it does not become ptraceable. The function ptrace_attach is modified to only set PT_PTRACE_CAP when CAP_SYS_PTRACE is held over task->mm->user_ns. The intent of PT_PTRACE_CAP is to be a flag to note that whatever permission changes the task might go through the tracer has sufficient permissions for it not to be an issue. task->cred->user_ns is always the same as or descendent of mm->user_ns. Which guarantees that having CAP_SYS_PTRACE over mm->user_ns is the worst case for the tasks credentials. To prevent regressions mm->dumpable and mm->user_ns are not considered when a task has no mm. As simply failing ptrace_may_attach causes regressions in privileged applications attempting to read things such as /proc/<pid>/stat Cc: [email protected] Acked-by: Kees Cook <[email protected]> Tested-by: Cyrill Gorcunov <[email protected]> Fixes: 8409cca ("userns: allow ptrace from non-init user namespaces") Signed-off-by: "Eric W. Biederman" <[email protected]>
1 parent 9c76358 commit bfedb58

4 files changed

Lines changed: 20 additions & 18 deletions

File tree

include/linux/mm_types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ struct mm_struct {
473473
*/
474474
struct task_struct __rcu *owner;
475475
#endif
476+
struct user_namespace *user_ns;
476477

477478
/* store ref to file /proc/<pid>/exe symlink points to */
478479
struct file __rcu *exe_file;

kernel/fork.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,8 @@ static void mm_init_owner(struct mm_struct *mm, struct task_struct *p)
745745
#endif
746746
}
747747

748-
static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
748+
static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p,
749+
struct user_namespace *user_ns)
749750
{
750751
mm->mmap = NULL;
751752
mm->mm_rb = RB_ROOT;
@@ -785,6 +786,7 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
785786
if (init_new_context(p, mm))
786787
goto fail_nocontext;
787788

789+
mm->user_ns = get_user_ns(user_ns);
788790
return mm;
789791

790792
fail_nocontext:
@@ -830,7 +832,7 @@ struct mm_struct *mm_alloc(void)
830832
return NULL;
831833

832834
memset(mm, 0, sizeof(*mm));
833-
return mm_init(mm, current);
835+
return mm_init(mm, current, current_user_ns());
834836
}
835837

836838
/*
@@ -845,6 +847,7 @@ void __mmdrop(struct mm_struct *mm)
845847
destroy_context(mm);
846848
mmu_notifier_mm_destroy(mm);
847849
check_mm(mm);
850+
put_user_ns(mm->user_ns);
848851
free_mm(mm);
849852
}
850853
EXPORT_SYMBOL_GPL(__mmdrop);
@@ -1126,7 +1129,7 @@ static struct mm_struct *dup_mm(struct task_struct *tsk)
11261129

11271130
memcpy(mm, oldmm, sizeof(*mm));
11281131

1129-
if (!mm_init(mm, tsk))
1132+
if (!mm_init(mm, tsk, mm->user_ns))
11301133
goto fail_nomem;
11311134

11321135
err = dup_mmap(mm, oldmm);

kernel/ptrace.c

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode)
220220
static int __ptrace_may_access(struct task_struct *task, unsigned int mode)
221221
{
222222
const struct cred *cred = current_cred(), *tcred;
223-
int dumpable = 0;
223+
struct mm_struct *mm;
224224
kuid_t caller_uid;
225225
kgid_t caller_gid;
226226

@@ -271,16 +271,11 @@ static int __ptrace_may_access(struct task_struct *task, unsigned int mode)
271271
return -EPERM;
272272
ok:
273273
rcu_read_unlock();
274-
smp_rmb();
275-
if (task->mm)
276-
dumpable = get_dumpable(task->mm);
277-
rcu_read_lock();
278-
if (dumpable != SUID_DUMP_USER &&
279-
!ptrace_has_cap(__task_cred(task)->user_ns, mode)) {
280-
rcu_read_unlock();
281-
return -EPERM;
282-
}
283-
rcu_read_unlock();
274+
mm = task->mm;
275+
if (mm &&
276+
((get_dumpable(mm) != SUID_DUMP_USER) &&
277+
!ptrace_has_cap(mm->user_ns, mode)))
278+
return -EPERM;
284279

285280
return security_ptrace_access_check(task, mode);
286281
}
@@ -331,6 +326,11 @@ static int ptrace_attach(struct task_struct *task, long request,
331326

332327
task_lock(task);
333328
retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS);
329+
if (!retval) {
330+
struct mm_struct *mm = task->mm;
331+
if (mm && ns_capable(mm->user_ns, CAP_SYS_PTRACE))
332+
flags |= PT_PTRACE_CAP;
333+
}
334334
task_unlock(task);
335335
if (retval)
336336
goto unlock_creds;
@@ -344,10 +344,6 @@ static int ptrace_attach(struct task_struct *task, long request,
344344

345345
if (seize)
346346
flags |= PT_SEIZED;
347-
rcu_read_lock();
348-
if (ns_capable(__task_cred(task)->user_ns, CAP_SYS_PTRACE))
349-
flags |= PT_PTRACE_CAP;
350-
rcu_read_unlock();
351347
task->ptrace = flags;
352348

353349
__ptrace_link(task, current);

mm/init-mm.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <linux/cpumask.h>
77

88
#include <linux/atomic.h>
9+
#include <linux/user_namespace.h>
910
#include <asm/pgtable.h>
1011
#include <asm/mmu.h>
1112

@@ -21,5 +22,6 @@ struct mm_struct init_mm = {
2122
.mmap_sem = __RWSEM_INITIALIZER(init_mm.mmap_sem),
2223
.page_table_lock = __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock),
2324
.mmlist = LIST_HEAD_INIT(init_mm.mmlist),
25+
.user_ns = &init_user_ns,
2426
INIT_MM_CONTEXT(init_mm)
2527
};

0 commit comments

Comments
 (0)