[PATCH 9/9] ptrace: Don't change __state
Eric W. Biederman
ebiederm at xmission.com
Thu Apr 28 09:50:19 PDT 2022
Oleg Nesterov <oleg at redhat.com> writes:
> On 04/27, Eric W. Biederman wrote:
>>
>> "Eric W. Biederman" <ebiederm at xmission.com> writes:
>>
>> > diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h
>> > index 3c8b34876744..1947c85aa9d9 100644
>> > --- a/include/linux/sched/signal.h
>> > +++ b/include/linux/sched/signal.h
>> > @@ -437,7 +437,8 @@ extern void signal_wake_up_state(struct task_struct *t, unsigned int state);
>> >
>> > static inline void signal_wake_up(struct task_struct *t, bool resume)
>> > {
>> > - signal_wake_up_state(t, resume ? TASK_WAKEKILL : 0);
>> > + bool wakekill = resume && !(t->jobctl & JOBCTL_DELAY_WAKEKILL);
>> > + signal_wake_up_state(t, wakekill ? TASK_WAKEKILL : 0);
>> > }
>> > static inline void ptrace_signal_wake_up(struct task_struct *t, bool resume)
>> > {
>>
>> Grrr. While looking through everything today I have realized that there
>> is a bug.
>>
>> Suppose we have 3 processes: TRACER, TRACEE, KILLER.
>>
>> Meanwhile TRACEE is in the middle of ptrace_stop, just after siglock has
>> been dropped.
>>
>> The TRACER process has performed ptrace_attach on TRACEE and is in the
>> middle of a ptrace operation and has just set JOBCTL_DELAY_WAKEKILL.
>>
>> Then comes in the KILLER process and sends the TRACEE a SIGKILL.
>> The TRACEE __state remains TASK_TRACED, as designed.
>>
>> The bug appears when the TRACEE makes it to schedule(). Inside
>> schedule there is a call to signal_pending_state() which notices
>> a SIGKILL is pending and refuses to sleep.
>
> And I think this is fine. This doesn't really differ from the case
> when the tracee was killed before it takes siglock.
Hmm. Maybe.
> The only problem (afaics) is that, once we introduce JOBCTL_TRACED,
> ptrace_stop() can leak this flag. That is why I suggested to clear
> it along with LISTENING/DELAY_WAKEKILL before return, exactly because
> schedule() won't block if fatal_signal_pending() is true.
>
> But may be I misunderstood you concern?
Prior to JOBCTL_DELAY_WAKEKILL once __state was set to __TASK_TRACED
we were guaranteed that schedule() would stop if a SIGKILL was
received after that point. As well as being immune from wake-ups
from SIGKILL.
I guess we are immune from wake-ups with JOBCTL_DELAY_WAKEKILL as I have
implemented it.
The practical concern then seems to be that we are not guaranteed
wait_task_inactive will succeed. Which means that it must continue
to include the TASK_TRACED bit.
Previously we were actually guaranteed in ptrace_check_attach that after
ptrace_freeze_traced would succeed as any pending fatal signal would
cause ptrace_freeze_traced to fail. Any incoming fatal signal would not
stop schedule from sleeping. The ptraced task would continue to be
ptraced, as all other ptrace operations are blocked by virtue of ptrace
being single threaded.
I think in my tired mind yesterday I thought it would messing things
up after schedule decided to sleep. Still I would like to be able to
let wait_task_inactive not care about the state of the process it is
going to sleep for.
Eric
More information about the linux-um
mailing list