@@ -386,24 +386,52 @@ static int check_ptrace_options(unsigned long data)
386386 return 0 ;
387387}
388388
389+ static inline void ptrace_set_stopped (struct task_struct * task )
390+ {
391+ guard (spinlock )(& task -> sighand -> siglock );
392+
393+ /*
394+ * If the task is already STOPPED, set JOBCTL_TRAP_STOP and
395+ * TRAPPING, and kick it so that it transits to TRACED. TRAPPING
396+ * will be cleared if the child completes the transition or any
397+ * event which clears the group stop states happens. We'll wait
398+ * for the transition to complete before returning from this
399+ * function.
400+ *
401+ * This hides STOPPED -> RUNNING -> TRACED transition from the
402+ * attaching thread but a different thread in the same group can
403+ * still observe the transient RUNNING state. IOW, if another
404+ * thread's WNOHANG wait(2) on the stopped tracee races against
405+ * ATTACH, the wait(2) may fail due to the transient RUNNING.
406+ *
407+ * The following task_is_stopped() test is safe as both transitions
408+ * in and out of STOPPED are protected by siglock.
409+ */
410+ if (task_is_stopped (task ) &&
411+ task_set_jobctl_pending (task , JOBCTL_TRAP_STOP | JOBCTL_TRAPPING )) {
412+ task -> jobctl &= ~JOBCTL_STOPPED ;
413+ signal_wake_up_state (task , __TASK_STOPPED );
414+ }
415+ }
416+
389417static int ptrace_attach (struct task_struct * task , long request ,
390418 unsigned long addr ,
391419 unsigned long flags )
392420{
393421 bool seize = (request == PTRACE_SEIZE );
394422 int retval ;
395423
396- retval = - EIO ;
397424 if (seize ) {
398425 if (addr != 0 )
399- goto out ;
426+ return - EIO ;
400427 /*
401428 * This duplicates the check in check_ptrace_options() because
402429 * ptrace_attach() and ptrace_setoptions() have historically
403430 * used different error codes for unknown ptrace options.
404431 */
405432 if (flags & ~(unsigned long )PTRACE_O_MASK )
406- goto out ;
433+ return - EIO ;
434+
407435 retval = check_ptrace_options (flags );
408436 if (retval )
409437 return retval ;
@@ -414,88 +442,54 @@ static int ptrace_attach(struct task_struct *task, long request,
414442
415443 audit_ptrace (task );
416444
417- retval = - EPERM ;
418445 if (unlikely (task -> flags & PF_KTHREAD ))
419- goto out ;
446+ return - EPERM ;
420447 if (same_thread_group (task , current ))
421- goto out ;
448+ return - EPERM ;
422449
423450 /*
424451 * Protect exec's credential calculations against our interference;
425452 * SUID, SGID and LSM creds get determined differently
426453 * under ptrace.
427454 */
428- retval = - ERESTARTNOINTR ;
429- if (mutex_lock_interruptible (& task -> signal -> cred_guard_mutex ))
430- goto out ;
455+ scoped_cond_guard (mutex_intr , return - ERESTARTNOINTR ,
456+ & task -> signal -> cred_guard_mutex ) {
431457
432- task_lock ( task );
433- retval = __ptrace_may_access (task , PTRACE_MODE_ATTACH_REALCREDS );
434- task_unlock ( task );
435- if ( retval )
436- goto unlock_creds ;
458+ scoped_guard ( task_lock , task ) {
459+ retval = __ptrace_may_access (task , PTRACE_MODE_ATTACH_REALCREDS );
460+ if ( retval )
461+ return retval ;
462+ }
437463
438- write_lock_irq (& tasklist_lock );
439- retval = - EPERM ;
440- if (unlikely (task -> exit_state ))
441- goto unlock_tasklist ;
442- if (task -> ptrace )
443- goto unlock_tasklist ;
464+ scoped_guard (write_lock_irq , & tasklist_lock ) {
465+ if (unlikely (task -> exit_state ))
466+ return - EPERM ;
467+ if (task -> ptrace )
468+ return - EPERM ;
444469
445- task -> ptrace = flags ;
470+ task -> ptrace = flags ;
446471
447- ptrace_link (task , current );
472+ ptrace_link (task , current );
448473
449- /* SEIZE doesn't trap tracee on attach */
450- if (!seize )
451- send_sig_info (SIGSTOP , SEND_SIG_PRIV , task );
474+ /* SEIZE doesn't trap tracee on attach */
475+ if (!seize )
476+ send_sig_info (SIGSTOP , SEND_SIG_PRIV , task );
452477
453- spin_lock (& task -> sighand -> siglock );
478+ ptrace_set_stopped (task );
479+ }
480+ }
454481
455482 /*
456- * If the task is already STOPPED, set JOBCTL_TRAP_STOP and
457- * TRAPPING, and kick it so that it transits to TRACED. TRAPPING
458- * will be cleared if the child completes the transition or any
459- * event which clears the group stop states happens. We'll wait
460- * for the transition to complete before returning from this
461- * function.
462- *
463- * This hides STOPPED -> RUNNING -> TRACED transition from the
464- * attaching thread but a different thread in the same group can
465- * still observe the transient RUNNING state. IOW, if another
466- * thread's WNOHANG wait(2) on the stopped tracee races against
467- * ATTACH, the wait(2) may fail due to the transient RUNNING.
468- *
469- * The following task_is_stopped() test is safe as both transitions
470- * in and out of STOPPED are protected by siglock.
483+ * We do not bother to change retval or clear JOBCTL_TRAPPING
484+ * if wait_on_bit() was interrupted by SIGKILL. The tracer will
485+ * not return to user-mode, it will exit and clear this bit in
486+ * __ptrace_unlink() if it wasn't already cleared by the tracee;
487+ * and until then nobody can ptrace this task.
471488 */
472- if (task_is_stopped (task ) &&
473- task_set_jobctl_pending (task , JOBCTL_TRAP_STOP | JOBCTL_TRAPPING )) {
474- task -> jobctl &= ~JOBCTL_STOPPED ;
475- signal_wake_up_state (task , __TASK_STOPPED );
476- }
477-
478- spin_unlock (& task -> sighand -> siglock );
479-
480- retval = 0 ;
481- unlock_tasklist :
482- write_unlock_irq (& tasklist_lock );
483- unlock_creds :
484- mutex_unlock (& task -> signal -> cred_guard_mutex );
485- out :
486- if (!retval ) {
487- /*
488- * We do not bother to change retval or clear JOBCTL_TRAPPING
489- * if wait_on_bit() was interrupted by SIGKILL. The tracer will
490- * not return to user-mode, it will exit and clear this bit in
491- * __ptrace_unlink() if it wasn't already cleared by the tracee;
492- * and until then nobody can ptrace this task.
493- */
494- wait_on_bit (& task -> jobctl , JOBCTL_TRAPPING_BIT , TASK_KILLABLE );
495- proc_ptrace_connector (task , PTRACE_ATTACH );
496- }
489+ wait_on_bit (& task -> jobctl , JOBCTL_TRAPPING_BIT , TASK_KILLABLE );
490+ proc_ptrace_connector (task , PTRACE_ATTACH );
497491
498- return retval ;
492+ return 0 ;
499493}
500494
501495/**
0 commit comments