main.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /*
  2. * Copyright (C) 2024 Midokura Japan KK. All rights reserved.
  3. * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. */
  5. #include "wasm_export.h"
  6. #include "bh_read_file.h"
  7. uint32_t
  8. host_consume_stack_and_call_indirect(wasm_exec_env_t exec_env, uint32_t funcidx,
  9. uint32_t x, uint32_t stack);
  10. uint32_t
  11. host_consume_stack(wasm_exec_env_t exec_env, uint32_t stack);
  12. extern unsigned int nest;
  13. static NativeSymbol native_symbols[] = {
  14. { "host_consume_stack_and_call_indirect",
  15. host_consume_stack_and_call_indirect, "(iii)i", NULL },
  16. { "host_consume_stack", host_consume_stack, "(i)i", NULL },
  17. };
  18. void *
  19. canary_addr()
  20. {
  21. uint8_t *p = os_thread_get_stack_boundary();
  22. #if defined(OS_ENABLE_HW_BOUND_CHECK) && WASM_DISABLE_STACK_HW_BOUND_CHECK == 0
  23. uint32_t page_size = os_getpagesize();
  24. uint32_t guard_page_count = STACK_OVERFLOW_CHECK_GUARD_PAGE_COUNT;
  25. return p + page_size * guard_page_count;
  26. #else
  27. return p;
  28. #endif
  29. }
  30. void
  31. canary_init(void)
  32. {
  33. uint32_t *canary = canary_addr();
  34. *canary = 0xaabbccdd;
  35. }
  36. bool
  37. canary_check(void)
  38. {
  39. /* assume an overflow if the first uint32_t on the stack was modified */
  40. const uint32_t *canary = (void *)canary_addr();
  41. return *canary == 0xaabbccdd;
  42. }
  43. struct record {
  44. bool failed;
  45. bool leaked;
  46. char exception[128]; /* EXCEPTION_BUF_LEN */
  47. };
  48. void
  49. print_record(unsigned int start, unsigned int end, const struct record *rec)
  50. {
  51. printf("%5u - %5u | %6s | %6s | %s\n", start, end,
  52. rec->failed ? "failed" : "ok", rec->leaked ? "leaked" : "ok",
  53. rec->exception);
  54. }
  55. int
  56. main(int argc, char **argv)
  57. {
  58. char *buffer;
  59. char error_buf[128];
  60. if (argc != 3) {
  61. return 2;
  62. }
  63. const char *module_path = argv[1];
  64. const char *funcname = argv[2];
  65. wasm_module_t module = NULL;
  66. uint32 buf_size;
  67. uint32 stack_size = 4096;
  68. /*
  69. * disable app heap.
  70. * - we use wasi
  71. * - https://github.com/bytecodealliance/wasm-micro-runtime/issues/2275
  72. */
  73. uint32 heap_size = 0;
  74. RuntimeInitArgs init_args;
  75. memset(&init_args, 0, sizeof(RuntimeInitArgs));
  76. init_args.mem_alloc_type = Alloc_With_System_Allocator;
  77. init_args.n_native_symbols = sizeof(native_symbols) / sizeof(NativeSymbol);
  78. init_args.native_module_name = "env";
  79. init_args.native_symbols = native_symbols;
  80. if (!wasm_runtime_full_init(&init_args)) {
  81. printf("wasm_runtime_full_init failed.\n");
  82. return -1;
  83. }
  84. buffer = bh_read_file_to_buffer(module_path, &buf_size);
  85. if (!buffer) {
  86. printf("bh_read_file_to_buffer failed\n");
  87. goto fail;
  88. }
  89. module = wasm_runtime_load((uint8 *)buffer, buf_size, error_buf,
  90. sizeof(error_buf));
  91. if (!module) {
  92. printf("wasm_runtime_load failed: %s\n", error_buf);
  93. goto fail;
  94. }
  95. /* header */
  96. printf(" stack size | fail? | leak? | exception\n");
  97. printf("-------------------------------------------------------------------"
  98. "--------\n");
  99. uint32_t page_size = os_getpagesize();
  100. unsigned int stack;
  101. unsigned int prevstack = 0; /* appease GCC -Wmaybe-uninitialized */
  102. unsigned int stack_range_start = 0;
  103. unsigned int stack_range_end = page_size * 6;
  104. unsigned int step = 16;
  105. struct record rec0;
  106. struct record rec1;
  107. struct record *rec = &rec0;
  108. struct record *prevrec = &rec1;
  109. bool have_prevrec = false;
  110. for (stack = stack_range_start; stack < stack_range_end; stack += step) {
  111. wasm_module_inst_t module_inst = NULL;
  112. wasm_exec_env_t exec_env = NULL;
  113. bool failed = true;
  114. const char *exception = NULL;
  115. nest = 0;
  116. canary_init();
  117. module_inst = wasm_runtime_instantiate(module, stack_size, heap_size,
  118. error_buf, sizeof(error_buf));
  119. if (!module_inst) {
  120. printf("wasm_runtime_instantiate failed: %s\n", error_buf);
  121. goto fail2;
  122. }
  123. exec_env = wasm_runtime_create_exec_env(module_inst, stack_size);
  124. if (!exec_env) {
  125. printf("wasm_runtime_create_exec_env failed\n");
  126. goto fail2;
  127. }
  128. wasm_function_inst_t func =
  129. wasm_runtime_lookup_function(module_inst, funcname);
  130. if (!func) {
  131. printf("wasm_runtime_lookup_function failed for %s\n", funcname);
  132. goto fail2;
  133. }
  134. /* note: the function type is (ii)i */
  135. uint32_t wasm_argv[] = {
  136. stack, /* native_stack */
  137. 30, /* recurse_count */
  138. };
  139. uint32_t wasm_argc = 2;
  140. if (!wasm_runtime_call_wasm(exec_env, func, wasm_argc, wasm_argv)) {
  141. exception = wasm_runtime_get_exception(module_inst);
  142. goto fail2;
  143. }
  144. failed = false;
  145. fail2:
  146. if (!canary_check()) {
  147. printf("stack overurn detected for stack=%u\n", stack);
  148. abort();
  149. }
  150. /*
  151. * note: non-zero "nest" here demonstrates resource leak on longjmp
  152. * from signal handler.
  153. * cf.
  154. * https://github.com/bytecodealliance/wasm-micro-runtime/issues/3320
  155. */
  156. memset(rec, 0, sizeof(*rec));
  157. rec->failed = failed;
  158. rec->leaked = nest != 0;
  159. strncpy(rec->exception, exception ? exception : "",
  160. sizeof(rec->exception));
  161. if (have_prevrec && memcmp(prevrec, rec, sizeof(*rec))) {
  162. print_record(prevstack, stack, prevrec);
  163. have_prevrec = false;
  164. }
  165. if (!have_prevrec) {
  166. prevstack = stack;
  167. struct record *tmp = prevrec;
  168. prevrec = rec;
  169. rec = tmp;
  170. have_prevrec = true;
  171. }
  172. if (exec_env) {
  173. wasm_runtime_destroy_exec_env(exec_env);
  174. }
  175. if (module_inst) {
  176. wasm_runtime_deinstantiate(module_inst);
  177. }
  178. }
  179. if (have_prevrec) {
  180. print_record(prevstack, stack, prevrec);
  181. }
  182. fail:
  183. if (module) {
  184. wasm_runtime_unload(module);
  185. }
  186. if (buffer) {
  187. BH_FREE(buffer);
  188. }
  189. wasm_runtime_destroy();
  190. }