Summary
When Ruby 3.4 or 4.0 is compiled to WebAssembly (wasm32-wasi) with Prism as the default parser, string-based eval and class_eval calls crash with an out-of-bounds memory access in pm_parser_init.
Building with --with-parser=parse.y and overriding RB_DEFAULT_PARSER to RB_DEFAULT_PARSER_PARSE_Y avoids the crash.
This issue is filed to investigate whether pm_parser_init has a WASM-specific bug. The parse.y workaround is a temporary measure; ideally Prism should work correctly in WASM environments without requiring a parser downgrade.
Environment
- Ruby: 3.4.8 / 4.0.2 (compiled to wasm32-unknown-wasip1 via ruby_wasm 2.9.0)
- wasi_sdk: 24.0
- WASM runtime: wasmtime
Steps to reproduce
- Build Ruby 3.4 or 4.0 for WASM with the default Prism parser
- Run
wasmtime run ruby-core.wasm -e 'eval("puts \"hello\"")'
Observed behaviour
The process crashes with exit code 134 (SIGABRT):
Error: failed to run main module ruby-core.wasm
...
ruby!pm_parser_init
ruby!pm_parse_string
ruby!eval_make_iseq
ruby!eval_string_with_cref
ruby!specific_eval
ruby!rb_mod_module_eval_internal
...
memory fault at wasm address 0xfffffffc in linear memory of size 0xffd30000
wasm trap: out of bounds memory access
The crash occurs in the call chain:
rb_mod_module_eval_internal → specific_eval → eval_string_with_cref → eval_make_iseq → pm_parse_string → pm_parser_init.
Ruby 3.3 (which uses parse.y by default) does not exhibit this crash.
Note that puts "hello" without eval does not crash; the issue is specific to string-based eval paths that invoke Prism.
Workaround
Forcing the parse.y parser avoids the crash. Two changes are needed:
- Source patch (
version.c): override RB_DEFAULT_PARSER to
RB_DEFAULT_PARSER_PARSE_Y
- Build flag: pass
--with-parser=parse.y to configure
Patch file:
https://github.com/aim2bpg/rubree/blob/main/ruby_wasm_patches/fix-default-parser-parse-y.patch
With this workaround applied, Ruby 3.4.8 and 4.0.2 boot and run successfully in WASM (verified with a Rails 8.1.3 application).
Note: this is a temporary workaround for WASM environments only and has no effect on native Ruby builds.
Related
Summary
When Ruby 3.4 or 4.0 is compiled to WebAssembly (wasm32-wasi) with Prism as the default parser, string-based
evalandclass_evalcalls crash with an out-of-bounds memory access inpm_parser_init.Building with
--with-parser=parse.yand overridingRB_DEFAULT_PARSERtoRB_DEFAULT_PARSER_PARSE_Yavoids the crash.This issue is filed to investigate whether
pm_parser_inithas a WASM-specific bug. The parse.y workaround is a temporary measure; ideally Prism should work correctly in WASM environments without requiring a parser downgrade.Environment
Steps to reproduce
wasmtime run ruby-core.wasm -e 'eval("puts \"hello\"")'Observed behaviour
The process crashes with exit code 134 (SIGABRT):
The crash occurs in the call chain:
rb_mod_module_eval_internal→specific_eval→eval_string_with_cref→eval_make_iseq→pm_parse_string→pm_parser_init.Ruby 3.3 (which uses parse.y by default) does not exhibit this crash.
Note that
puts "hello"withoutevaldoes not crash; the issue is specific to string-based eval paths that invoke Prism.Workaround
Forcing the parse.y parser avoids the crash. Two changes are needed:
version.c): overrideRB_DEFAULT_PARSERtoRB_DEFAULT_PARSER_PARSE_Y--with-parser=parse.ytoconfigurePatch file:
https://github.com/aim2bpg/rubree/blob/main/ruby_wasm_patches/fix-default-parser-parse-y.patch
With this workaround applied, Ruby 3.4.8 and 4.0.2 boot and run successfully in WASM (verified with a Rails 8.1.3 application).
Note: this is a temporary workaround for WASM environments only and has no effect on native Ruby builds.
Related
root cause: missing C extension)