#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>

static size_t pagesz;
static void *pages;

/*
 * Allocate 2 adjacent pages, revoke all permissions on the 2nd.
 */

static void *test_init()
{
	pagesz=getpagesize();

	if ((pages=mmap((void *) 0,pagesz*2,PROT_READ|PROT_WRITE,
	    MAP_PRIVATE|MAP_ANON,-1,(off_t) 0))==MAP_FAILED) {
		fprintf(stderr,"can't get two lousy pages!\n");
		exit(1);
	}
	if (mprotect((void *) (((size_t) pages)+pagesz),pagesz,PROT_NONE) == -1)
	{
		fprintf(stderr,"can't turn off perms on 2nd page!\n");
		exit(2);
	}

	return pages;
}

static void test_readonly()
{
	if (mprotect(pages,pagesz,PROT_READ) == -1) {
		fprintf(stderr,"can't turn off write perm on 1st page!\n");
		exit(3);
	}
}


static void test_readwrite()
{
	if (mprotect(pages,pagesz,PROT_READ|PROT_WRITE) == -1) {
		fprintf(stderr,"can't turn back on write perm on 1st page!\n");
		exit(4);
	}
}

/*
 * Out of the first page, create a test buffer of size TSTBUFLEN that
 * ends and the end of the first page.  Initialize test buffer, set the 1st
 * page read-only.  Play with strnlen() a bit, set the 1st page read/write,
 * fool with the contents of the test buffer a bit, set 1st page back
 * read-only, play with strnlen() some more, etc.
 *
 * Assuming hardware capable of sufficient memory enforcement,
 * this should verify that:
 *
 *   strnlen() does not look at memory that it shouldn't
 *
 *   strnlen() does indeed treat 1st arg as const char *
 *
 *   strnlen() terminates properly on either maxlen or null byte,
 *   including at boundary conditions.
 *
 */


#define TSTBUFLEN 15
#define TSTOFF 5   /* < TSTBUFLEN */

extern size_t strnlen(const char *, size_t); /* since not in string.h yet */

int main(int argc, char **argv)
{

	char *buf;

	test_init();

	buf=(char *) (((size_t) pages)+pagesz-TSTBUFLEN);

	memcpy( (void *) buf,"0123456789abcde",TSTBUFLEN);

	test_readonly();
	assert(strnlen(buf,0)==0);
	assert(strnlen(buf,TSTBUFLEN)==TSTBUFLEN);
	assert(strnlen(buf,TSTBUFLEN-1)==TSTBUFLEN-1);


	test_readwrite();
	buf[TSTOFF]='\0';

	test_readonly();
	assert(strnlen(buf,TSTBUFLEN)==TSTOFF);

	test_readwrite();
	buf[0]='\0';

	test_readonly();
	assert(strnlen(buf,TSTBUFLEN)==0);

	printf("all tests succeeded\n");
	exit(0);
	/*NOTREACHED*/
}
