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

RangeError in some scenarios calling RegEx #759

Closed
greim opened this issue Feb 8, 2015 · 24 comments
Closed

RangeError in some scenarios calling RegEx #759

greim opened this issue Feb 8, 2015 · 24 comments
Labels
v8 engine Issues and PRs related to the V8 dependency.

Comments

@greim
Copy link

greim commented Feb 8, 2015

Example of the error:

/Users/foo/project/node_modules/mold-source-map/index.js:9
  var m = source.match(convert.commentRegex);
                 ^
RangeError: Maximum call stack size exceeded
    at String.match (native)
    at extractComment (/Users/foo/project/node_modules/mold-source-map/index.js:9:18)
    at new SourceMolder (/Users/foo/project/node_modules/mold-source-map/index.js:44:18)
    at exports.fromSource (/Users/foo/project/node_modules/mold-source-map/index.js:73:10)
    at Stream.end (/Users/foo/project/node_modules/mold-source-map/index.js:63:24)
    at _end (/Users/foo/project/node_modules/mold-source-map/node_modules/through/index.js:61:9)
    at Stream.stream.end (/Users/foo/project/node_modules/mold-source-map/node_modules/through/index.js:70:5)
    at Labeled.onend (/Users/foo/project/node_modules/browserify/node_modules/labeled-stream-splicer/node_modules/stream-splicer/node_modules/readable-stream/lib/_stream_readable.js:537:10)
    at Labeled.g (events.js:184:16)
    at Labeled.emit (events.js:119:20)

It doesn't seem like mold-source-map or its deps are doing anything wrong. Here's an isolated test case: thlorenz/mold-source-map#5 (comment)

It doesn't happen on node 0.12 or 0.11. I wondered if this was a difference in newer v8? Unfortunately ./source.js (noted in the linked code) is proprietary, but it's 125k lines long, trailed by a 6.5 million character long one-liner source map comment.

@benjamingr
Copy link
Member

Parsing a regular expression causing a call stack exceeded error is definitely more likely a v8 bug. A quick peek at the v8 issue tracker doesn't show any bugs relating to that related You should file a bug against v8 - it definitely sounds like a regression they'll address.

@brendanashworth brendanashworth added the confirmed-bug Issues with confirmed bugs. label Feb 8, 2015
@micnic
Copy link
Contributor

micnic commented Feb 9, 2015

I'm not sure but this may be triggered because convert-source-map is using the non-standard, deprecated Object.prototype.__defineGetter__() method in these lines: https://github.com/thlorenz/convert-source-map/blob/master/index.js#L131-L134 , I would try to replace it with Object.defineProperty() and check if the behavior of your test code is the same.

@benjamingr
Copy link
Member

@micnic __defineGetter__ is used internally in v8, it's basically just a call to DefineOwnProperty with a created property descriptor as you can see here https://github.com/v8/v8/blob/8851bf83a938f79ab522b829261157396d9988aa/src/v8natives.js#L276-L289

This is pretty much also what Object.defineProperty does https://github.com/v8/v8/blob/8851bf83a938f79ab522b829261157396d9988aa/src/v8natives.js#L1170 (except for when proxies are involved but this is not the case here).

@micnic
Copy link
Contributor

micnic commented Feb 9, 2015

@benjamingr , you are right, then it's definitely a v8 issue.

@greim , it would be great if you could reproduce this bug with a smaller (and open) input for source.js and fill an issue in V8's issue tracker

@bnoordhuis
Copy link
Member

As pointed out by others, this is essentially a V8 issue. I'm curious though, does passing --stack_size=4096 on the command line make a difference?

@greim
Copy link
Author

greim commented Feb 9, 2015

Here's code that reproduces it for me:

var patt = /(?:\/\/|\/\*)[@#][ \t]+sourceMappingURL=data:(?:application|text)\/json;base64,((?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?)(?:[ \t]*\*\/)?$/mg;
var source = '//# sourceMappingURL=data:application/json;base64,'
var len = 4473955
while (source.length < len){
  source += 'ICAgICAgICApXG4gICAgICAgIClcbiAgICAgIClcbi'
}
source = source.substring(0,len)
patt.test(source)

I narrowed it doen to len = 4473955. One number lower (4473954) wouldn't trigger the error. The weird thing is that this does seem to trigger range error in 0.11 and 0.12, so I'll need to work a bit more to figure out what the difference is; I might have some error in my reasoning or test code.

Error output:

$ node test.js
/Users/greim/deleteme/test.js:5
  source += 'ICAgICAgICApXG4gICAgICAgIClcbiAgICAgIClcbi'
          ^
RangeError: Maximum call stack size exceeded
    at RegExp.test (native)
    at Object.<anonymous> (/Users/greim/deleteme/test.js:8:6)
    at Module._compile (module.js:446:26)
    at Object.Module._extensions..js (module.js:464:10)
    at Module.load (module.js:341:32)
    at Function.Module._load (module.js:296:12)
    at Function.Module.runMain (module.js:487:10)
    at startup (node.js:111:16)
    at node.js:799:3

[edit] It's also strange that the above error output shows the += line as where the error occurred. Inserting log statements definitely shows it getting past that part.

@lidlanca
Copy link

lidlanca commented Feb 9, 2015

@bnoordhuis

I tried --stack_size=4096 on a windows machine, from what I understand it has no affact on windows platform as the stack can not be changed programmatically beyond the default limits at linkage.

In visual studio, I added the /F 4096
https://msdn.microsoft.com/en-us/library/tdkhxaks.aspx

And I was able to run the replication code @greim provided without --stack_size=4096 in the command line. and no stack error.

@greim
Copy link
Author

greim commented Feb 10, 2015

--stack_size=4096 doesn't make a difference for me.

I did notice that munging the regex ever so slightly in order to prevent a match makes the error go away. In other words changing this:

/(?:\/\/|\/\*)[@#][ \t]+sourceMappingURL=data:...

...to this:

/(?:\/\/|\/\*)[@#][ \t]+siurceMappingURL=data:...

(BTW I'm on OS X 10.10.2)

@lidlanca
Copy link

@greim

What is your system stack size limit.
I think you can check it with
ulimit -a

@targos
Copy link
Member

targos commented Feb 10, 2015

The error also happens on Chrome. It is definitely a v8 issue.

@benjamingr
Copy link
Member

@targos that settles it then. V8 has also accepted the bug.

@greim
Copy link
Author

greim commented Feb 10, 2015

Stack size is 8192. In the v8 bug it was pointed out this is due to regex backtracking and isn't really a bug, although I think the error reporting could be a little clearer. In any case I'll close this later today unless someone disagrees or thinks there's a reason to keep it open. Or if someone else closes it that's fine.

@Fishrock123 Fishrock123 removed the confirmed-bug Issues with confirmed bugs. label Feb 10, 2015
@thlorenz
Copy link
Contributor

This is definitely a regression since the exact same scenario works with nodes that use older v8 versions.

I'd like to introduce the v8 label for similar issues @mikeal but can't myself do so. This label will help the v8 team find issues they can help us with.

I feel we need to stay on top of these regressions as code that ran fine with previous node versions now suddenly and now suddenly doesn't work anymore is not going to benefit io.js adoption.

@beanieboi @targos are you going to stay on top of this bug and make sure that it gets solved in v8 and/or io.js users are aware of this issue until it gets fixed?

@lidlanca
Copy link

@thlorenz
The same code fail on node v0.10.34 for me.

As noted in the v8 issue tracker, this is as designed.
Increasing the irregexp stack size will allow linear growth of the source string(in the replication code).

//v8/src/regexp-stack.h
static const size_t kMaximumStackSize = 128 * MB;  //default 64

There are some inconsistencies between versions and sometimes the same process, more likely due to working on the limit of the stack dynamic allocation. and some external factors.

@thlorenz thlorenz added the v8 engine Issues and PRs related to the V8 dependency. label Feb 11, 2015
@thlorenz
Copy link
Contributor

@lidlanca thanks for pointing that out, I wasn't aware.

According to the issue @greim filed on my module it did work with previous/other node versions.

only see this on io.js, not on node 0.11 or o.12

@greim
Copy link
Author

greim commented Feb 13, 2015

The error I was originally seeing goes away with this slight modification to the regex:

var works = /^[ \t]*\/(?:\/|\*)[@#][ \t]+sourceMappingURL=data:(?:application|text)\/json;base64,(..*)$/mg
var fails = /^[ \t]*\/(?:\/|\*)[@#][ \t]+sourceMappingURL=data:(?:application|text)\/json;base64,(.+)$/mg

Anybody with strong regex foo know why the one would cause a RangeError but not the other?

@bnoordhuis
Copy link
Member

Those regular expressions behave really strangely...

$ out/Release/iojs
> var works = /^[ \t]*\/(?:\/|\*)[@#][ \t]+sourceMappingURL=data:(?:application|text)\/json;base64,(..*)$/mg
undefined
> var fails = /^[ \t]*\/(?:\/|\*)[@#][ \t]+sourceMappingURL=data:(?:application|text)\/json;base64,(.+)$/mg
undefined
> var s = '//@ sourceMappingURL=data:text/json;base64,xyzzy'
undefined
> Array(11).join('.').split('').map(function() { return works.test(s) })
[ false, true, false, true, false, true, false, true, false, true ]
> Array(11).join('.').split('').map(function() { return fails.test(s) })
[ false, true, false, true, false, true, false, true, false, true ]

@targos
Copy link
Member

targos commented Feb 13, 2015

@bnoordhuis it is apparently because of the g flag. Remove it and it will always return true. I don't understand why though...

Btw : Array(11).join('.') === '.'.repeat(10)

@benjamingr
Copy link
Member

@targos regular expressions need a lot of backtracking to perform matches and a call stack size exceeded error should not be too surprising.

@greim this has nothing to do with being strong with regex - ..* and .+ are pretty much identical - the only difference is implementation detail in v8 that caused this regression.

This is pretty much all explained in the issue tracker issue which raises an interesting issue -@thlorenz what is the policy on v8 won't-fix issues?

@thlorenz
Copy link
Contributor

what is the policy on v8 won't-fix issues?

I'm not the right person to ask as I honestly don't know.

@greim
Copy link
Author

greim commented Feb 13, 2015

Also, I can't reproduce this particular error in isolation, using the same regex on the same string. Even if I fill most available memory with other stuff.

for (var i=0, amount=80000000, arr=Array(amount); i<amount; i++) arr[i] = i
var source = require('fs').readFileSync('./source.js', 'utf8')
source.match(/(?:\/\/|\/\*)[@#][ \t]+sourceMappingURL=data:(?:application|text)\/json;base64,(.+)$/mg)

Using a number much higher than 80000000 causes the process to fail due to out of memory, but never a RangeError.

Anyway, if this is just risk of doing business with v8 regex that's fine, I don't want to keep spamming this thread. Mainly wanted to rule out some bug in newer v8 that might make life hard for iojs adopters.

@greim
Copy link
Author

greim commented Feb 13, 2015

@greim
Copy link
Author

greim commented Apr 23, 2015

Update: As mentioned previously, I couldn't reproduce this in isolation; it would only occur during my gulp build.

Upon further investigation, commenting out some seemingly unrelated code in my gulp build which was using marked to process markdown, prevents the error. marked makes heavy use of regex. It's as if something in marked was changing the state of the regex engine.

Unfortunately I still can't reproduce in isolation, but I'm removing things from the gulp build to try to narrow it down.

dmitry added a commit to dmitry/karma-sourcemap-loader that referenced this issue Jun 30, 2015
On io.js 2.2.1 and 2.3.1 (by the time of writing it's the latest) I have this error:

```
RangeError: Maximum call stack size exceeded
  at String.match (native)
```

It happens only on the second pass while karma is working (auto watching and reruns the specs).

For more details read this issue:

nodejs/node#759

Similar issues:

thlorenz/convert-source-map#10
thlorenz/convert-source-map#11

Looks like this related to optimizations in V8 regex engine:
http://blog.chromium.org/2009/02/irregexp-google-chromes-new-regexp.html

Simply changing from `.+` to `..*` equivalent rule fixes an issue with match function.
dmitry added a commit to dmitry/karma-sourcemap-loader that referenced this issue Jun 30, 2015
On io.js 2.2.1 and 2.3.1 (by the time of writing it's the latest) I have this error:

```
RangeError: Maximum call stack size exceeded
  at String.match (native)
```

It happens only on the second pass while karma is working (auto watching and reruns the specs).

For more details read this issue:

nodejs/node#759

Similar issues:

thlorenz/convert-source-map#10
thlorenz/convert-source-map#11

Looks like this related to optimizations in V8 regex engine:
http://blog.chromium.org/2009/02/irregexp-google-chromes-new-regexp.html

Simply changing from `.+` to `..*` equivalent rule fixes an issue with match function.
elliottsj pushed a commit to EventMobi/karma-sourcemap-loader that referenced this issue Oct 8, 2015
On io.js 2.2.1 and 2.3.1 (by the time of writing it's the latest) I have this error:

```
RangeError: Maximum call stack size exceeded
  at String.match (native)
```

It happens only on the second pass while karma is working (auto watching and reruns the specs).

For more details read this issue:

nodejs/node#759

Similar issues:

thlorenz/convert-source-map#10
thlorenz/convert-source-map#11

Looks like this related to optimizations in V8 regex engine:
http://blog.chromium.org/2009/02/irregexp-google-chromes-new-regexp.html

Simply changing from `.+` to `..*` equivalent rule fixes an issue with match function.
bbbush added a commit to bbbush/gulp-sourcemaps that referenced this issue Dec 11, 2015
bbbush added a commit to bbbush/gulp-sourcemaps that referenced this issue Dec 11, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
v8 engine Issues and PRs related to the V8 dependency.
Projects
None yet
Development

No branches or pull requests

10 participants