master
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}