#include <sys/mman.h>
#include <sys/wait.h>
#include <assert.h>
#include <fcntl.h>
#include <paths.h>
#include <string.h>
#include <unistd.h>
#define CHECK(x) assert((x))
#define CHECK_EQ(a, b) assert((a) == (b))
#define CHECK_NE(a, b) assert((a) != (b))
#define CHECK_GE(a, b) assert((a) >= (b))
static int
ismemset(const void *s, int c, size_t n)
{
const unsigned char *p = s;
size_t i;
for (i = 0; i < n; i++)
if (p[i] != c)
return (0);
return (1);
}
static void
wait_for_clean_exit(pid_t pid)
{
int status;
CHECK_EQ(pid, waitpid(pid, &status, 0));
CHECK(WIFEXITED(status));
CHECK_EQ(0, WEXITSTATUS(status));
}
enum {
NPAGES = 4,
PARENT_BYTE = 42,
CHILD_BYTE = 53,
GRANDCHILD_BYTE = 65
};
static void
dotest(int fd, size_t len, int flags)
{
void *p;
pid_t pid;
p = mmap(NULL, len, PROT_READ|PROT_WRITE, flags, fd, 0);
CHECK_NE(MAP_FAILED, p);
CHECK_EQ(0, minherit(p, len, MAP_INHERIT_ZERO));
memset(p, PARENT_BYTE, len);
pid = fork();
CHECK_GE(pid, 0);
if (pid == 0) {
CHECK(ismemset(p, 0, len));
memset(p, CHILD_BYTE, len);
pid = fork();
CHECK_GE(pid, 0);
if (pid == 0) {
CHECK(ismemset(p, 0, len));
memset(p, GRANDCHILD_BYTE, len);
_exit(0);
}
wait_for_clean_exit(pid);
CHECK(ismemset(p, CHILD_BYTE, len));
memset(p, 0, len);
_exit(0);
}
wait_for_clean_exit(pid);
CHECK(ismemset(p, PARENT_BYTE, len));
memset(p, 0, len);
CHECK_EQ(0, munmap(p, len));
}
int
main()
{
long pagesize;
size_t len;
pagesize = sysconf(_SC_PAGESIZE);
CHECK_GE(pagesize, 1);
len = NPAGES * pagesize;
dotest(-1, len, MAP_ANON|MAP_PRIVATE);
dotest(-1, len, MAP_ANON|MAP_SHARED);
int fd = open(_PATH_BSHELL, O_RDONLY);
CHECK_GE(fd, 0);
dotest(fd, len, MAP_FILE|MAP_PRIVATE);
CHECK_EQ(0, close(fd));
return (0);
}