update
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static void
|
||||
pread_all(int fd, unsigned char *buf, size_t len)
|
||||
{
|
||||
size_t done;
|
||||
ssize_t nr;
|
||||
|
||||
for (done = 0; done < len; done += nr) {
|
||||
nr = pread(fd, buf + done, len - done, done);
|
||||
if (nr < 0)
|
||||
err(1, "pread");
|
||||
if (nr == 0)
|
||||
errx(1, "unexpected EOF at %zu", done);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
fnv1a64(const unsigned char *buf, size_t len)
|
||||
{
|
||||
uint64_t hash;
|
||||
|
||||
hash = UINT64_C(14695981039346656037);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
hash ^= buf[i];
|
||||
hash *= UINT64_C(1099511628211);
|
||||
}
|
||||
return (hash);
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
xorshift32(uint32_t *state)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
value = *state;
|
||||
value ^= value << 13;
|
||||
value ^= value >> 17;
|
||||
value ^= value << 5;
|
||||
*state = value;
|
||||
return (value);
|
||||
}
|
||||
|
||||
static void
|
||||
verify_random_faults(const unsigned char *map, const unsigned char *expected,
|
||||
size_t size, size_t pagesize)
|
||||
{
|
||||
size_t *order;
|
||||
size_t npages, page, span;
|
||||
uint32_t state;
|
||||
|
||||
npages = (size + pagesize - 1) / pagesize;
|
||||
order = calloc(npages, sizeof(*order));
|
||||
if (order == NULL)
|
||||
err(1, "calloc page order");
|
||||
for (size_t i = 0; i < npages; i++)
|
||||
order[i] = i;
|
||||
state = UINT32_C(0x5eed22);
|
||||
for (size_t i = npages; i > 1; i--) {
|
||||
size_t other;
|
||||
|
||||
other = xorshift32(&state) % i;
|
||||
page = order[i - 1];
|
||||
order[i - 1] = order[other];
|
||||
order[other] = page;
|
||||
}
|
||||
for (size_t i = 0; i < npages; i++) {
|
||||
page = order[i] * pagesize;
|
||||
span = size - page < pagesize ? size - page : pagesize;
|
||||
if (memcmp(map + page, expected + page, span) != 0)
|
||||
errx(1, "random page mismatch at offset %zu", page);
|
||||
}
|
||||
free(order);
|
||||
}
|
||||
|
||||
static void
|
||||
verify_sigbus(const unsigned char *map, size_t eof_page)
|
||||
{
|
||||
pid_t child;
|
||||
int status;
|
||||
|
||||
child = fork();
|
||||
if (child < 0)
|
||||
err(1, "fork");
|
||||
if (child == 0) {
|
||||
volatile unsigned char value;
|
||||
|
||||
value = map[eof_page];
|
||||
_exit(value == 0 ? 0 : 1);
|
||||
}
|
||||
if (waitpid(child, &status, 0) != child)
|
||||
err(1, "waitpid");
|
||||
if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGBUS)
|
||||
errx(1, "full page beyond EOF did not raise SIGBUS (status=%#x)",
|
||||
status);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
unsigned char *expected, *map, *private_map, byte;
|
||||
struct stat sb;
|
||||
size_t eof_page, map_len, pagesize, private_offset;
|
||||
uint64_t hash;
|
||||
int fd, rwfd, shared_errno;
|
||||
|
||||
if (argc != 2)
|
||||
errx(2, "usage: mmap_fault file");
|
||||
pagesize = (size_t)getpagesize();
|
||||
fd = open(argv[1], O_RDONLY);
|
||||
if (fd < 0)
|
||||
err(1, "open %s", argv[1]);
|
||||
if (fstat(fd, &sb) != 0)
|
||||
err(1, "fstat %s", argv[1]);
|
||||
if (sb.st_size <= (off_t)(pagesize * 3))
|
||||
errx(1, "fixture must exceed three VM pages");
|
||||
if ((uintmax_t)sb.st_size > SIZE_MAX - pagesize * 2)
|
||||
errx(1, "fixture is too large");
|
||||
eof_page = ((size_t)sb.st_size + pagesize - 1) / pagesize * pagesize;
|
||||
map_len = eof_page + pagesize;
|
||||
expected = malloc((size_t)sb.st_size);
|
||||
if (expected == NULL)
|
||||
err(1, "malloc expected data");
|
||||
pread_all(fd, expected, (size_t)sb.st_size);
|
||||
|
||||
map = mmap(NULL, map_len, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (map == MAP_FAILED)
|
||||
err(1, "mmap read-only private");
|
||||
if (madvise(map, map_len, MADV_DONTNEED) != 0)
|
||||
err(1, "madvise MADV_DONTNEED");
|
||||
verify_random_faults(map, expected, (size_t)sb.st_size, pagesize);
|
||||
if (memcmp(map, expected, (size_t)sb.st_size) != 0)
|
||||
errx(1, "sequential mmap data differs from pread");
|
||||
for (size_t i = (size_t)sb.st_size; i < eof_page; i++) {
|
||||
if (map[i] != 0)
|
||||
errx(1, "non-zero byte in partial EOF page at %zu", i);
|
||||
}
|
||||
verify_sigbus(map, eof_page);
|
||||
hash = fnv1a64(map, (size_t)sb.st_size);
|
||||
|
||||
errno = 0;
|
||||
private_map = mmap(NULL, eof_page, PROT_READ | PROT_WRITE, MAP_SHARED,
|
||||
fd, 0);
|
||||
shared_errno = errno;
|
||||
if (private_map != MAP_FAILED) {
|
||||
munmap(private_map, eof_page);
|
||||
errx(1, "writable MAP_SHARED unexpectedly succeeded");
|
||||
}
|
||||
if (shared_errno != EACCES)
|
||||
errx(1, "writable MAP_SHARED returned %s, expected Permission denied",
|
||||
strerror(shared_errno));
|
||||
|
||||
private_map = mmap(NULL, eof_page, PROT_READ | PROT_WRITE, MAP_PRIVATE,
|
||||
fd, 0);
|
||||
if (private_map == MAP_FAILED)
|
||||
err(1, "mmap writable private");
|
||||
private_offset = (size_t)sb.st_size / 2;
|
||||
byte = expected[private_offset];
|
||||
private_map[private_offset] ^= UINT8_C(0xff);
|
||||
if (private_map[private_offset] == byte)
|
||||
errx(1, "private mapping did not change");
|
||||
if (pread(fd, &byte, 1, private_offset) != 1)
|
||||
err(1, "pread private verification");
|
||||
if (byte != expected[private_offset])
|
||||
errx(1, "MAP_PRIVATE modified the EROFS file");
|
||||
|
||||
errno = 0;
|
||||
rwfd = open(argv[1], O_RDWR);
|
||||
if (rwfd >= 0) {
|
||||
close(rwfd);
|
||||
errx(1, "O_RDWR unexpectedly succeeded");
|
||||
}
|
||||
if (errno != EROFS)
|
||||
errx(1, "O_RDWR returned %s, expected Read-only file system",
|
||||
strerror(errno));
|
||||
|
||||
if (munmap(private_map, eof_page) != 0 || munmap(map, map_len) != 0)
|
||||
err(1, "munmap");
|
||||
free(expected);
|
||||
if (close(fd) != 0)
|
||||
err(1, "close");
|
||||
printf("PASS size=%jd pages=%zu fnv1a64=%016" PRIx64
|
||||
" random-faults=%zu eof-zero=PASS sigbus=PASS private-cow=PASS\n",
|
||||
(intmax_t)sb.st_size, eof_page / pagesize, hash,
|
||||
((size_t)sb.st_size + pagesize - 1) / pagesize);
|
||||
return (0);
|
||||
}
|
||||
Reference in New Issue
Block a user