main.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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. unsigned int stack;
  100. unsigned int prevstack = 0; /* appease GCC -Wmaybe-uninitialized */
  101. unsigned int stack_range_start = 0;
  102. unsigned int stack_range_end = 4096 * 6;
  103. unsigned int step = 16;
  104. struct record rec0;
  105. struct record rec1;
  106. struct record *rec = &rec0;
  107. struct record *prevrec = &rec1;
  108. bool have_prevrec = false;
  109. for (stack = stack_range_start; stack < stack_range_end; stack += step) {
  110. wasm_module_inst_t module_inst = NULL;
  111. wasm_exec_env_t exec_env = NULL;
  112. bool failed = true;
  113. const char *exception = NULL;
  114. nest = 0;
  115. canary_init();
  116. module_inst = wasm_runtime_instantiate(module, stack_size, heap_size,
  117. error_buf, sizeof(error_buf));
  118. if (!module_inst) {
  119. printf("wasm_runtime_instantiate failed: %s\n", error_buf);
  120. goto fail2;
  121. }
  122. exec_env = wasm_runtime_create_exec_env(module_inst, stack_size);
  123. if (!exec_env) {
  124. printf("wasm_runtime_create_exec_env failed\n");
  125. goto fail2;
  126. }
  127. wasm_function_inst_t func =
  128. wasm_runtime_lookup_function(module_inst, funcname);
  129. if (!func) {
  130. printf("wasm_runtime_lookup_function failed for %s\n", funcname);
  131. goto fail2;
  132. }
  133. /* note: the function type is (ii)i */
  134. uint32_t wasm_argv[] = {
  135. stack, /* native_stack */
  136. 30, /* recurse_count */
  137. };
  138. uint32_t wasm_argc = 2;
  139. if (!wasm_runtime_call_wasm(exec_env, func, wasm_argc, wasm_argv)) {
  140. exception = wasm_runtime_get_exception(module_inst);
  141. goto fail2;
  142. }
  143. failed = false;
  144. fail2:
  145. if (!canary_check()) {
  146. printf("stack overurn detected for stack=%u\n", stack);
  147. abort();
  148. }
  149. /*
  150. * note: non-zero "nest" here demonstrates resource leak on longjmp
  151. * from signal handler.
  152. * cf.
  153. * https://github.com/bytecodealliance/wasm-micro-runtime/issues/3320
  154. */
  155. memset(rec, 0, sizeof(*rec));
  156. rec->failed = failed;
  157. rec->leaked = nest != 0;
  158. strncpy(rec->exception, exception ? exception : "",
  159. sizeof(rec->exception));
  160. if (have_prevrec && memcmp(prevrec, rec, sizeof(*rec))) {
  161. print_record(prevstack, stack, prevrec);
  162. have_prevrec = false;
  163. }
  164. if (!have_prevrec) {
  165. prevstack = stack;
  166. struct record *tmp = prevrec;
  167. prevrec = rec;
  168. rec = tmp;
  169. have_prevrec = true;
  170. }
  171. if (exec_env) {
  172. wasm_runtime_destroy_exec_env(exec_env);
  173. }
  174. if (module_inst) {
  175. wasm_runtime_deinstantiate(module_inst);
  176. }
  177. }
  178. if (have_prevrec) {
  179. print_record(prevstack, stack, prevrec);
  180. }
  181. fail:
  182. if (module) {
  183. wasm_runtime_unload(module);
  184. }
  185. if (buffer) {
  186. BH_FREE(buffer);
  187. }
  188. wasm_runtime_destroy();
  189. }