⬢ DragonFlyBSD Kernel Audit
DF-5611 / siglog.c
← back to finding ↓ download raw
/*
 * siglog.c - DF-5611 observation shim (LD_PRELOAD on the vkernel).
 *
 * Pure OBSERVER + real trigger: it never injects signals or faults.
 *  - wraps sigaction(SIGXCPU,...) so every SIGXCPU delivery logs
 *    {pthread_self, %gs base} BEFORE calling the real vkernel handler
 *    (stopsig).  %gs base == 0  => delivered on a cothread / pre-init AP
 *    thread  => stopsig's mycpu load must fault.
 *  - wraps pthread_create() to log each created host thread so the doomed
 *    thread can be correlated with its creation.
 *  - logs sigblock() masks so the runtime evidence shows exactly which
 *    signal sets cpu_disable_intr()/cpu_mask_all_signals() install
 *    (SIGXCPU absent from both - machintr.c:194-195, 207-209).
 *
 * Signal-handler-context code only uses raw syscalls, pthread_self(),
 * and single write(2) calls (async-signal-safe).
 */
#define _GNU_SOURCE
#include <signal.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <dlfcn.h>
#include <sys/syscall.h>

struct tls_info_ { void *base; long size; };

/* SYS_get_tls_area = 473, which=1 is %gs (TLS_WHICH_GS) */
static uintptr_t
gsbase(void)
{
	struct tls_info_ ti;
	ti.base = NULL;
	ti.size = 0;
	if (syscall(473, 1, &ti, sizeof(ti)) != 0)
		return (uintptr_t)0xdeadbeefU;
	return (uintptr_t)ti.base;
}

/* minimal async-safe line logger: "SIGLOG: " tag + 3 hex fields + '\n' */
static void
slog_raw(const char *tag, uintptr_t a, uintptr_t b, uintptr_t c)
{
	char buf[160];
	int n = 0;
	int i;
	const char *p;

	for (p = "SIGLOG: "; *p; ++p) buf[n++] = *p;
	for (p = tag; *p; ++p) buf[n++] = *p;
	buf[n++] = ' ';
	buf[n++] = '0'; buf[n++] = 'x';
	for (i = (int)(sizeof(uintptr_t) * 2) - 1; i >= 0; --i) {
		char h = (char)((a >> (i * 4)) & 0xf);
		buf[n++] = (char)(h < 10 ? '0' + h : 'a' + h - 10);
	}
	buf[n++] = ' ';
	buf[n++] = '0'; buf[n++] = 'x';
	for (i = (int)(sizeof(uintptr_t) * 2) - 1; i >= 0; --i) {
		char h = (char)((b >> (i * 4)) & 0xf);
		buf[n++] = (char)(h < 10 ? '0' + h : 'a' + h - 10);
	}
	buf[n++] = ' ';
	buf[n++] = '0'; buf[n++] = 'x';
	for (i = (int)(sizeof(uintptr_t) * 2) - 1; i >= 0; --i) {
		char h = (char)((c >> (i * 4)) & 0xf);
		buf[n++] = (char)(h < 10 ? '0' + h : 'a' + h - 10);
	}
	buf[n++] = '\n';
	write(2, buf, (size_t)n);
}

/* ---------------- SIGXCPU handler wrapper ---------------- */
static void (*real_xcpu_handler)(int, siginfo_t *, void *);

static void
xcpu_wrapper(int signo, siginfo_t *info, void *ctx)
{
	int save = errno;
	slog_raw("XCPU-IN thr=", (uintptr_t)pthread_self(),
		 gsbase(), (uintptr_t)ctx);
	errno = save;
	if (real_xcpu_handler)
		real_xcpu_handler(signo, info, ctx);
	errno = save;
}

/* ---------------- pthread_create wrapper ---------------- */
struct pc_arg {
	void *(*start)(void *);
	void *arg;
};

static void *
pc_child(struct pc_arg *pa)
{
	void *(*start)(void *) = pa->start;
	void *arg = pa->arg;

	slog_raw("THREAD-START thr=", (uintptr_t)pthread_self(),
		 gsbase(), (uintptr_t)start);
	free(pa);
	return start(arg);
}

static int (*real_pthread_create)(pthread_t *, const pthread_attr_t *,
				  void *(*)(void *), void *);

int
pthread_create(pthread_t *t, const pthread_attr_t *a,
	       void *(*start)(void *), void *arg)
{
	struct pc_arg *pa;
	int rc;

	if (!real_pthread_create)
		return EAGAIN;
	pa = malloc(sizeof(*pa));
	if (pa == NULL)
		return real_pthread_create(t, a, start, arg);
	pa->start = start;
	pa->arg = arg;
	rc = real_pthread_create(t, a, pc_child, pa);
	if (rc == 0)
		slog_raw("THREAD-CREATE newthr=", (uintptr_t)*t,
			 0, (uintptr_t)start);
	else
		free(pa);
	return rc;
}

/* ---------------- sigaction wrapper (SIGXCPU only) ---------------- */
static int (*real_sigaction)(int, const struct sigaction *,
			     struct sigaction *);

int
sigaction(int signo, const struct sigaction *act, struct sigaction *oact)
{
	struct sigaction na;
	void *h;

	if (!real_sigaction)
		return -1;
	if (signo == SIGXCPU && act != NULL) {
		h = act->sa_sigaction;
		if (h != NULL && h != SIG_IGN && h != SIG_DFL &&
		    h != (void *)xcpu_wrapper) {
			real_xcpu_handler = h;
			na = *act;
			na.sa_sigaction = xcpu_wrapper;
			slog_raw("HOOK-XCPU handler=", (uintptr_t)h,
				 (uintptr_t)pthread_self(), gsbase());
			return real_sigaction(signo, &na, oact);
		}
	}
	return real_sigaction(signo, act, oact);
}

/* ---------------- sigblock logger ---------------- */
static int (*real_sigblock_)(int);

int
sigblock(int mask)
{
	if (real_sigblock_)
		slog_raw("SIGBLOCK mask=", (uintptr_t)(unsigned int)mask,
			 (uintptr_t)pthread_self(), 0);
	return real_sigblock_ ? real_sigblock_(mask) : 0;
}

/* ---------------- bootstrap ---------------- */
__attribute__((constructor))
static void
siglog_init(void)
{
	real_pthread_create = (void *(*)(pthread_t *, const pthread_attr_t *,
			    void *(*)(void *), void *))
		dlsym(RTLD_NEXT, "pthread_create");
	real_sigaction = (int (*)(int, const struct sigaction *,
		     struct sigaction *))
		dlsym(RTLD_NEXT, "sigaction");
	real_sigblock_ = (int (*)(int))dlsym(RTLD_NEXT, "sigblock");
	slog_raw("INIT thr=", (uintptr_t)pthread_self(), gsbase(), 0);
}