bun icon indicating copy to clipboard operation
bun copied to clipboard

MINOR: Segmentation fault instead of useful error message when there are too many nested loops

Open JL102 opened this issue 2 years ago • 3 comments

Version

0.1.4

Platform

Linux jbook 5.18.0-2-amd64 #1 SMP PREEMPT_DYNAMIC Debian 5.18.5-1 (2022-06-16) x86_64 GNU/Linux

What steps will reproduce the bug?

Create a JS file with a LOT of nested for loops - specifically 1,875 of them on my system.

let counter = 0;
for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) 
for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) 
for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) 
(... copied and pasted many, many times)
for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) counter++;
console.log(counter);

Attempt to run the file with Bun:

$ bun test.js
Segmentation fault
$

it crashes with a segfault instead of throwing a JS RangeError.

How often does it reproduce? Is there a required condition?

It reproduces reliably once you add enough for loops.

What is the expected behavior?

Expected: Something akin to this, as Bun already can throw RangeErrors for recursive functions:

476 | function foo() {
477 |   foo();
    ^
 RangeError: Maximum call stack size exceeded.

This is what NodeJS throws:

$ node test.js
node:vm:352
  const result = _compileFunction(
                 ^

RangeError: Maximum call stack size exceeded
    at Object.compileFunction (node:vm:352:18)
    at wrapSafe (node:internal/modules/cjs/loader:1033:15)
    at Module._compile (node:internal/modules/cjs/loader:1069:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
    at Module.load (node:internal/modules/cjs/loader:981:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
    at node:internal/main/run_main_module:17:47
$

It's worth noting that Node throws a RangeError after "only" 1,274 nested loops (on my system).

What do you see instead?

$ bun test.js
Segmentation fault
$

Additional information

This is minor, since it's an edge case, but still worth reporting I'm sure, to improve Bun's stability.

JL102 avatar Jul 21 '22 16:07 JL102

Does it happen if you run the file with bun build file-with-lots-of-loops.js?

That would answer if the bug is in the transpiler or if the bug is in how Bun embeds JavaScriptCore

Jarred-Sumner avatar Jul 21 '22 22:07 Jarred-Sumner

Yes; bun build file.js results in the same Segmentation fault.

It may be worth testing this on the Safari browser, to see if JavaScriptCore actually has protection for this kind of call stack issue.

JL102 avatar Jul 23 '22 01:07 JL102

Just tested it on my roommate's MacBook, in Safari's built in JS console. It manages to handle way more nested for loops. It throws a RangeError: Maximum call stack size exceeded. on the 8,064th nested loop. 🤯

JL102 avatar Jul 23 '22 02:07 JL102

In bun version 1.0, it's now able to survive a shocking 8,710 nested loops when running, but the behavior is still quite strange.

I once again have an original loops.js:

let counter = 0;
for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) 
for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) 
for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) 
(... copied and pasted many, many times)
for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) for (let i = 0; i < 1; i++) counter++;
console.log(counter);

When there are precisely 8,711 instances of for (let i = 0; i < 1; i++, bun loops.js runs as expected and console-logs 1. However, running bun build loops.js or bun build --bun loops.js or bun build loops.js > out/loops.js, it gets a Segmentation fault. My guess is that the segfault occurs when bun's attempting to write the built file to stdout?

I also tested the number of fors that bun build can handle before getting a segfault, and it's precisely 2,034. This is suspiciously close to 2,048. Maybe the segfault arises from attempting to write too many characters to a line? (When editing out/loops.js to add the line that would be there if the segfault didn't occur at 2,035 fors, the line with the last for (let i2035 = 0;i2035 < 1; i2035++) is 4,106 characters long and the line with counter++ is 4,080 characters long)

JL102 avatar Sep 08 '23 16:09 JL102

This is the segfault I get when trying to reproduce the above snippet with ~2500 lines.

Thread 2 "Bun Pool 0" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7ffff7b7e6c0 (LWP 8300)]
0x000055555c730a72 in src.js_parser.NewParser_(false,.react,false).parseSuffix (p=0x7ffff7b0bdb0, _left=..., level=comma, errors=0x0, flags=none) at /home/yourname/Projects/bun/src/js_parser.zig:13177
13177                   switch (p.lexer.token) {
(gdb) bt
#0  0x000055555c730a72 in src.js_parser.NewParser_(false,.react,false).parseSuffix (p=0x7ffff7b0bdb0, _left=..., level=comma, errors=0x0, flags=none) at /home/yourname/Projects/bun/src/js_parser.zig:13177
#1  0x000055555c720e8e in src.js_parser.NewParser_(false,.react,false).parseExprCommon (p=0x7ffff7b0bdb0, level=comma, errors=0x0, flags=none) at /home/yourname/Projects/bun/src/js_parser.zig:12304
#2  0x000055555c7302bb in src.js_parser.NewParser_(false,.react,false).parseExpr () at /home/yourname/Projects/bun/src/js_parser.zig:12269
#3  src.js_parser.NewParser_(false,.react,false).parseAndDeclareDecls (p=0x7ffff7b0bdb0, kind=other, opts=0x7ffff778b2f0) at /home/yourname/Projects/bun/src/js_parser.zig:11158
#4  0x000055555c72f8b5 in src.js_parser.NewParser_(false,.react,false).parseExprOrLetStmt (p=0x7ffff7b0bdb0, opts=0x7ffff778b2f0) at /home/yourname/Projects/bun/src/js_parser.zig:10859
#5  0x000055555c2425aa in src.js_parser.NewParser_(false,.react,false).parseStmt (p=0x7ffff7b0bdb0, opts=0x7ffff7793ef8) at /home/yourname/Projects/bun/src/js_parser.zig:9865
#6  0x000055555c244502 in src.js_parser.NewParser_(false,.react,false).parseStmt (p=0x7ffff7b0bdb0, opts=0x7ffff779bd58) at /home/yourname/Projects/bun/src/js_parser.zig:9944
#7  0x000055555c244502 in src.js_parser.NewParser_(false,.react,false).parseStmt (p=0x7ffff7b0bdb0, opts=0x7ffff77a3bb8) at /home/yourname/Projects/bun/src/js_parser.zig:9944
#8  0x000055555c244502 in src.js_parser.NewParser_(false,.react,false).parseStmt (p=0x7ffff7b0bdb0, opts=0x7ffff77aba18) at /home/yourname/Projects/bun/src/js_parser.zig:9944
#9  0x000055555c244502 in src.js_parser.NewParser_(false,.react,false).parseStmt (p=0x7ffff7b0bdb0, opts=0x7ffff77b3878) at /home/yourname/Projects/bun/src/js_parser.zig:9944
#10 0x000055555c244502 in src.js_parser.NewParser_(false,.react,false).parseStmt (p=0x7ffff7b0bdb0, opts=0x7ffff77bb6d8) at /home/yourname/Projects/bun/src/js_parser.zig:9944
...
#111 0x000055555c244502 in src.js_parser.NewParser_(false,.react,false).parseStmt (p=0x7ffff7b0bdb0, opts=0x7ffff7ad92b8) at /home/yourname/Projects/bun/src/js_parser.zig:9944
#112 0x000055555c244502 in src.js_parser.NewParser_(false,.react,false).parseStmt (p=0x7ffff7b0bdb0, opts=0x7ffff7ae1118) at /home/yourname/Projects/bun/src/js_parser.zig:9944
#113 0x000055555c244502 in src.js_parser.NewParser_(false,.react,false).parseStmt (p=0x7ffff7b0bdb0, opts=0x7ffff7ae8f78) at /home/yourname/Projects/bun/src/js_parser.zig:9944
#114 0x000055555c244502 in src.js_parser.NewParser_(false,.react,false).parseStmt (p=0x7ffff7b0bdb0, opts=0x7ffff7af0dd8) at /home/yourname/Projects/bun/src/js_parser.zig:9944
#115 0x000055555c244502 in src.js_parser.NewParser_(false,.react,false).parseStmt (p=0x7ffff7b0bdb0, opts=0x7ffff7af8c38) at /home/yourname/Projects/bun/src/js_parser.zig:9944
#116 0x000055555c244502 in src.js_parser.NewParser_(false,.react,false).parseStmt (p=0x7ffff7b0bdb0, opts=0x7ffff7b00a98) at /home/yourname/Projects/bun/src/js_parser.zig:9944
#117 0x000055555c244502 in src.js_parser.NewParser_(false,.react,false).parseStmt (p=0x7ffff7b0bdb0, opts=0x7ffff7b088f8) at /home/yourname/Projects/bun/src/js_parser.zig:9944
#118 0x000055555c244502 in src.js_parser.NewParser_(false,.react,false).parseStmt (p=0x7ffff7b0bdb0, opts=0x7ffff7b0a918) at /home/yourname/Projects/bun/src/js_parser.zig:9944
#119 0x000055555c236b87 in src.js_parser.NewParser_(false,.react,false).parseStmtsUpTo (p=0x7ffff7b0bdb0, eend=t_end_of_file, _opts=0x7ffff7b0e0d8) at /home/yourname/Projects/bun/src/js_parser.zig:11547
#120 0x000055555c25b691 in src.js_parser.Parser._parse__anon_147939 (self=0x7ffff7b24d10) at /home/yourname/Projects/bun/src/js_parser.zig:3143
#121 0x000055555c2a1969 in src.js_parser.Parser.parse (self=0x7ffff7b24d10) at /home/yourname/Projects/bun/src/js_parser.zig:3015
#122 0x000055555c42d5a6 in src.cache.JavaScript.parse (allocator=..., opts=..., defines=0x20000200060, log=0x7ffff7b34038, source=0x7ffff7b2a248) at /home/yourname/Projects/bun/src/cache.zig:257
#123 0x000055555c444bd3 in src.bundler.bundle_v2.ParseTask.getAST (log=0x7ffff7b34038, bundler=0x7ffff7b2e0d8, opts=..., allocator=..., resolver=0x7ffff7b2eb88, source=..., loader=jsx, unique_key_prefix=7140080665904418178,
    unique_key_for_additional_file=0x7ffff7b312d8) at /home/yourname/Projects/bun/src/bundler/bundle_v2.zig:2466
#124 0x000055555bf30092 in src.bundler.bundle_v2.ParseTask.run_ (task=0x20000270400, this=0x20002020000, step=0x7ffff7b33f17, log=0x7ffff7b34038) at /home/yourname/Projects/bun/src/bundler/bundle_v2.zig:2677
#125 0x000055555bad7bc8 in src.bundler.bundle_v2.ParseTask.run (this=0x20000270400) at /home/yourname/Projects/bun/src/bundler/bundle_v2.zig:2743
#126 0x000055555b6d97a8 in src.bundler.bundle_v2.ParseTask.callback (this=0x20000270588) at /home/yourname/Projects/bun/src/bundler/bundle_v2.zig:2730
#127 0x000055555c8984c5 in src.thread_pool.Thread.run (thread_pool=0x20000230060) at /home/yourname/Projects/bun/src/thread_pool.zig:725
#128 0x000055555c441afb in Thread.callFn__anon_160495 (args=...) at /home/yourname/Projects/bun/.cache/zig/lib/std/Thread.zig:411
#129 0x000055555bf2cc63 in Thread.PosixThreadImpl.spawn__anon_110764.Instance.entryFn (raw_arg=0x5555628d24a0) at /home/yourname/Projects/bun/.cache/zig/lib/std/Thread.zig:684
#130 0x00007ffff7c8f6ba in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:444
#131 0x00007ffff7d1e120 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

It looks like a stackoverflow (?)

And this is what the main thread is doing:

#0  bun_on_tick_before (vm=0x5555628caf60) at /home/yourname/Projects/bun/src/bun.js/bindings/JSCUSocketsLoopIntegration.cpp:20
#1  0x000055555e1eec9e in us_loop_run_bun_tick (loop=0x5555628caf60, timeoutMs=0, tickCallbackContext=0x7ffff7fbb000) at /home/yourname/Projects/bun/packages/bun-usockets/src/eventing/epoll_kqueue.c:195
#2  0x000055555bbd4385 in src.deps.uws.PosixLoop.tick (this=0x5555628caf60) at /home/yourname/Projects/bun/src/deps/uws.zig:1015
#3  0x000055555bf32cf7 in src.bun.js.event_loop.MiniEventLoop.tick__anon_110840 (this=0x200001a2400, context=0x200001a2400) at /home/yourname/Projects/bun/src/bun.js/event_loop.zig:1394
#4  0x000055555badd454 in src.bun.js.event_loop.AnyEventLoop.tick__anon_88558 (this=0x200001a2400, context=0x200001a2400) at /home/yourname/Projects/bun/src/bun.js/event_loop.zig:1464
#5  0x000055555b7439a1 in src.bundler.bundle_v2.BundleV2.waitForParse (this=0x200001a2400) at /home/yourname/Projects/bun/src/bundler/bundle_v2.zig:469
#6  0x000055555b743365 in src.bundler.bundle_v2.BundleV2.generateFromCLI (bundler=0x7ffffffe58c8, allocator=..., event_loop=..., unique_key=7140080665904418178, enable_reloading=false, reachable_files_count=0x7ffffffec288,
    minify_duration=0x7ffffffec290, source_code_size=0x7ffffffec298) at /home/yourname/Projects/bun/src/bundler/bundle_v2.zig:1024
#7  0x000055555b74cf5b in src.cli.build_command.BuildCommand.exec (ctx_=...) at /home/yourname/Projects/bun/src/cli/build_command.zig:270
#8  0x000055555b9ab34c in src.cli.Command.start (allocator=..., log=0x555562753d48 <src.cli.Cli.log_>) at /home/yourname/Projects/bun/src/cli.zig:1317
#9  0x000055555b570533 in src.cli.Cli.start__anon_5066 (allocator=...) at /home/yourname/Projects/bun/src/cli.zig:57
#10 0x000055555b56e4e3 in src.main.main () at /home/yourname/Projects/bun/src/main.zig:54
#11 0x000055555b56df45 in start.callMain () at /home/yourname/Projects/bun/.cache/zig/lib/std/start.zig:575
#12 start.initEventLoopAndCallMain () at /home/yourname/Projects/bun/.cache/zig/lib/std/start.zig:519
#13 start.callMainWithArgs () at /home/yourname/Projects/bun/.cache/zig/lib/std/start.zig:469
#14 main (c_argc=3, c_argv=0x7fffffffda58, c_envp=0x7fffffffda78) at /home/yourname/Projects/bun/.cache/zig/lib/std/start.zig:484

LukasKastern avatar Jan 16 '24 16:01 LukasKastern