1// Userspace emulation of mmap and munmap. Restrictions apply.
  2//
  3// This is meant to be complete enough to be compatible with code that uses
  4// mmap for simple file I/O. It just allocates memory with malloc and reads
  5// and writes data with pread and pwrite.
  6
  7#define _WASI_EMULATED_MMAN
  8#include <stdlib.h>
  9#include <errno.h>
 10#include <unistd.h>
 11#include <string.h>
 12#include <limits.h>
 13#include <sys/mman.h>
 14#include <sys/types.h>
 15
 16struct map {
 17    int prot;
 18    int flags;
 19    off_t offset;
 20    size_t length;
 21};
 22
 23void *mmap(void *addr, size_t length, int prot, int flags,
 24           int fd, off_t offset) {
 25    // Check for unsupported flags.
 26    if ((flags & (MAP_PRIVATE | MAP_SHARED)) == 0 ||
 27        (flags & MAP_FIXED) != 0 ||
 28#ifdef MAP_SHARED_VALIDATE
 29        (flags & MAP_SHARED_VALIDATE) == MAP_SHARED_VALIDATE ||
 30#endif
 31#ifdef MAP_NORESERVE
 32        (flags & MAP_NORESERVE) != 0 ||
 33#endif
 34#ifdef MAP_GROWSDOWN
 35        (flags & MAP_GROWSDOWN) != 0 ||
 36#endif
 37#ifdef MAP_HUGETLB
 38        (flags & MAP_HUGETLB) != 0 ||
 39#endif
 40#ifdef MAP_FIXED_NOREPLACE
 41        (flags & MAP_FIXED_NOREPLACE) != 0 ||
 42#endif
 43        0)
 44    {
 45        errno = EINVAL;
 46        return MAP_FAILED;
 47    }
 48
 49    // Check for unsupported protection requests.
 50    if (prot == PROT_NONE ||
 51#ifdef PROT_EXEC
 52        (prot & PROT_EXEC) != 0 ||
 53#endif
 54        0)
 55    {
 56        errno = EINVAL;
 57        return MAP_FAILED;
 58    }
 59
 60    //  To be consistent with POSIX.
 61    if (length == 0) {
 62        errno = EINVAL;
 63        return MAP_FAILED;
 64    }
 65
 66    // Check for integer overflow.
 67    size_t buf_len = 0;
 68    if (__builtin_add_overflow(length, sizeof(struct map), &buf_len)) {
 69        errno = ENOMEM;
 70        return MAP_FAILED;
 71    }
 72
 73    // Allocate the memory.
 74    struct map *map = malloc(buf_len);
 75    if (!map) {
 76        errno = ENOMEM;
 77        return MAP_FAILED;
 78    }
 79
 80    // Initialize the header.
 81    map->prot = prot;
 82    map->flags = flags;
 83    map->offset = offset;
 84    map->length = length;
 85
 86    // Initialize the main memory buffer, either with the contents of a file,
 87    // or with zeros.
 88    addr = map + 1;
 89    if ((flags & MAP_ANON) == 0) {
 90        char *body = (char *)addr;
 91        while (length > 0) {
 92            const ssize_t nread = pread(fd, body, length, offset);
 93            if (nread < 0) {
 94                if (errno == EINTR)
 95                    continue;
 96                return MAP_FAILED;
 97            }
 98            if (nread == 0)
 99                break;
100            length -= (size_t)nread;
101            offset += (size_t)nread;
102            body += (size_t)nread;
103        }
104    } else {
105        memset(addr, 0, length);
106    }
107
108    return addr;
109}
110
111int munmap(void *addr, size_t length) {
112    struct map *map = (struct map *)addr - 1;
113
114    // We don't support partial munmapping.
115    if (map->length != length) {
116        errno = EINVAL;
117        return -1;
118    }
119
120    // Release the memory.
121    free(map);
122
123    // Success!
124    return 0;
125}
126
127int mprotect(void *addr, size_t length, int prot) {
128    // Address must be page-aligned.
129    size_t begin = (size_t)addr;
130    if ((begin & (PAGESIZE - 1)) != 0) {
131        errno = EINVAL;
132        return -1;
133    }
134
135    // Length must not be big enough to wrap around.
136    size_t end;
137    if (__builtin_add_overflow(begin, length, &end)) {
138        errno = ENOMEM;
139        return -1;
140    }
141
142    // Range must be in bounds of linear memory.
143    size_t memory_size = __builtin_wasm_memory_size(0) * PAGESIZE;
144    if (end > memory_size) {
145        errno = ENOMEM;
146        return -1;
147    }
148
149    // Can only protect memory as read/write (which is a no-op).
150    if (prot != (PROT_READ | PROT_WRITE)) {
151        errno = ENOTSUP;
152        return -1;
153    }
154
155    // Success!
156    return 0;
157}