소스 검색

fix(tailcall): Fixes heap buffer overflow in fast interpreter (#4916) (#4917)

Signed-off-by: Stephen Berard <stephen.berard@outlook.com>
Stephen Berard 1 주 전
부모
커밋
4b306f0fc3

+ 7 - 0
core/iwasm/interpreter/wasm_interp_fast.c

@@ -7576,6 +7576,13 @@ wasm_interp_call_func_bytecode(WASMModuleInstance *module,
             }
         }
         frame->lp = frame->operand + cur_func->const_cell_num;
+        if ((uint8 *)(frame->lp + cur_func->param_cell_num)
+            > exec_env->wasm_stack.top_boundary) {
+            if (lp_base)
+                wasm_runtime_free(lp_base);
+            wasm_set_exception(module, "wasm operand stack overflow");
+            goto got_exception;
+        }
         if (lp - lp_base > 0) {
             word_copy(frame->lp, lp_base, lp - lp_base);
         }

+ 3 - 0
tests/regression/ba-issues/build_wamr.sh

@@ -63,4 +63,7 @@ build_iwasm "-DWAMR_BUILD_REF_TYPES=1 -DWAMR_BUILD_FAST_JIT=1 -DWAMR_BUILD_SIMD=
 # build default iwasm for testing wasm loader with branch hints enabled
 build_iwasm "-DWAMR_BUILD_BRANCH_HINTS=1" "default-branch-hints-enabled"
 
+# build default iwasm for testing tail call with fast-interp
+build_iwasm "-DWAMR_BUILD_REF_TYPES=1 -DWAMR_BUILD_FAST_INTERP=1 -DWAMR_BUILD_TAIL_CALL=1 -DWAMR_BUILD_LIBC_WASI=0" "default-tail-call-wasi-disabled"
+
 # TODO: add more version of iwasm, for example, sgx version

BIN
tests/regression/ba-issues/issues/issue-4916/tail_call_stack_overflow.wasm


+ 282 - 0
tests/regression/ba-issues/issues/issue-4916/tail_call_stack_overflow.wat

@@ -0,0 +1,282 @@
+(module
+
+  ;; Callee has ~1000 const cells (500 unique i64 constants).
+  ;; When tiny_caller does a return_call to this function, the fast interpreter
+  ;; must stage the parameter at frame->operand + callee->const_cell_num.
+  ;; Without the fix, this write extends past the end of tiny_caller's small
+  ;; frame and corrupts adjacent heap memory.
+  (func $callee (param i32) (result i32)
+    (drop (i64.add (i64.const 100000000001) (i64.const 100000000002)))
+    (drop (i64.add (i64.const 100000000003) (i64.const 100000000004)))
+    (drop (i64.add (i64.const 100000000005) (i64.const 100000000006)))
+    (drop (i64.add (i64.const 100000000007) (i64.const 100000000008)))
+    (drop (i64.add (i64.const 100000000009) (i64.const 100000000010)))
+    (drop (i64.add (i64.const 100000000011) (i64.const 100000000012)))
+    (drop (i64.add (i64.const 100000000013) (i64.const 100000000014)))
+    (drop (i64.add (i64.const 100000000015) (i64.const 100000000016)))
+    (drop (i64.add (i64.const 100000000017) (i64.const 100000000018)))
+    (drop (i64.add (i64.const 100000000019) (i64.const 100000000020)))
+    (drop (i64.add (i64.const 100000000021) (i64.const 100000000022)))
+    (drop (i64.add (i64.const 100000000023) (i64.const 100000000024)))
+    (drop (i64.add (i64.const 100000000025) (i64.const 100000000026)))
+    (drop (i64.add (i64.const 100000000027) (i64.const 100000000028)))
+    (drop (i64.add (i64.const 100000000029) (i64.const 100000000030)))
+    (drop (i64.add (i64.const 100000000031) (i64.const 100000000032)))
+    (drop (i64.add (i64.const 100000000033) (i64.const 100000000034)))
+    (drop (i64.add (i64.const 100000000035) (i64.const 100000000036)))
+    (drop (i64.add (i64.const 100000000037) (i64.const 100000000038)))
+    (drop (i64.add (i64.const 100000000039) (i64.const 100000000040)))
+    (drop (i64.add (i64.const 100000000041) (i64.const 100000000042)))
+    (drop (i64.add (i64.const 100000000043) (i64.const 100000000044)))
+    (drop (i64.add (i64.const 100000000045) (i64.const 100000000046)))
+    (drop (i64.add (i64.const 100000000047) (i64.const 100000000048)))
+    (drop (i64.add (i64.const 100000000049) (i64.const 100000000050)))
+    (drop (i64.add (i64.const 100000000051) (i64.const 100000000052)))
+    (drop (i64.add (i64.const 100000000053) (i64.const 100000000054)))
+    (drop (i64.add (i64.const 100000000055) (i64.const 100000000056)))
+    (drop (i64.add (i64.const 100000000057) (i64.const 100000000058)))
+    (drop (i64.add (i64.const 100000000059) (i64.const 100000000060)))
+    (drop (i64.add (i64.const 100000000061) (i64.const 100000000062)))
+    (drop (i64.add (i64.const 100000000063) (i64.const 100000000064)))
+    (drop (i64.add (i64.const 100000000065) (i64.const 100000000066)))
+    (drop (i64.add (i64.const 100000000067) (i64.const 100000000068)))
+    (drop (i64.add (i64.const 100000000069) (i64.const 100000000070)))
+    (drop (i64.add (i64.const 100000000071) (i64.const 100000000072)))
+    (drop (i64.add (i64.const 100000000073) (i64.const 100000000074)))
+    (drop (i64.add (i64.const 100000000075) (i64.const 100000000076)))
+    (drop (i64.add (i64.const 100000000077) (i64.const 100000000078)))
+    (drop (i64.add (i64.const 100000000079) (i64.const 100000000080)))
+    (drop (i64.add (i64.const 100000000081) (i64.const 100000000082)))
+    (drop (i64.add (i64.const 100000000083) (i64.const 100000000084)))
+    (drop (i64.add (i64.const 100000000085) (i64.const 100000000086)))
+    (drop (i64.add (i64.const 100000000087) (i64.const 100000000088)))
+    (drop (i64.add (i64.const 100000000089) (i64.const 100000000090)))
+    (drop (i64.add (i64.const 100000000091) (i64.const 100000000092)))
+    (drop (i64.add (i64.const 100000000093) (i64.const 100000000094)))
+    (drop (i64.add (i64.const 100000000095) (i64.const 100000000096)))
+    (drop (i64.add (i64.const 100000000097) (i64.const 100000000098)))
+    (drop (i64.add (i64.const 100000000099) (i64.const 100000000100)))
+    (drop (i64.add (i64.const 100000000101) (i64.const 100000000102)))
+    (drop (i64.add (i64.const 100000000103) (i64.const 100000000104)))
+    (drop (i64.add (i64.const 100000000105) (i64.const 100000000106)))
+    (drop (i64.add (i64.const 100000000107) (i64.const 100000000108)))
+    (drop (i64.add (i64.const 100000000109) (i64.const 100000000110)))
+    (drop (i64.add (i64.const 100000000111) (i64.const 100000000112)))
+    (drop (i64.add (i64.const 100000000113) (i64.const 100000000114)))
+    (drop (i64.add (i64.const 100000000115) (i64.const 100000000116)))
+    (drop (i64.add (i64.const 100000000117) (i64.const 100000000118)))
+    (drop (i64.add (i64.const 100000000119) (i64.const 100000000120)))
+    (drop (i64.add (i64.const 100000000121) (i64.const 100000000122)))
+    (drop (i64.add (i64.const 100000000123) (i64.const 100000000124)))
+    (drop (i64.add (i64.const 100000000125) (i64.const 100000000126)))
+    (drop (i64.add (i64.const 100000000127) (i64.const 100000000128)))
+    (drop (i64.add (i64.const 100000000129) (i64.const 100000000130)))
+    (drop (i64.add (i64.const 100000000131) (i64.const 100000000132)))
+    (drop (i64.add (i64.const 100000000133) (i64.const 100000000134)))
+    (drop (i64.add (i64.const 100000000135) (i64.const 100000000136)))
+    (drop (i64.add (i64.const 100000000137) (i64.const 100000000138)))
+    (drop (i64.add (i64.const 100000000139) (i64.const 100000000140)))
+    (drop (i64.add (i64.const 100000000141) (i64.const 100000000142)))
+    (drop (i64.add (i64.const 100000000143) (i64.const 100000000144)))
+    (drop (i64.add (i64.const 100000000145) (i64.const 100000000146)))
+    (drop (i64.add (i64.const 100000000147) (i64.const 100000000148)))
+    (drop (i64.add (i64.const 100000000149) (i64.const 100000000150)))
+    (drop (i64.add (i64.const 100000000151) (i64.const 100000000152)))
+    (drop (i64.add (i64.const 100000000153) (i64.const 100000000154)))
+    (drop (i64.add (i64.const 100000000155) (i64.const 100000000156)))
+    (drop (i64.add (i64.const 100000000157) (i64.const 100000000158)))
+    (drop (i64.add (i64.const 100000000159) (i64.const 100000000160)))
+    (drop (i64.add (i64.const 100000000161) (i64.const 100000000162)))
+    (drop (i64.add (i64.const 100000000163) (i64.const 100000000164)))
+    (drop (i64.add (i64.const 100000000165) (i64.const 100000000166)))
+    (drop (i64.add (i64.const 100000000167) (i64.const 100000000168)))
+    (drop (i64.add (i64.const 100000000169) (i64.const 100000000170)))
+    (drop (i64.add (i64.const 100000000171) (i64.const 100000000172)))
+    (drop (i64.add (i64.const 100000000173) (i64.const 100000000174)))
+    (drop (i64.add (i64.const 100000000175) (i64.const 100000000176)))
+    (drop (i64.add (i64.const 100000000177) (i64.const 100000000178)))
+    (drop (i64.add (i64.const 100000000179) (i64.const 100000000180)))
+    (drop (i64.add (i64.const 100000000181) (i64.const 100000000182)))
+    (drop (i64.add (i64.const 100000000183) (i64.const 100000000184)))
+    (drop (i64.add (i64.const 100000000185) (i64.const 100000000186)))
+    (drop (i64.add (i64.const 100000000187) (i64.const 100000000188)))
+    (drop (i64.add (i64.const 100000000189) (i64.const 100000000190)))
+    (drop (i64.add (i64.const 100000000191) (i64.const 100000000192)))
+    (drop (i64.add (i64.const 100000000193) (i64.const 100000000194)))
+    (drop (i64.add (i64.const 100000000195) (i64.const 100000000196)))
+    (drop (i64.add (i64.const 100000000197) (i64.const 100000000198)))
+    (drop (i64.add (i64.const 100000000199) (i64.const 100000000200)))
+    (drop (i64.add (i64.const 100000000201) (i64.const 100000000202)))
+    (drop (i64.add (i64.const 100000000203) (i64.const 100000000204)))
+    (drop (i64.add (i64.const 100000000205) (i64.const 100000000206)))
+    (drop (i64.add (i64.const 100000000207) (i64.const 100000000208)))
+    (drop (i64.add (i64.const 100000000209) (i64.const 100000000210)))
+    (drop (i64.add (i64.const 100000000211) (i64.const 100000000212)))
+    (drop (i64.add (i64.const 100000000213) (i64.const 100000000214)))
+    (drop (i64.add (i64.const 100000000215) (i64.const 100000000216)))
+    (drop (i64.add (i64.const 100000000217) (i64.const 100000000218)))
+    (drop (i64.add (i64.const 100000000219) (i64.const 100000000220)))
+    (drop (i64.add (i64.const 100000000221) (i64.const 100000000222)))
+    (drop (i64.add (i64.const 100000000223) (i64.const 100000000224)))
+    (drop (i64.add (i64.const 100000000225) (i64.const 100000000226)))
+    (drop (i64.add (i64.const 100000000227) (i64.const 100000000228)))
+    (drop (i64.add (i64.const 100000000229) (i64.const 100000000230)))
+    (drop (i64.add (i64.const 100000000231) (i64.const 100000000232)))
+    (drop (i64.add (i64.const 100000000233) (i64.const 100000000234)))
+    (drop (i64.add (i64.const 100000000235) (i64.const 100000000236)))
+    (drop (i64.add (i64.const 100000000237) (i64.const 100000000238)))
+    (drop (i64.add (i64.const 100000000239) (i64.const 100000000240)))
+    (drop (i64.add (i64.const 100000000241) (i64.const 100000000242)))
+    (drop (i64.add (i64.const 100000000243) (i64.const 100000000244)))
+    (drop (i64.add (i64.const 100000000245) (i64.const 100000000246)))
+    (drop (i64.add (i64.const 100000000247) (i64.const 100000000248)))
+    (drop (i64.add (i64.const 100000000249) (i64.const 100000000250)))
+    (drop (i64.add (i64.const 100000000251) (i64.const 100000000252)))
+    (drop (i64.add (i64.const 100000000253) (i64.const 100000000254)))
+    (drop (i64.add (i64.const 100000000255) (i64.const 100000000256)))
+    (drop (i64.add (i64.const 100000000257) (i64.const 100000000258)))
+    (drop (i64.add (i64.const 100000000259) (i64.const 100000000260)))
+    (drop (i64.add (i64.const 100000000261) (i64.const 100000000262)))
+    (drop (i64.add (i64.const 100000000263) (i64.const 100000000264)))
+    (drop (i64.add (i64.const 100000000265) (i64.const 100000000266)))
+    (drop (i64.add (i64.const 100000000267) (i64.const 100000000268)))
+    (drop (i64.add (i64.const 100000000269) (i64.const 100000000270)))
+    (drop (i64.add (i64.const 100000000271) (i64.const 100000000272)))
+    (drop (i64.add (i64.const 100000000273) (i64.const 100000000274)))
+    (drop (i64.add (i64.const 100000000275) (i64.const 100000000276)))
+    (drop (i64.add (i64.const 100000000277) (i64.const 100000000278)))
+    (drop (i64.add (i64.const 100000000279) (i64.const 100000000280)))
+    (drop (i64.add (i64.const 100000000281) (i64.const 100000000282)))
+    (drop (i64.add (i64.const 100000000283) (i64.const 100000000284)))
+    (drop (i64.add (i64.const 100000000285) (i64.const 100000000286)))
+    (drop (i64.add (i64.const 100000000287) (i64.const 100000000288)))
+    (drop (i64.add (i64.const 100000000289) (i64.const 100000000290)))
+    (drop (i64.add (i64.const 100000000291) (i64.const 100000000292)))
+    (drop (i64.add (i64.const 100000000293) (i64.const 100000000294)))
+    (drop (i64.add (i64.const 100000000295) (i64.const 100000000296)))
+    (drop (i64.add (i64.const 100000000297) (i64.const 100000000298)))
+    (drop (i64.add (i64.const 100000000299) (i64.const 100000000300)))
+    (drop (i64.add (i64.const 100000000301) (i64.const 100000000302)))
+    (drop (i64.add (i64.const 100000000303) (i64.const 100000000304)))
+    (drop (i64.add (i64.const 100000000305) (i64.const 100000000306)))
+    (drop (i64.add (i64.const 100000000307) (i64.const 100000000308)))
+    (drop (i64.add (i64.const 100000000309) (i64.const 100000000310)))
+    (drop (i64.add (i64.const 100000000311) (i64.const 100000000312)))
+    (drop (i64.add (i64.const 100000000313) (i64.const 100000000314)))
+    (drop (i64.add (i64.const 100000000315) (i64.const 100000000316)))
+    (drop (i64.add (i64.const 100000000317) (i64.const 100000000318)))
+    (drop (i64.add (i64.const 100000000319) (i64.const 100000000320)))
+    (drop (i64.add (i64.const 100000000321) (i64.const 100000000322)))
+    (drop (i64.add (i64.const 100000000323) (i64.const 100000000324)))
+    (drop (i64.add (i64.const 100000000325) (i64.const 100000000326)))
+    (drop (i64.add (i64.const 100000000327) (i64.const 100000000328)))
+    (drop (i64.add (i64.const 100000000329) (i64.const 100000000330)))
+    (drop (i64.add (i64.const 100000000331) (i64.const 100000000332)))
+    (drop (i64.add (i64.const 100000000333) (i64.const 100000000334)))
+    (drop (i64.add (i64.const 100000000335) (i64.const 100000000336)))
+    (drop (i64.add (i64.const 100000000337) (i64.const 100000000338)))
+    (drop (i64.add (i64.const 100000000339) (i64.const 100000000340)))
+    (drop (i64.add (i64.const 100000000341) (i64.const 100000000342)))
+    (drop (i64.add (i64.const 100000000343) (i64.const 100000000344)))
+    (drop (i64.add (i64.const 100000000345) (i64.const 100000000346)))
+    (drop (i64.add (i64.const 100000000347) (i64.const 100000000348)))
+    (drop (i64.add (i64.const 100000000349) (i64.const 100000000350)))
+    (drop (i64.add (i64.const 100000000351) (i64.const 100000000352)))
+    (drop (i64.add (i64.const 100000000353) (i64.const 100000000354)))
+    (drop (i64.add (i64.const 100000000355) (i64.const 100000000356)))
+    (drop (i64.add (i64.const 100000000357) (i64.const 100000000358)))
+    (drop (i64.add (i64.const 100000000359) (i64.const 100000000360)))
+    (drop (i64.add (i64.const 100000000361) (i64.const 100000000362)))
+    (drop (i64.add (i64.const 100000000363) (i64.const 100000000364)))
+    (drop (i64.add (i64.const 100000000365) (i64.const 100000000366)))
+    (drop (i64.add (i64.const 100000000367) (i64.const 100000000368)))
+    (drop (i64.add (i64.const 100000000369) (i64.const 100000000370)))
+    (drop (i64.add (i64.const 100000000371) (i64.const 100000000372)))
+    (drop (i64.add (i64.const 100000000373) (i64.const 100000000374)))
+    (drop (i64.add (i64.const 100000000375) (i64.const 100000000376)))
+    (drop (i64.add (i64.const 100000000377) (i64.const 100000000378)))
+    (drop (i64.add (i64.const 100000000379) (i64.const 100000000380)))
+    (drop (i64.add (i64.const 100000000381) (i64.const 100000000382)))
+    (drop (i64.add (i64.const 100000000383) (i64.const 100000000384)))
+    (drop (i64.add (i64.const 100000000385) (i64.const 100000000386)))
+    (drop (i64.add (i64.const 100000000387) (i64.const 100000000388)))
+    (drop (i64.add (i64.const 100000000389) (i64.const 100000000390)))
+    (drop (i64.add (i64.const 100000000391) (i64.const 100000000392)))
+    (drop (i64.add (i64.const 100000000393) (i64.const 100000000394)))
+    (drop (i64.add (i64.const 100000000395) (i64.const 100000000396)))
+    (drop (i64.add (i64.const 100000000397) (i64.const 100000000398)))
+    (drop (i64.add (i64.const 100000000399) (i64.const 100000000400)))
+    (drop (i64.add (i64.const 100000000401) (i64.const 100000000402)))
+    (drop (i64.add (i64.const 100000000403) (i64.const 100000000404)))
+    (drop (i64.add (i64.const 100000000405) (i64.const 100000000406)))
+    (drop (i64.add (i64.const 100000000407) (i64.const 100000000408)))
+    (drop (i64.add (i64.const 100000000409) (i64.const 100000000410)))
+    (drop (i64.add (i64.const 100000000411) (i64.const 100000000412)))
+    (drop (i64.add (i64.const 100000000413) (i64.const 100000000414)))
+    (drop (i64.add (i64.const 100000000415) (i64.const 100000000416)))
+    (drop (i64.add (i64.const 100000000417) (i64.const 100000000418)))
+    (drop (i64.add (i64.const 100000000419) (i64.const 100000000420)))
+    (drop (i64.add (i64.const 100000000421) (i64.const 100000000422)))
+    (drop (i64.add (i64.const 100000000423) (i64.const 100000000424)))
+    (drop (i64.add (i64.const 100000000425) (i64.const 100000000426)))
+    (drop (i64.add (i64.const 100000000427) (i64.const 100000000428)))
+    (drop (i64.add (i64.const 100000000429) (i64.const 100000000430)))
+    (drop (i64.add (i64.const 100000000431) (i64.const 100000000432)))
+    (drop (i64.add (i64.const 100000000433) (i64.const 100000000434)))
+    (drop (i64.add (i64.const 100000000435) (i64.const 100000000436)))
+    (drop (i64.add (i64.const 100000000437) (i64.const 100000000438)))
+    (drop (i64.add (i64.const 100000000439) (i64.const 100000000440)))
+    (drop (i64.add (i64.const 100000000441) (i64.const 100000000442)))
+    (drop (i64.add (i64.const 100000000443) (i64.const 100000000444)))
+    (drop (i64.add (i64.const 100000000445) (i64.const 100000000446)))
+    (drop (i64.add (i64.const 100000000447) (i64.const 100000000448)))
+    (drop (i64.add (i64.const 100000000449) (i64.const 100000000450)))
+    (drop (i64.add (i64.const 100000000451) (i64.const 100000000452)))
+    (drop (i64.add (i64.const 100000000453) (i64.const 100000000454)))
+    (drop (i64.add (i64.const 100000000455) (i64.const 100000000456)))
+    (drop (i64.add (i64.const 100000000457) (i64.const 100000000458)))
+    (drop (i64.add (i64.const 100000000459) (i64.const 100000000460)))
+    (drop (i64.add (i64.const 100000000461) (i64.const 100000000462)))
+    (drop (i64.add (i64.const 100000000463) (i64.const 100000000464)))
+    (drop (i64.add (i64.const 100000000465) (i64.const 100000000466)))
+    (drop (i64.add (i64.const 100000000467) (i64.const 100000000468)))
+    (drop (i64.add (i64.const 100000000469) (i64.const 100000000470)))
+    (drop (i64.add (i64.const 100000000471) (i64.const 100000000472)))
+    (drop (i64.add (i64.const 100000000473) (i64.const 100000000474)))
+    (drop (i64.add (i64.const 100000000475) (i64.const 100000000476)))
+    (drop (i64.add (i64.const 100000000477) (i64.const 100000000478)))
+    (drop (i64.add (i64.const 100000000479) (i64.const 100000000480)))
+    (drop (i64.add (i64.const 100000000481) (i64.const 100000000482)))
+    (drop (i64.add (i64.const 100000000483) (i64.const 100000000484)))
+    (drop (i64.add (i64.const 100000000485) (i64.const 100000000486)))
+    (drop (i64.add (i64.const 100000000487) (i64.const 100000000488)))
+    (drop (i64.add (i64.const 100000000489) (i64.const 100000000490)))
+    (drop (i64.add (i64.const 100000000491) (i64.const 100000000492)))
+    (drop (i64.add (i64.const 100000000493) (i64.const 100000000494)))
+    (drop (i64.add (i64.const 100000000495) (i64.const 100000000496)))
+    (drop (i64.add (i64.const 100000000497) (i64.const 100000000498)))
+    (drop (i64.add (i64.const 100000000499) (i64.const 100000000500)))
+    (local.get 0)
+  )
+
+  ;; Tiny caller: no locals, no constants. Does a return_call to $callee.
+  ;; This is the vulnerable site: tiny_caller's frame has const_cell_num=0
+  ;; but $callee has const_cell_num~=1000, so without the fix the fast
+  ;; interpreter writes the staged parameter ~4000 bytes past the end of
+  ;; tiny_caller's frame.
+  (func $tiny_caller (result i32)
+    (return_call $callee (i32.const 42))
+  )
+
+  ;; Builds up 1000 normal call frames then tail-calls through tiny_caller.
+  (func $fill (param i32) (result i32)
+    (if (result i32) (i32.gt_s (local.get 0) (i32.const 0))
+      (then (call $fill (i32.sub (local.get 0) (i32.const 1))))
+      (else (call $tiny_caller))
+    )
+  )
+
+  (func (export "test") (result i32)
+    (call $fill (i32.const 1000))
+  )
+)

+ 16 - 0
tests/regression/ba-issues/running_config.json

@@ -1852,6 +1852,22 @@
                 "description": ""
             }
 
+        },
+        {
+            "deprecated": false,
+            "ids": [
+                4916
+            ],
+            "runtime": "iwasm-default-tail-call-wasi-disabled",
+            "file": "tail_call_stack_overflow.wasm",
+            "mode": "fast-interp",
+            "options": "--stack-size=131072 --heap-size=0 -f test",
+            "argument": "",
+            "expected return": {
+                "ret code": 0,
+                "stdout content": "0x2a:i32",
+                "description": "no heap buffer overflow from return_call when callee has a larger const pool than caller"
+            }
         }
     ]
 }