⬢ DragonFlyBSD Kernel Audit
DF-5800 / fragstress.c
← back to finding ↓ download raw
/*
 * DF pass-3 vm_map.c stress rig 2 — clip/simplify/unclip contention.
 *
 * Builds a heavily fragmented user map (adjacent anon mappings with
 * alternating protections and inheritances), then several threads beat on
 * overlapping ranges with mprotect / munmap+mmap / madvise / minherit /
 * mlock / munlock concurrently.  Exercises:
 *   - vm_map_clip_start/clip_end + vm_map_simplify_entry merging under
 *     MAP_ENTRY_IN_TRANSITION contention (mlock's clip_range)
 *   - vm_map_unclip_range KASSERT paths
 *   - vm_map_insert coalesce path (adjacent identical mappings)
 *   - freehint updates vs deletion
 *
 * build: cc -O -o fragstress fragstress.c -lpthread
 */
#include <sys/mman.h>
#include <sys/resource.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#ifndef VM_INHERIT_NONE
#define VM_INHERIT_SHARE	0
#define VM_INHERIT_COPY	1
#define VM_INHERIT_NONE	2
#endif

#define NMAPS	2048
#define MAPLEN	(64 * 1024)

static void *base[NMAPS];
static volatile int stop_flag;
static volatile long ops;

static void *
worker(void *arg)
{
	unsigned long id = (unsigned long)arg;
	unsigned seed = id * 7919 + 13;

	while (!stop_flag) {
		int idx = rand_r(&seed) % NMAPS;
		int len = 1 + rand_r(&seed) % 8; /* spans adjacent entries */
		int op = rand_r(&seed) % 8;
		void *p = base[idx];
		size_t sz = (size_t)len * MAPLEN;

		switch (op) {
		case 0:
			mprotect(p, sz, PROT_READ | PROT_WRITE);
			break;
		case 1:
			mprotect(p, sz, PROT_READ);
			break;
		case 2:
			mprotect(p, sz, PROT_READ | PROT_WRITE | PROT_EXEC);
			break;
		case 3:
			madvise(p, sz, MADV_DONTNEED);
			break;
		case 4:
			minherit(p, sz, VM_INHERIT_NONE);
			break;
		case 5:
			minherit(p, sz, VM_INHERIT_COPY);
			break;
		case 6:
			mlock(p, sz);
			break;
		case 7:
			munlock(p, sz);
			break;
		}
		__sync_fetch_and_add(&ops, 1);
	}
	return NULL;
}

int
main(void)
{
	pthread_t th[6];
	int i, round;

	/* Reserve a big contiguous span then chop it into pieces */
	char *arena = mmap(NULL, (size_t)NMAPS * MAPLEN * 2,
			   PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
	if (arena == MAP_FAILED) {
		perror("arena mmap");
		return 1;
	}
	for (i = 0; i < NMAPS; i++) {
		base[i] = mmap(arena + (size_t)i * MAPLEN * 2, MAPLEN,
			       PROT_READ | PROT_WRITE,
			       MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
		if (base[i] == MAP_FAILED) {
			perror("piece mmap");
			return 1;
		}
		memset(base[i], 0x5a, MAPLEN);
	}

	for (round = 0; round < 3; round++) {
		for (i = 0; i < 6; i++)
			pthread_create(&th[i], NULL, worker, (void *)(long)i);
		sleep(10);
		stop_flag = 1;
		for (i = 0; i < 6; i++)
			pthread_join(th[i], NULL);
		stop_flag = 0;
		printf("round %d: %ld ops complete\n", round, ops);
		fflush(stdout);
	}

	/* sanity: all pieces still readable */
	for (i = 0; i < NMAPS; i += 64)
		if (*(volatile char *)base[i] != 0x5a &&
		    *(volatile char *)base[i] != 0) {
			printf("CORRUPTION at %p: %02x\n", base[i],
			       *(volatile char *)base[i]);
			return 2;
		}
	printf("fragstress: complete, no corruption\n");
	return 0;
}