Don't clear p_ptevents on normal SIGKILL delivery

The ptrace() user has the option of discarding the signal. In such a
case, p_ptevents should not be modified. If the ptrace() user decides to
send a SIGKILL, ptevents will be cleared in ptracestop(). procfs events
do not have the capability to discard the signal, so continue to clear
the mask in that case.

Reviewed by:	jhb (initial revision)
MFC after:	1 week
Sponsored by:	Dell EMC
Differential Revision:	https://reviews.freebsd.org/D9939
This commit is contained in:
badger 2017-03-16 13:03:31 +00:00
parent 8911b8bd0f
commit 643b691b05
2 changed files with 76 additions and 4 deletions

View File

@ -2179,11 +2179,9 @@ tdsendsignal(struct proc *p, struct thread *td, int sig, ksiginfo_t *ksi)
if (action == SIG_HOLD &&
!((prop & SIGPROP_CONT) && (p->p_flag & P_STOPPED_SIG)))
return (ret);
/*
* SIGKILL: Remove procfs STOPEVENTs and ptrace events.
*/
/* SIGKILL: Remove procfs STOPEVENTs. */
if (sig == SIGKILL) {
p->p_ptevents = 0;
/* from procfs_ioctl.c: PIOCBIC */
p->p_stops = 0;
/* from procfs_ioctl.c: PIOCCONT */

View File

@ -2919,6 +2919,79 @@ ATF_TC_BODY(ptrace__parent_terminate_with_pending_sigstop2, tc)
terminate_with_pending_sigstop(false);
}
/*
* Verify that after ptrace() discards a SIGKILL signal, the event mask
* is not modified.
*/
ATF_TC_WITHOUT_HEAD(ptrace__event_mask_sigkill_discard);
ATF_TC_BODY(ptrace__event_mask_sigkill_discard, tc)
{
struct ptrace_lwpinfo pl;
pid_t fpid, wpid;
int status, event_mask, new_event_mask;
ATF_REQUIRE((fpid = fork()) != -1);
if (fpid == 0) {
trace_me();
raise(SIGSTOP);
exit(0);
}
/* The first wait() should report the stop from trace_me(). */
wpid = waitpid(fpid, &status, 0);
ATF_REQUIRE(wpid == fpid);
ATF_REQUIRE(WIFSTOPPED(status));
ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
/* Set several unobtrusive event bits. */
event_mask = PTRACE_EXEC | PTRACE_FORK | PTRACE_LWP;
ATF_REQUIRE(ptrace(PT_SET_EVENT_MASK, wpid, (caddr_t)&event_mask,
sizeof(event_mask)) == 0);
/* Send a SIGKILL without using ptrace. */
ATF_REQUIRE(kill(fpid, SIGKILL) == 0);
/* Continue the child ignoring the SIGSTOP. */
ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
/* The next stop should be due to the SIGKILL. */
wpid = waitpid(fpid, &status, 0);
ATF_REQUIRE(wpid == fpid);
ATF_REQUIRE(WIFSTOPPED(status));
ATF_REQUIRE(WSTOPSIG(status) == SIGKILL);
ATF_REQUIRE(ptrace(PT_LWPINFO, wpid, (caddr_t)&pl, sizeof(pl)) != -1);
ATF_REQUIRE(pl.pl_flags & PL_FLAG_SI);
ATF_REQUIRE(pl.pl_siginfo.si_signo == SIGKILL);
/* Continue the child ignoring the SIGKILL. */
ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
/* The next wait() should report the stop from SIGSTOP. */
wpid = waitpid(fpid, &status, 0);
ATF_REQUIRE(wpid == fpid);
ATF_REQUIRE(WIFSTOPPED(status));
ATF_REQUIRE(WSTOPSIG(status) == SIGSTOP);
/* Check the current event mask. It should not have changed. */
new_event_mask = 0;
ATF_REQUIRE(ptrace(PT_GET_EVENT_MASK, wpid, (caddr_t)&new_event_mask,
sizeof(new_event_mask)) == 0);
ATF_REQUIRE(event_mask == new_event_mask);
/* Continue the child to let it exit. */
ATF_REQUIRE(ptrace(PT_CONTINUE, fpid, (caddr_t)1, 0) == 0);
/* The last event should be for the child process's exit. */
wpid = waitpid(fpid, &status, 0);
ATF_REQUIRE(WIFEXITED(status));
ATF_REQUIRE(WEXITSTATUS(status) == 0);
wpid = wait(&status);
ATF_REQUIRE(wpid == -1);
ATF_REQUIRE(errno == ECHILD);
}
ATF_TP_ADD_TCS(tp)
{
@ -2965,6 +3038,7 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, ptrace__PT_CONTINUE_with_signal_thread_sigmask);
ATF_TP_ADD_TC(tp, ptrace__parent_terminate_with_pending_sigstop1);
ATF_TP_ADD_TC(tp, ptrace__parent_terminate_with_pending_sigstop2);
ATF_TP_ADD_TC(tp, ptrace__event_mask_sigkill_discard);
return (atf_no_error());
}