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