wryun / es-shell

es: a shell with higher-order functions
http://wryun.github.io/es-shell/
Other
313 stars 26 forks source link

Settor function call during exception unwinding causes a segfault with `-DGCDEBUG` #85

Closed jpco closed 8 months ago

jpco commented 9 months ago

Minimal repro: compile with -DGCDEBUG=1 and run

./es -c 'set-foo = result; local (foo = !) throw whatever'

It seems the problem is that if a GC is triggered within in the call chain of throw(e) -> varpop(foo) -> callsettor(foo, NULL) -> append(set-foo, NULL) -> gcreserve(), then e becomes invalid memory by the time it's being used in a CatchException block. This implies there's a missing root for e somewhere.

Unfortunately, because throw hacks up the root list, it's tricky to try to root e in the stack there. I wonder if there's some globalroot-related workaround that's possible. Or maybe e needs to be rooted in handler->rootlist before calling the varpop()s? That seems a little wacky.

jpco commented 9 months ago

Okay, I tried a globalroot fix and it confirmed that's where the bug is. Unfortunately doing that is itself buggy as illustrated in the following scenario:

./es -c 'set-foo = @ {if {~ $* ()} {catch @ {result ()} {throw error set-foo YIKES}}}; local (foo = !) {throw whatever}'
YIKES

Using a simple globalroot causes the second throw in set-foo to clobber the exception from the first throw whatever, which means that the catcher in $&batchloop sees the exception error set-foo YIKES, when it should see the exception whatever. Not great.