/*
 * DF-5691 rig-only page-guard allocator.
 *
 * Every allocation is page-aligned, ends directly against a PROT_NONE
 * guard page, and is munmap()ed on free.  Any read/write past the end of
 * an allocation, or any access after free, SIGSEGVs deterministically.
 * (Guest base cc 8.3 has no usable ASAN runtime -> page guards instead.)
 *
 * Redirected onto proplib in the rig builds via prop_object_impl.h:
 *   _PROP_MALLOC/_PROP_CALLOC/_PROP_REALLOC/_PROP_FREE and
 *   _PROP_POOL_GET/_PROP_POOL_PUT  (userland block only; see build.sh).
 */
#ifndef DF5691_GUARDALLOC_H
#define DF5691_GUARDALLOC_H

#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

static size_t
ga_pagesz(void)
{
	static size_t ps;
	if (ps == 0)
		ps = (size_t)sysconf(_SC_PAGESIZE);
	return (ps);
}

/* layout: [header page: size][data pages ...][PROT_NONE guard page] */
static void *
ga_malloc(size_t size)
{
	size_t ps = ga_pagesz();
	size_t need = (size + ps - 1) & ~(ps - 1);
	unsigned char *m;

	if (size == 0)
		size = 1, need = ps;
	m = mmap(NULL, ps + need + ps, PROT_READ | PROT_WRITE,
	    MAP_ANON | MAP_PRIVATE, -1, 0);
	if (m == MAP_FAILED || m == NULL)
		return (NULL);
	if (mprotect(m + ps + need, ps, PROT_NONE) != 0) {
		munmap(m, ps + need + ps);
		return (NULL);
	}
	*(size_t *)(void *)m = size;
	return (m + ps);
}

static void *
ga_calloc(size_t n, size_t size)
{
	void *p = ga_malloc(n * size);
	if (p != NULL)
		memset(p, 0, n * size);
	return (p);
}

static void
ga_free(void *p)
{
	size_t ps = ga_pagesz();
	unsigned char *m;
	size_t size, need;

	if (p == NULL)
		return;
	m = (unsigned char *)p - ps;
	size = *(size_t *)(void *)m;
	need = (size + ps - 1) & ~(ps - 1);
	munmap(m, ps + need + ps);
}

static void *
ga_realloc(void *v, size_t size)
{
	unsigned char *m;
	size_t old;
	void *n;

	if (v == NULL)
		return (ga_malloc(size));
	m = (unsigned char *)v - ga_pagesz();
	old = *(size_t *)(void *)m;
	n = ga_malloc(size);
	if (n == NULL)
		return (NULL);
	memcpy(n, v, old < size ? old : size);
	ga_free(v);
	return (n);
}

#endif /* DF5691_GUARDALLOC_H */
