Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Too many nested for-loops causes segfault #817

Open
JL102 opened this issue Jul 21, 2022 · 6 comments
Open

Too many nested for-loops causes segfault #817

JL102 opened this issue Jul 21, 2022 · 6 comments
Labels
bug Something isn't working crash An issue that could cause a crash

Comments

@JL102
Copy link
Contributor

JL102 commented Jul 21, 2022

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 JL102 added bug Something isn't working needs repro Needs an example to reproduce labels Jul 21, 2022
@Jarred-Sumner
Copy link
Collaborator

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

@JL102
Copy link
Contributor Author

JL102 commented Jul 23, 2022

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
Copy link
Contributor Author

JL102 commented Jul 23, 2022

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. 🤯

@sno2 sno2 added the segfault label Jul 29, 2022
@github-actions github-actions bot removed the crash label Oct 22, 2022
@Electroid Electroid added crash An issue that could cause a crash and removed needs repro Needs an example to reproduce labels Jan 18, 2023
@JL102
Copy link
Contributor Author

JL102 commented Sep 8, 2023

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)

@Electroid Electroid changed the title MINOR: Segmentation fault instead of useful error message when there are too many nested loops Too many nested for-loops causes segfault Oct 25, 2023
@LukasKastern
Copy link
Contributor

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

@Jarred-Sumner
Copy link
Collaborator

This is a stack overflow in Bun's JS parser

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working crash An issue that could cause a crash
Projects
None yet
Development

No branches or pull requests

5 participants