git: dabf006a638f - main - Add per-process flag to disable logsigexit

From: Kyle Evans <kevans_at_FreeBSD.org>
Date: Sat, 14 Dec 2024 05:18:53 UTC
The branch main has been updated by kevans:

URL: https://cgit.FreeBSD.org/src/commit/?id=dabf006a638fdc44cdcf69731de8ac83959db731

commit dabf006a638fdc44cdcf69731de8ac83959db731
Author:     Kyle Evans <kevans@FreeBSD.org>
AuthorDate: 2024-12-14 05:17:20 +0000
Commit:     Kyle Evans <kevans@FreeBSD.org>
CommitDate: 2024-12-14 05:18:30 +0000

    Add per-process flag to disable logsigexit
    
    I added a third value for kern.logsigexit to mean 'auto' as an abundance
    of caution, but I don't know how much it matters -- that can be easily
    consolidated back to boolean-ish.
    
    This is primarily targeted towards people running test suites under CI
    (e.g. buildbot, jenkins). Oftentimes tests entail segfaults that are
    expected, and logs get spammed -- this can be particularly high volume
    depending on the application. Per-process control of this behavior is
    desirable because they may still want to be logging legitimate
    segfaults, so the system-wide atomic bomb kern.logsigexit=0 is not a
    great option.
    
    This adds a process flag to disable it, controllable via
    procctl(2)/proccontrol(1); the latter knows it as "sigexitlog" due to
    its length, but it's referred to almost everywhere else as
    "sigexit_log."
    
    Reviewed by:    kib (earlier version), pstef
    Differential Revision:  https://reviews.freebsd.org/D21903
---
 lib/libsys/procctl.2                  | 37 +++++++++++++++++++++++--
 sys/compat/freebsd32/freebsd32_misc.c |  3 ++
 sys/kern/kern_fork.c                  |  3 +-
 sys/kern/kern_procctl.c               | 52 +++++++++++++++++++++++++++++++++++
 sys/kern/kern_sig.c                   |  8 +++++-
 sys/sys/proc.h                        |  3 ++
 sys/sys/procctl.h                     |  6 ++++
 usr.bin/proccontrol/proccontrol.1     |  7 +++--
 usr.bin/proccontrol/proccontrol.c     | 24 ++++++++++++++++
 9 files changed, 137 insertions(+), 6 deletions(-)

diff --git a/lib/libsys/procctl.2 b/lib/libsys/procctl.2
index 2d443a1ae4bd..75804ba243f1 100644
--- a/lib/libsys/procctl.2
+++ b/lib/libsys/procctl.2
@@ -27,7 +27,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd December 4, 2024
+.Dd December 14, 2024
 .Dt PROCCTL 2
 .Os
 .Sh NAME
@@ -79,7 +79,7 @@ All status query requests
 require the caller to have the right to observe the target.
 .Pp
 The following commands are supported:
-.Bl -tag -width PROC_TRAPCAP_STATUS
+.Bl -tag -width PROC_LOGSIGEXIT_STATUS
 .It Dv PROC_ASLR_CTL
 Controls Address Space Layout Randomization (ASLR) in program
 images created
@@ -127,6 +127,39 @@ If the currently executed image in the process itself has ASLR enabled,
 the
 .Dv PROC_ASLR_ACTIVE
 flag is or-ed with the value listed above.
+.It Dv PROC_LOGSIGEXIT_CTL
+Controls the logging of exits due to signals that would normally cause a core
+dump.
+The
+.Va arg
+parameter must point to an integer variable holding one of the following values:
+.Bl -tag -width PROC_LOGSIGEXIT_FORCE_DISABLE
+.It Dv PROC_LOGSIGEXIT_FORCE_ENABLE
+Enables logging of exits due to signals that would normally cause a core dump.
+Logging is done via
+.Xr log 9
+with a log level of
+.Dv LOG_INFO .
+.It Dv PROC_LOGSIGEXIT_FORCE_DISABLE
+Disables the logging of exits due to signals that would normally cause a core
+dump.
+.It Dv PROC_LOGSIGEXIT_NOFORCE
+The logging behavior is delegated to the
+.Xr sysctl 3
+MIB variable
+.Va kern.logsigexit .
+.El
+.It Dv PROC_LOGSIGEXIT_STATUS
+Returns the current status of logging for the target process.
+The
+.Va arg
+parameter must point to an integer variable, where one of the following values
+is written:
+.Bl -tag -width PROC_LOGSIGEXIT_FORCE_DISABLE
+.It Dv PROC_LOGSIGEXIT_FORCE_ENABLE
+.It Dv PROC_LOGSIGEXIT_FORCE_DISABLE
+.It Dv PROC_LOGSIGEXIT_NOFORCE
+.El
 .It Dv PROC_PROTMAX_CTL
 Controls the maximum protection used for
 .Xr mmap 2
diff --git a/sys/compat/freebsd32/freebsd32_misc.c b/sys/compat/freebsd32/freebsd32_misc.c
index e6aea3acdecd..67ebb5d52589 100644
--- a/sys/compat/freebsd32/freebsd32_misc.c
+++ b/sys/compat/freebsd32/freebsd32_misc.c
@@ -3860,6 +3860,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
 	case PROC_TRAPCAP_CTL:
 	case PROC_NO_NEW_PRIVS_CTL:
 	case PROC_WXMAP_CTL:
+	case PROC_LOGSIGEXIT_CTL:
 		error = copyin(PTRIN(uap->data), &flags, sizeof(flags));
 		if (error != 0)
 			return (error);
@@ -3895,6 +3896,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
 	case PROC_TRAPCAP_STATUS:
 	case PROC_NO_NEW_PRIVS_STATUS:
 	case PROC_WXMAP_STATUS:
+	case PROC_LOGSIGEXIT_STATUS:
 		data = &flags;
 		break;
 	case PROC_PDEATHSIG_CTL:
@@ -3928,6 +3930,7 @@ freebsd32_procctl(struct thread *td, struct freebsd32_procctl_args *uap)
 	case PROC_TRAPCAP_STATUS:
 	case PROC_NO_NEW_PRIVS_STATUS:
 	case PROC_WXMAP_STATUS:
+	case PROC_LOGSIGEXIT_STATUS:
 		if (error == 0)
 			error = copyout(&flags, uap->data, sizeof(flags));
 		break;
diff --git a/sys/kern/kern_fork.c b/sys/kern/kern_fork.c
index 41f6a76c4fa1..a66bc4be62a8 100644
--- a/sys/kern/kern_fork.c
+++ b/sys/kern/kern_fork.c
@@ -496,7 +496,8 @@ do_fork(struct thread *td, struct fork_req *fr, struct proc *p2, struct thread *
 	    P2_ASLR_IGNSTART | P2_NOTRACE | P2_NOTRACE_EXEC |
 	    P2_PROTMAX_ENABLE | P2_PROTMAX_DISABLE | P2_TRAPCAP |
 	    P2_STKGAP_DISABLE | P2_STKGAP_DISABLE_EXEC | P2_NO_NEW_PRIVS |
-	    P2_WXORX_DISABLE | P2_WXORX_ENABLE_EXEC);
+	    P2_WXORX_DISABLE | P2_WXORX_ENABLE_EXEC | P2_LOGSIGEXIT_CTL |
+	    P2_LOGSIGEXIT_ENABLE);
 	p2->p_swtick = ticks;
 	if (p1->p_flag & P_PROFIL)
 		startprofclock(p2);
diff --git a/sys/kern/kern_procctl.c b/sys/kern/kern_procctl.c
index e7519f1b0de4..96365e192d3c 100644
--- a/sys/kern/kern_procctl.c
+++ b/sys/kern/kern_procctl.c
@@ -945,6 +945,46 @@ pdeathsig_status(struct thread *td, struct proc *p, void *data)
 	return (0);
 }
 
+static int
+logsigexit_ctl(struct thread *td, struct proc *p, void *data)
+{
+	int state;
+
+	PROC_LOCK_ASSERT(p, MA_OWNED);
+	state = *(int *)data;
+
+	switch (state) {
+	case PROC_LOGSIGEXIT_CTL_NOFORCE:
+		p->p_flag2 &= ~(P2_LOGSIGEXIT_CTL | P2_LOGSIGEXIT_ENABLE);
+		break;
+	case PROC_LOGSIGEXIT_CTL_FORCE_ENABLE:
+		p->p_flag2 |= P2_LOGSIGEXIT_CTL | P2_LOGSIGEXIT_ENABLE;
+		break;
+	case PROC_LOGSIGEXIT_CTL_FORCE_DISABLE:
+		p->p_flag2 |= P2_LOGSIGEXIT_CTL;
+		p->p_flag2 &= ~P2_LOGSIGEXIT_ENABLE;
+		break;
+	default:
+		return (EINVAL);
+	}
+	return (0);
+}
+
+static int
+logsigexit_status(struct thread *td, struct proc *p, void *data)
+{
+	int state;
+
+	if ((p->p_flag2 & P2_LOGSIGEXIT_CTL) == 0)
+		state = PROC_LOGSIGEXIT_CTL_NOFORCE;
+	else if ((p->p_flag2 & P2_LOGSIGEXIT_ENABLE) != 0)
+		state = PROC_LOGSIGEXIT_CTL_FORCE_ENABLE;
+	else
+		state = PROC_LOGSIGEXIT_CTL_FORCE_DISABLE;
+	*(int *)data = state;
+	return (0);
+}
+
 enum {
 	PCTL_SLOCKED,
 	PCTL_XLOCKED,
@@ -1100,6 +1140,18 @@ static const struct procctl_cmd_info procctl_cmds_info[] = {
 	      .need_candebug = false,
 	      .copyin_sz = 0, .copyout_sz = sizeof(int),
 	      .exec = wxmap_status, .copyout_on_error = false, },
+	[PROC_LOGSIGEXIT_CTL] =
+	    { .lock_tree = PCTL_SLOCKED, .one_proc = true,
+	      .esrch_is_einval = false, .no_nonnull_data = false,
+	      .need_candebug = true,
+	      .copyin_sz = sizeof(int), .copyout_sz = 0,
+	      .exec = logsigexit_ctl, .copyout_on_error = false, },
+	[PROC_LOGSIGEXIT_STATUS] =
+	    { .lock_tree = PCTL_UNLOCKED, .one_proc = true,
+	      .esrch_is_einval = false, .no_nonnull_data = false,
+	      .need_candebug = false,
+	      .copyin_sz = 0, .copyout_sz = sizeof(int),
+	      .exec = logsigexit_status, .copyout_on_error = false, },
 };
 
 int
diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c
index c59bfd035be7..d7aa932aa7e3 100644
--- a/sys/kern/kern_sig.c
+++ b/sys/kern/kern_sig.c
@@ -3606,11 +3606,17 @@ sigexit(struct thread *td, int sig)
 	struct proc *p = td->td_proc;
 	const char *coreinfo;
 	int rv;
+	bool logexit;
 
 	PROC_LOCK_ASSERT(p, MA_OWNED);
 	proc_set_p2_wexit(p);
 
 	p->p_acflag |= AXSIG;
+	if ((p->p_flag2 & P2_LOGSIGEXIT_CTL) == 0)
+		logexit = kern_logsigexit != 0;
+	else
+		logexit = (p->p_flag2 & P2_LOGSIGEXIT_ENABLE) != 0;
+
 	/*
 	 * We must be single-threading to generate a core dump.  This
 	 * ensures that the registers in the core file are up-to-date.
@@ -3649,7 +3655,7 @@ sigexit(struct thread *td, int sig)
 			coreinfo = " (no core dump - other error)";
 			break;
 		}
-		if (kern_logsigexit)
+		if (logexit)
 			log(LOG_INFO,
 			    "pid %d (%s), jid %d, uid %d: exited on "
 			    "signal %d%s\n", p->p_pid, p->p_comm,
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index 2615fa0dc275..786cc447dc2c 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -886,6 +886,9 @@ struct proc {
 #define	P2_MEMBAR_GLOBE		0x00400000	/* membar global expedited
 						   registered */
 
+#define	P2_LOGSIGEXIT_ENABLE	0x00800000	/* Disable logging on sigexit */
+#define	P2_LOGSIGEXIT_CTL	0x01000000	/* Override kern.logsigexit */
+
 /* Flags protected by proctree_lock, kept in p_treeflags. */
 #define	P_TREE_ORPHANED		0x00000001	/* Reparented, on orphan list */
 #define	P_TREE_FIRST_ORPHAN	0x00000002	/* First element of orphan
diff --git a/sys/sys/procctl.h b/sys/sys/procctl.h
index 851ca821d72d..11b831882f0a 100644
--- a/sys/sys/procctl.h
+++ b/sys/sys/procctl.h
@@ -65,6 +65,8 @@
 #define	PROC_NO_NEW_PRIVS_STATUS 20	/* query suid/sgid disabled status */
 #define	PROC_WXMAP_CTL		21	/* control W^X */
 #define	PROC_WXMAP_STATUS	22	/* query W^X */
+#define	PROC_LOGSIGEXIT_CTL	23	/* en/dis logging on sigexit */
+#define	PROC_LOGSIGEXIT_STATUS	24	/* query logging on sigexit */
 
 /* Operations for PROC_SPROTECT (passed in integer arg). */
 #define	PPROT_OP(x)	((x) & 0xf)
@@ -153,6 +155,10 @@ struct procctl_reaper_kill {
 #define	PROC_WX_MAPPINGS_DISALLOW_EXEC	0x0002
 #define	PROC_WXORX_ENFORCE		0x80000000
 
+#define	PROC_LOGSIGEXIT_CTL_NOFORCE		1
+#define	PROC_LOGSIGEXIT_CTL_FORCE_ENABLE	2
+#define	PROC_LOGSIGEXIT_CTL_FORCE_DISABLE	3
+
 #ifndef _KERNEL
 __BEGIN_DECLS
 int	procctl(idtype_t, id_t, int, void *);
diff --git a/usr.bin/proccontrol/proccontrol.1 b/usr.bin/proccontrol/proccontrol.1
index e0d0e9416d35..817e9b5865bc 100644
--- a/usr.bin/proccontrol/proccontrol.1
+++ b/usr.bin/proccontrol/proccontrol.1
@@ -25,7 +25,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd August 23, 2024
+.Dd December 14, 2024
 .Dt PROCCONTROL 1
 .Os
 .Sh NAME
@@ -56,7 +56,7 @@ parameter
 Possible values for
 .Ar mode
 are:
-.Bl -tag -width trapcap
+.Bl -tag -width logsigexit
 .It Ar aslr
 Control the Address Space Layout Randomization.
 Only applicable to the new process spawned.
@@ -79,6 +79,9 @@ Controls the KPTI enable, AMD64 only.
 .It Ar la48
 Control limiting usermode process address space to 48 bits of address,
 AMD64 only, on machines capable of 57-bit addressing.
+.It Ar logsigexit
+Controls the logging of exits due to a signal that would normally cause a core
+dump.
 .El
 .Pp
 The
diff --git a/usr.bin/proccontrol/proccontrol.c b/usr.bin/proccontrol/proccontrol.c
index 32c4c9e7f1a0..3548ca606656 100644
--- a/usr.bin/proccontrol/proccontrol.c
+++ b/usr.bin/proccontrol/proccontrol.c
@@ -51,6 +51,7 @@ enum mode {
 	MODE_LA57,
 	MODE_LA48,
 #endif
+	MODE_LOGSIGEXIT,
 };
 
 static const struct {
@@ -71,6 +72,7 @@ static const struct {
 	{ MODE_LA57,		"la57" },
 	{ MODE_LA48,		"la48" },
 #endif
+	{ MODE_LOGSIGEXIT,	"logsigexit" },
 };
 
 static pid_t
@@ -194,6 +196,10 @@ main(int argc, char *argv[])
 			error = procctl(P_PID, pid, PROC_LA_STATUS, &arg);
 			break;
 #endif
+		case MODE_LOGSIGEXIT:
+			error = procctl(P_PID, pid, PROC_LOGSIGEXIT_STATUS,
+			    &arg);
+			break;
 		default:
 			usage();
 			break;
@@ -331,6 +337,19 @@ main(int argc, char *argv[])
 				printf(", la57 active\n");
 			break;
 #endif
+		case MODE_LOGSIGEXIT:
+			switch (arg) {
+			case PROC_LOGSIGEXIT_CTL_NOFORCE:
+				printf("not forced\n");
+				break;
+			case PROC_LOGSIGEXIT_CTL_FORCE_ENABLE:
+				printf("force enabled\n");
+				break;
+			case PROC_LOGSIGEXIT_CTL_FORCE_DISABLE:
+				printf("force disabled\n");
+				break;
+			}
+			break;
 		}
 	} else {
 		switch (mode) {
@@ -390,6 +409,11 @@ main(int argc, char *argv[])
 			error = procctl(P_PID, pid, PROC_LA_CTL, &arg);
 			break;
 #endif
+		case MODE_LOGSIGEXIT:
+			arg = enable ? PROC_LOGSIGEXIT_CTL_FORCE_ENABLE :
+			    PROC_LOGSIGEXIT_CTL_FORCE_DISABLE;
+			error = procctl(P_PID, pid, PROC_LOGSIGEXIT_CTL, &arg);
+			break;
 		default:
 			usage();
 			break;