1 /*
2 * Copyright 2018 WebAssembly Community Group participants
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "wasm-rt.h"
18 #include "wasm-rt-os.h"
19
20 #include <assert.h>
21 #include <limits.h>
22 #include <stdarg.h>
23 #include <stdbool.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #ifdef WASM_RT_CUSTOM_TRAP_HANDLER
30 // forward declare the signature of any custom trap handler
31 void WASM_RT_CUSTOM_TRAP_HANDLER(const char*);
32 #endif
33
wasm_rt_trap(wasm_rt_trap_t code)34 void wasm_rt_trap(wasm_rt_trap_t code) {
35 const char* error_message = "wasm2c: unknown trap";
36 switch(code)
37 {
38 case WASM_RT_TRAP_NONE: {
39 // this should never happen
40 error_message = "wasm2c: WASM_RT_TRAP_NONE";
41 break;
42 }
43 case WASM_RT_TRAP_OOB: {
44 error_message = "wasm2c: WASM_RT_TRAP_OOB";
45 break;
46 }
47 case WASM_RT_TRAP_INT_OVERFLOW: {
48 error_message = "wasm2c: WASM_RT_TRAP_INT_OVERFLOW";
49 break;
50 }
51 case WASM_RT_TRAP_DIV_BY_ZERO: {
52 error_message = "wasm2c: WASM_RT_TRAP_DIV_BY_ZERO";
53 break;
54 }
55 case WASM_RT_TRAP_INVALID_CONVERSION: {
56 error_message = "wasm2c: WASM_RT_TRAP_INVALID_CONVERSION";
57 break;
58 }
59 case WASM_RT_TRAP_UNREACHABLE: {
60 error_message = "wasm2c: WASM_RT_TRAP_UNREACHABLE";
61 break;
62 }
63 case WASM_RT_TRAP_CALL_INDIRECT_TABLE_EXPANSION: {
64 error_message = "wasm2c: WASM_RT_TRAP_CALL_INDIRECT_TABLE_EXPANSION";
65 break;
66 }
67 case WASM_RT_TRAP_CALL_INDIRECT_OOB_INDEX: {
68 error_message = "wasm2c: WASM_RT_TRAP_CALL_INDIRECT_OOB_INDEX";
69 break;
70 }
71 case WASM_RT_TRAP_CALL_INDIRECT_NULL_PTR: {
72 error_message = "wasm2c: WASM_RT_TRAP_CALL_INDIRECT_NULL_PTR";
73 break;
74 }
75 case WASM_RT_TRAP_CALL_INDIRECT_TYPE_MISMATCH: {
76 error_message = "wasm2c: WASM_RT_TRAP_CALL_INDIRECT_TYPE_MISMATCH";
77 break;
78 }
79 case WASM_RT_TRAP_CALL_INDIRECT_UNKNOWN_ERR: {
80 error_message = "wasm2c: WASM_RT_TRAP_CALL_INDIRECT_UNKNOWN_ERR";
81 break;
82 }
83 case WASM_RT_TRAP_EXHAUSTION: {
84 error_message = "wasm2c: WASM_RT_TRAP_EXHAUSTION";
85 break;
86 }
87 case WASM_RT_TRAP_SHADOW_MEM: {
88 error_message = "wasm2c: WASM_RT_TRAP_SHADOW_MEM";
89 break;
90 }
91 case WASM_RT_TRAP_WASI: {
92 error_message = "wasm2c: WASM_RT_TRAP_WASI";
93 break;
94 }
95 };
96 #ifdef WASM_RT_CUSTOM_TRAP_HANDLER
97 WASM_RT_CUSTOM_TRAP_HANDLER(error_message);
98 #else
99 fprintf(stderr, "Error: %s\n", error_message);
100 abort();
101 #endif
102 }
103
wasm_rt_callback_error_trap(wasm_rt_table_t * table,uint32_t func_index,uint32_t expected_func_type)104 void wasm_rt_callback_error_trap(wasm_rt_table_t* table, uint32_t func_index, uint32_t expected_func_type) {
105 if (func_index >= table->size) {
106 wasm_rt_trap(WASM_RT_TRAP_CALL_INDIRECT_OOB_INDEX);
107 } else if (!table->data[func_index].func) {
108 wasm_rt_trap(WASM_RT_TRAP_CALL_INDIRECT_NULL_PTR);
109 } else if (table->data[func_index].func_type != expected_func_type) {
110 wasm_rt_trap(WASM_RT_TRAP_CALL_INDIRECT_TYPE_MISMATCH);
111 }
112 wasm_rt_trap(WASM_RT_TRAP_CALL_INDIRECT_UNKNOWN_ERR);
113 }
114
115
func_types_are_equal(wasm_func_type_t * a,wasm_func_type_t * b)116 static bool func_types_are_equal(wasm_func_type_t* a, wasm_func_type_t* b) {
117 if (a->param_count != b->param_count || a->result_count != b->result_count)
118 return 0;
119 uint32_t i;
120 for (i = 0; i < a->param_count; ++i)
121 if (a->params[i] != b->params[i])
122 return 0;
123 for (i = 0; i < a->result_count; ++i)
124 if (a->results[i] != b->results[i])
125 return 0;
126 return 1;
127 }
128
wasm_rt_register_func_type(wasm_func_type_t ** p_func_type_structs,uint32_t * p_func_type_count,uint32_t param_count,uint32_t result_count,wasm_rt_type_t * types)129 uint32_t wasm_rt_register_func_type(wasm_func_type_t** p_func_type_structs,
130 uint32_t* p_func_type_count,
131 uint32_t param_count,
132 uint32_t result_count,
133 wasm_rt_type_t* types) {
134 wasm_func_type_t func_type;
135
136 func_type.param_count = param_count;
137 if (func_type.param_count != 0) {
138 func_type.params = malloc(param_count * sizeof(wasm_rt_type_t));
139 assert(func_type.params != 0);
140 } else {
141 func_type.params = 0;
142 }
143
144 func_type.result_count = result_count;
145 if (func_type.result_count != 0) {
146 func_type.results = malloc(result_count * sizeof(wasm_rt_type_t));
147 assert(func_type.results != 0);
148 } else {
149 func_type.results = 0;
150 }
151
152 uint32_t i;
153 for (i = 0; i < param_count; ++i)
154 func_type.params[i] = types[i];
155 for (i = 0; i < result_count; ++i)
156 func_type.results[i] = types[(uint64_t)(param_count) + i];
157
158 for (i = 0; i < *p_func_type_count; ++i) {
159 wasm_func_type_t* func_types = *p_func_type_structs;
160 if (func_types_are_equal(&func_types[i], &func_type)) {
161 if (func_type.params) {
162 free(func_type.params);
163 }
164 if (func_type.results) {
165 free(func_type.results);
166 }
167 return i + 1;
168 }
169 }
170
171 uint32_t idx = (*p_func_type_count)++;
172 // realloc works fine even if *p_func_type_structs is null
173 *p_func_type_structs = realloc(*p_func_type_structs, *p_func_type_count * sizeof(wasm_func_type_t));
174 (*p_func_type_structs)[idx] = func_type;
175 return idx + 1;
176 }
177
wasm_rt_cleanup_func_types(wasm_func_type_t ** p_func_type_structs,uint32_t * p_func_type_count)178 void wasm_rt_cleanup_func_types(wasm_func_type_t** p_func_type_structs, uint32_t* p_func_type_count) {
179 // Use a u64 to iterate over u32 arrays to prevent infinite loops
180 const uint32_t func_count = *p_func_type_count;
181 for (uint64_t idx = 0; idx < func_count; idx++){
182 wasm_func_type_t* func_type = &((*p_func_type_structs)[idx]);
183 if (func_type->params != 0) {
184 free(func_type->params);
185 func_type->params = 0;
186 }
187 if (func_type->results != 0) {
188 free(func_type->results);
189 func_type->results = 0;
190 }
191 }
192 free(*p_func_type_structs);
193 }
194
195 #if UINTPTR_MAX == 0xffffffff
is_power_of_two(uint64_t x)196 static int is_power_of_two(uint64_t x)
197 {
198 return ((x != 0) && !(x & (x - 1)));
199 }
200 #endif
201
202 #define WASM_PAGE_SIZE 65536
203
204 #if UINTPTR_MAX == 0xffffffffffffffff
205 // Guard page of 4GiB
206 # define WASM_HEAP_GUARD_PAGE_SIZE 0x100000000ull
207 // Heap aligned to 4GB
208 # define WASM_HEAP_ALIGNMENT 0x100000000ull
209 // By default max heap is 4GB
210 # define WASM_HEAP_DEFAULT_MAX_PAGES 65536
211 // Runtime can override the max heap up to 4GB
212 # define WASM_HEAP_MAX_ALLOWED_PAGES 65536
213 #elif UINTPTR_MAX == 0xffffffff
214 // No guard pages
215 # define WASM_HEAP_GUARD_PAGE_SIZE 0
216 // Unaligned heap
217 # define WASM_HEAP_ALIGNMENT 0
218 // Default max heap is 16MB (1GB if you enable incremental heaps)
219 # ifdef WASM_USE_INCREMENTAL_MOVEABLE_MEMORY_ALLOC
220 # define WASM_HEAP_DEFAULT_MAX_PAGES 16384
221 # else
222 # define WASM_HEAP_DEFAULT_MAX_PAGES 256
223 # endif
224 // Runtime can override the max heap up to 1GB
225 # define WASM_HEAP_MAX_ALLOWED_PAGES 16384
226 #else
227 # error "Unknown pointer size"
228 #endif
229
wasm_rt_get_default_max_linear_memory_size()230 uint64_t wasm_rt_get_default_max_linear_memory_size() {
231 uint64_t ret = ((uint64_t) WASM_HEAP_DEFAULT_MAX_PAGES) * WASM_PAGE_SIZE;
232 return ret;
233 }
234
wasm_rt_allocate_memory(wasm_rt_memory_t * memory,uint32_t initial_pages,uint32_t max_pages)235 bool wasm_rt_allocate_memory(wasm_rt_memory_t* memory,
236 uint32_t initial_pages,
237 uint32_t max_pages) {
238 const uint32_t byte_length = initial_pages * WASM_PAGE_SIZE;
239
240 const uint32_t suggested_max_pages = max_pages == 0? WASM_HEAP_DEFAULT_MAX_PAGES : max_pages;
241 const uint32_t chosen_max_pages = (WASM_HEAP_MAX_ALLOWED_PAGES < suggested_max_pages)? WASM_HEAP_MAX_ALLOWED_PAGES : suggested_max_pages;
242
243 if (chosen_max_pages < initial_pages) {
244 return false;
245 }
246
247 #ifdef WASM_USE_GUARD_PAGES
248 // mmap based heaps with guard pages
249 // Guard pages already allocates memory incrementally thus we don't need to look at WASM_USE_INCREMENTAL_MOVEABLE_MEMORY_ALLOC
250 void* addr = NULL;
251 const uint64_t retries = 10;
252 const uint64_t heap_reserve_size = ((uint64_t) chosen_max_pages) * WASM_PAGE_SIZE + WASM_HEAP_GUARD_PAGE_SIZE;
253
254 // 32-bit platforms rely on masking for sandboxing
255 // thus we require the heap reserve size to always be a power of 2
256 #if UINTPTR_MAX == 0xffffffff
257 if (!is_power_of_two(heap_reserve_size)) {
258 return false;
259 }
260 #endif
261
262 for (uint64_t i = 0; i < retries; i++) {
263 addr = os_mmap_aligned(NULL, heap_reserve_size, MMAP_PROT_NONE, MMAP_MAP_NONE, WASM_HEAP_ALIGNMENT, 0 /* alignment_offset */);
264 if (addr) {
265 break;
266 }
267 }
268
269 if (!addr) {
270 os_print_last_error("os_mmap failed.");
271 return false;
272 }
273 int ret = os_mmap_commit(addr, byte_length, MMAP_PROT_READ | MMAP_PROT_WRITE);
274 if (ret != 0) {
275 return false;
276 }
277 // This is a valid way to initialize a constant field that is not undefined behavior
278 // https://stackoverflow.com/questions/9691404/how-to-initialize-const-in-a-struct-in-c-with-malloc
279 // Summary: malloc of a struct, followed by a write to the constant fields is still defined behavior iff
280 // there is no prior read of the field
281 *(uint8_t**) &memory->data = addr;
282 #else
283 // malloc based heaps
284 # ifdef WASM_USE_INCREMENTAL_MOVEABLE_MEMORY_ALLOC
285 memory->data = calloc(byte_length, 1);
286 # else
287 const uint64_t heap_max_size = ((uint64_t) chosen_max_pages) * WASM_PAGE_SIZE;
288 *(uint8_t**) &memory->data = calloc(heap_max_size, 1);
289 # endif
290 #endif
291
292 memory->size = byte_length;
293 memory->pages = initial_pages;
294 memory->max_pages = chosen_max_pages;
295
296 // 32-bit platforms use masking for sandboxing. Compute the mask
297 #if UINTPTR_MAX == 0xffffffff
298 *(uint32_t*) &memory->mem_mask = heap_reserve_size - 1;
299 #endif
300
301 #if defined(WASM_CHECK_SHADOW_MEMORY)
302 wasm2c_shadow_memory_create(memory);
303 #endif
304 return true;
305 }
306
wasm_rt_deallocate_memory(wasm_rt_memory_t * memory)307 void wasm_rt_deallocate_memory(wasm_rt_memory_t* memory) {
308 #ifdef WASM_USE_GUARD_PAGES
309 const uint64_t heap_reserve_size = ((uint64_t) memory->max_pages) * WASM_PAGE_SIZE + WASM_HEAP_GUARD_PAGE_SIZE;
310 os_munmap(memory->data, heap_reserve_size);
311 #else
312 free(memory->data);
313 #endif
314
315 #if defined(WASM_CHECK_SHADOW_MEMORY)
316 wasm2c_shadow_memory_destroy(memory);
317 #endif
318 }
319
wasm_rt_grow_memory(wasm_rt_memory_t * memory,uint32_t delta)320 uint32_t wasm_rt_grow_memory(wasm_rt_memory_t* memory, uint32_t delta) {
321 uint32_t old_pages = memory->pages;
322 uint32_t new_pages = memory->pages + delta;
323 if (new_pages == 0) {
324 return 0;
325 }
326 if (new_pages < old_pages || new_pages > memory->max_pages) {
327 return (uint32_t)-1;
328 }
329 uint32_t old_size = old_pages * WASM_PAGE_SIZE;
330 uint32_t new_size = new_pages * WASM_PAGE_SIZE;
331 uint32_t delta_size = delta * WASM_PAGE_SIZE;
332
333 #ifdef WASM_USE_GUARD_PAGES
334 // mmap based heaps with guard pages
335 int ret = os_mmap_commit(memory->data + old_size, delta_size, MMAP_PROT_READ | MMAP_PROT_WRITE);
336 if (ret != 0) {
337 return (uint32_t)-1;
338 }
339 #else
340 // malloc based heaps --- if below macro is not defined, the max memory range is already allocated
341 # ifdef WASM_USE_INCREMENTAL_MOVEABLE_MEMORY_ALLOC
342 uint8_t* new_data = realloc(memory->data, new_size);
343 if (new_data == NULL) {
344 return (uint32_t)-1;
345 }
346 # if !WABT_BIG_ENDIAN
347 memset(new_data + old_size, 0, delta_size);
348 # endif
349 memory->data = new_data;
350 # endif
351 #endif
352
353 #if WABT_BIG_ENDIAN
354 memmove(memory->data + new_size - old_size, memory->data, old_size);
355 memset(memory->data, 0, delta_size);
356 #endif
357 memory->pages = new_pages;
358 memory->size = new_size;
359 #if defined(WASM_CHECK_SHADOW_MEMORY)
360 wasm2c_shadow_memory_expand(memory);
361 #endif
362 return old_pages;
363 }
364
wasm_rt_allocate_table(wasm_rt_table_t * table,uint32_t elements,uint32_t max_elements)365 void wasm_rt_allocate_table(wasm_rt_table_t* table,
366 uint32_t elements,
367 uint32_t max_elements) {
368 assert(max_elements >= elements);
369 table->size = elements;
370 table->max_size = max_elements;
371 table->data = calloc(table->size, sizeof(wasm_rt_elem_t));
372 assert(table->data != 0);
373 }
374
wasm_rt_deallocate_table(wasm_rt_table_t * table)375 void wasm_rt_deallocate_table(wasm_rt_table_t* table) {
376 free(table->data);
377 }
378
379 #define WASM_SATURATING_U32_ADD(ret_ptr, a, b) { \
380 if ((a) > (UINT32_MAX - (b))) { \
381 /* add will overflowed */ \
382 *ret_ptr = UINT32_MAX; \
383 } else { \
384 *ret_ptr = (a) + (b); \
385 } \
386 }
387
388 #define WASM_CHECKED_U32_RET_SIZE_T_MULTIPLY(ret_ptr, a, b) { \
389 if ((a) > (SIZE_MAX / (b))) { \
390 /* multiple will overflowed */ \
391 wasm_rt_trap(WASM_RT_TRAP_CALL_INDIRECT_TABLE_EXPANSION); \
392 } else { \
393 /* convert to size by assigning */ \
394 *ret_ptr = a; \
395 *ret_ptr = *ret_ptr * b; \
396 } \
397 }
398
wasm_rt_expand_table(wasm_rt_table_t * table)399 void wasm_rt_expand_table(wasm_rt_table_t* table) {
400 uint32_t new_size = 0;
401 WASM_SATURATING_U32_ADD(&new_size, table->size, 32);
402
403 if (new_size > table->max_size) {
404 new_size = table->max_size;
405 }
406
407 if (table->size == new_size) {
408 // table is already as large as we allowed, can't expand further
409 wasm_rt_trap(WASM_RT_TRAP_CALL_INDIRECT_TABLE_EXPANSION);
410 }
411
412 size_t allocation_size = 0;
413 WASM_CHECKED_U32_RET_SIZE_T_MULTIPLY(&allocation_size, sizeof(wasm_rt_elem_t), new_size);
414 table->data = realloc(table->data, allocation_size);
415 assert(table->data != 0);
416
417 memset(&(table->data[table->size]), 0, allocation_size - (table->size * sizeof(wasm_rt_elem_t)));
418 table->size = new_size;
419 }
420
wasm2c_ensure_linked()421 void wasm2c_ensure_linked() {
422 // We use this to ensure the dynamic library with the wasi symbols is loaded for the host application
423 }
424
425 #undef WASM_PAGE_SIZE
426 #undef WASM_HEAP_GUARD_PAGE_SIZE
427 #undef WASM_HEAP_ALIGNMENT
428 #undef WASM_HEAP_DEFAULT_MAX_PAGES
429 #undef WASM_HEAP_MAX_ALLOWED_PAGES
430 #undef WASM_SATURATING_U32_ADD
431 #undef WASM_CHECKED_U32_RET_SIZE_T_MULTIPLY
432