From: Tetsuo Handa on
Kees Cook wrote:
> +static spinlock_t ptracer_relations_lock;
static DEFINE_SPINLOCK(ptracer_relations_lock);

> +static int yama_ptracer_add(struct task_struct *tracer,
> + struct task_struct *tracee)
> +{
> + struct ptrace_relation *relation;
> +
> + relation = kzalloc(sizeof(*relation), GFP_KERNEL);
You can use kmalloc() since all fields are initialized within this function.
> + if (!relation)
> + return -ENOMEM;
> + relation->tracer = tracer;
> + relation->tracee = tracee;
> + spin_lock(&ptracer_relations_lock);
> + list_add(&relation->node, &ptracer_relations);
> + spin_unlock(&ptracer_relations_lock);
> +
> + return 0;
> +}

> +static int ptracer_exception_found(struct task_struct *tracer,
> + struct task_struct *tracee)
> +{
> + int rc = 0;
> + struct ptrace_relation *relation;
> + struct task_struct *parent = NULL;
> +
> + spin_lock(&ptracer_relations_lock);
> + list_for_each_entry(relation, &ptracer_relations, node)
> + if (relation->tracee == tracee) {
> + parent = relation->tracer;
> + break;
> + }
> + if (task_is_descendant(parent, tracer))
> + rc = 1;
> + spin_unlock(&ptracer_relations_lock);

Can't we release ptracer_relations_lock before calling
task_is_descendant() since task_is_descendant() won't
access "struct ptrace_relation" on ptracer_relations list.

> @@ -32,27 +204,20 @@ static int yama_ptrace_access_check(struct task_struct *child,
> {
> int rc;
>
> + /* If standard caps disallows it, so does Yama. We should
> + * should only tighten restrictions further.
s/should should/should/
> + */

> @@ -221,6 +388,8 @@ static __init int yama_init(void)
>
> printk(KERN_INFO "Yama: becoming mindful.\n");
>
> + spin_lock_init(&ptracer_relations_lock);
> +
You can statically initialize by using DEFINE_SPINLOCK().
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Kees Cook on
Hi Tetsuo,

On Wed, Jun 30, 2010 at 10:09:54AM +0900, Tetsuo Handa wrote:
> Kees Cook wrote:
> > +static spinlock_t ptracer_relations_lock;
> static DEFINE_SPINLOCK(ptracer_relations_lock);

Ah, very cool, I missed that while reading through spinlock code. :)

> > + relation = kzalloc(sizeof(*relation), GFP_KERNEL);
> You can use kmalloc() since all fields are initialized within this function.

I wasn't sure if list_add needed a zeroed ->node, so I opted for safety
here. Is list_add safe to use on an uninitialized ->node? (Looks like it
is on code review, I'll just use regular kmalloc.)

> > +static int ptracer_exception_found(struct task_struct *tracer,
> > + struct task_struct *tracee)
> > +{
> > + int rc = 0;
> > + struct ptrace_relation *relation;
> > + struct task_struct *parent = NULL;
> > +
> > + spin_lock(&ptracer_relations_lock);
> > + list_for_each_entry(relation, &ptracer_relations, node)
> > + if (relation->tracee == tracee) {
> > + parent = relation->tracer;
> > + break;
> > + }
> > + if (task_is_descendant(parent, tracer))
> > + rc = 1;
> > + spin_unlock(&ptracer_relations_lock);
>
> Can't we release ptracer_relations_lock before calling
> task_is_descendant() since task_is_descendant() won't
> access "struct ptrace_relation" on ptracer_relations list.

This is where it gets a little funny. I need to keep that lock so that
task_is_descendant isn't racing yama_task_free. I don't want to be in
the position where I've left the lock only to have another CPU free the
task_struct that was just located, so I have to keep the lock until I've
finished using "parent". (And I can't take the task with get_task since
it's already too late, and if I take it during _add, the task will never
be freed.)

> > @@ -32,27 +204,20 @@ static int yama_ptrace_access_check(struct task_struct *child,
> > {
> > int rc;
> >
> > + /* If standard caps disallows it, so does Yama. We should
> > + * should only tighten restrictions further.
> s/should should/should/

Agh, thanks.

-Kees

--
Kees Cook
Ubuntu Security Team
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Serge E. Hallyn on
Quoting Kees Cook (kees.cook(a)canonical.com):
> Some application suites have external crash handlers that depend on
> being able to use PTRACE to generate crash reports (KDE, Chromium, etc).
> Since the inferior process generally knows the PID of the debugger,
> it can use PR_SET_PTRACER to allow a specific PID and its descendants
> to perform the PTRACE instead of only a direct ancestor.
>
> Signed-off-by: Kees Cook <kees.cook(a)canonical.com>
> ---

Hi Kees - very nice, overall. One little note though:

> @@ -32,27 +204,20 @@ static int yama_ptrace_access_check(struct task_struct *child,
> {
> int rc;
>
> + /* If standard caps disallows it, so does Yama. We should
> + * should only tighten restrictions further.
> + */
> rc = cap_ptrace_access_check(child, mode);

This means that if capable(CAP_SYS_PTRACE) we'll always shortcut
here, so

> - if (rc != 0)
> + if (rc)
> return rc;
>
> /* require ptrace target be a child of ptracer on attach */
> - if (mode == PTRACE_MODE_ATTACH && ptrace_scope &&
> - !capable(CAP_SYS_PTRACE)) {
> - struct task_struct *walker = child;
> -
> - rcu_read_lock();
> - read_lock(&tasklist_lock);
> - while (walker->pid > 0) {
> - if (walker == current)
> - break;
> - walker = walker->real_parent;
> - }
> - if (walker->pid == 0)
> - rc = -EPERM;
> - read_unlock(&tasklist_lock);
> - rcu_read_unlock();
> - }
> + if (mode == PTRACE_MODE_ATTACH &&
> + ptrace_scope &&
> + !capable(CAP_SYS_PTRACE) &&

You don't need the CAP_SYS_PTRACE check here AFAICS.

> + !task_is_descendant(current, child) &&
> + !ptracer_exception_found(current, child))
> + rc = -EPERM;
>
> if (rc) {
> char name[sizeof(current->comm)];
> @@ -170,6 +335,8 @@ static struct security_operations yama_ops = {
> .ptrace_access_check = yama_ptrace_access_check,
> .inode_follow_link = yama_inode_follow_link,
> .path_link = yama_path_link,
> + .task_prctl = yama_task_prctl,
> + .task_free = yama_task_free,
> };
>
> #ifdef CONFIG_SYSCTL
> @@ -221,6 +388,8 @@ static __init int yama_init(void)
>
> printk(KERN_INFO "Yama: becoming mindful.\n");
>
> + spin_lock_init(&ptracer_relations_lock);
> +
> if (register_security(&yama_ops))
> panic("Yama: kernel registration failed.\n");
>
> --
> 1.7.1
>
>
> --
> Kees Cook
> Ubuntu Security Team
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo(a)vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Kees Cook on
Hi Serge,

On Tue, Jun 29, 2010 at 10:56:09PM -0500, Serge E. Hallyn wrote:
> Quoting Kees Cook (kees.cook(a)canonical.com):
> > Some application suites have external crash handlers that depend on
> > being able to use PTRACE to generate crash reports (KDE, Chromium, etc).
> > Since the inferior process generally knows the PID of the debugger,
> > it can use PR_SET_PTRACER to allow a specific PID and its descendants
> > to perform the PTRACE instead of only a direct ancestor.
> >
> > Signed-off-by: Kees Cook <kees.cook(a)canonical.com>
> > ---
>
> Hi Kees - very nice, overall. One little note though:

Thanks for looking it over!

> > rc = cap_ptrace_access_check(child, mode);
>
> This means that if capable(CAP_SYS_PTRACE) we'll always shortcut
> here, so
>
> > + if (mode == PTRACE_MODE_ATTACH &&
> > + ptrace_scope &&
> > + !capable(CAP_SYS_PTRACE) &&
> > + !task_is_descendant(current, child) &&
> > + !ptracer_exception_found(current, child))
> > + rc = -EPERM;
>
> You don't need the CAP_SYS_PTRACE check here AFAICS.

I don't think that's true -- the capable(CAP_SYS_PTRACE) tests
are always done in the negative since we only ever abort with error
instead of forcing an early "okay" (see also __ptrace_may_access() in
kernel/ptrace.c, where capable(CAP_SYS_PTRACE) is called repeatedly while
evaluating various negative conditions).

For cap_ptrace_access_check, capable(CAP_SYS_PTRACE) is only tested if
the tracee's process permitted caps are not a subset of the tracer's.
i.e. cap_ptrace_access_check will return 0 when either cap_issubset
or capable. In the case of normal user processes or a tracer
with greater caps, capable(CAP_SYS_PTRACE) will never be tested in
cap_ptrace_access_check.

As a result, I have to include it in the test here too. I guess it's
arguable that I should move it to the end of the series of &&s, but it
logically doesn't really matter.

-Kees

(Interestingly, this means that having CAP_SYS_PTRACE means a process
effectively has all capabilities...)

--
Kees Cook
Ubuntu Security Team
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
From: Christoph Hellwig on

Err, no. This is just a very clear sign that your ptrace restrictions
were completely wrong to start with and break applications left, right
and center. Just get rid of it instead of letting workarounds for your
bad design creep into the core kernel and applications.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo(a)vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/