Subject: Re: ptrace() vs. SIGKILL?
To: None <tech-security@netbsd.org, tech-kern@netbsd.org>
From: der Mouse <mouse@Rodents.Montreal.QC.CA>
List: tech-kern
Date: 12/07/2002 23:42:42
>>> Hmmm....  If ptrace() could prevent SIGKILL from taking effect for
>>> anyone but the superuser then that would be a very serious security
>>> bug, not just on un-hardened boxes.

>> Greg, meet Reality.

>> I've never seen a system with ptrace that _didn't_ work this way.
>> (Not that I've looked very hard.)

> I think you're referring to the fact that a ptrace'd process will
> stop and signal the tracer when it receives SIGKILL.

More generally, that SIGKILL and SIGSTOP are not special with respect
to ptrace signal handling.  UTSL....

        /*
         * If proc is traced, always give parent a chance.
         */
        if (p->p_flag & P_TRACED)
                action = SIG_DFL;

> However I mean very literally "if ptrace() can _prevent_ SIGKILL from
> ultimately taking effect" then it's a security bug waiting to happen.

I repeat: Greg, meet Reality.

Try ftp.netbsd.org:/pub/NetBSD/misc/mouse/sigkill.c on your favorite
system.

Here's what I get:

[Red] 135> sigkill
p1 started, pid 21811
p2 started, pid 21812
p3 started, pid 21813
p1 tick
p2 attaching to 21811
p2 waiting for 21811 to stop
p2 stop wait shows signal 17
p2 telling 21811 to continue
p1 tick
p3 SIGKILLing 21811
p2 waiting for 21811 to stop
p2 stop wait shows signal 9
p3 sent SIGKILL
p2 telling 21811 to continue, signal 0
p1 tick
p2 waiting for 21811 to stop
p1 tick
p1 tick

I would love to hear about any ptrace-supporting OS which doesn't work
this way.

> The [ptrace(2)] manual warns that such child processes [which have
> called PTRACE_TRACE_ME but have not been traced by their parents]
> cannot be made to continue without using ptrace(), which if true
> would make it much harder to clean up after such an attack.

It's sort-of true.  If their parent is killed, they are nuked at the
same time as they are reparented to init.  UTSL again, this time
exit1(), in kern_exit.c:

        /*
         * Give orphaned children to init(8).
         */
        q = p->p_children.lh_first;
        if (q)          /* only need this if any child is S_ZOMB */
                wakeup((caddr_t)initproc);
        for (; q != 0; q = nq) {
                nq = q->p_sibling.le_next;
                proc_reparent(q, initproc);
                /*
                 * Traced processes are killed
                 * since their existence means someone is screwing up.
                 */
                if (q->p_flag & P_TRACED) {
                        q->p_flag &= ~(P_TRACED|P_WAITED|P_FSTRACE);
                        psignal(q, SIGKILL);
                }
        }

/~\ The ASCII				der Mouse
\ / Ribbon Campaign
 X  Against HTML	       mouse@rodents.montreal.qc.ca
/ \ Email!	     7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B