4

I am using Google's v8 engine for embedding javascript in my application. At certain times, I will be invoking user-supplied code, and I want to ensure that it does not behave badly by allocating too much memory. Currently, when the javascript tries to make or resize an array to be too large, for example, I get an unceremonious message:

#
# Fatal error in CALL_AND_RETRY_LAST
# Allocation failed - process out of memory
#

And then entire process crashes with a SIGILL. Obviously, this is not acceptable. I require the ability to run user-supplied code, however... and it is not feasible to manually vet all code before it is executed by the engine.

What I would ideally like to do in this case is simply terminate the isolate that was consuming too much memory (without affecting any other isolates that may be running). Is there any way to designate a maximum amount of memory that a js program is allowed to use before it fails, and so if it exceeds that limit, instead of crashing the process, the invocation of the Run or Call commands would simply return an error or set some status flag indicating that it was abnormally terminated.

Things I have tried so far:

Setting a custom array_buffer allocator when the isolate is created, which tracks how much memory is being used and terminates the isolate when the memory usage gets too high> The Allocate function of my allocator does not ever get called.

Calling AddMemoryAllocationCallback with a function that tracks memory usage and tries to terminate the isolate via TerminateExecution() when the allocations exceeds a certain amount. This function does get called, but I get an out of memory error after this function only has reported a couple of megabytes being used, while I know for a fact that the data being created by the bad behaving v8 function is FAR larger than that.

Setting a fatal error handler via SetFatalErrorHandler and trying to invoke TerminateExecution there. This function does get called, but it does not prevent the process from crashing.

Is there anything else I can try?

markt1964
  • 2,638
  • 2
  • 22
  • 54

1 Answers1

3

Edit: authoritative response from the V8 team -- you can't. But they'll accept a patch.

v8::Isolate::SetFatalErrorHandler() should allow you to not crash. But, my understanding is that the isolate is still unusable after the fact. Probably no way of getting around that, since the isolate will be left in an unrecoverable statea.

http://v8.paulfryzel.com/docs/master/classv8_1_1_isolate.html#a131f1e2e6a80618ac3c8c266a041851d

(maybe. there seems to be a lot of stuff going on about this in the 2013-2014 timeframe where people at google said the right thing to do was to just let v8 kill the process -- to which a lot of people thought was dumb. I don't see any resolution)

edit: Mailing list response was that you cannot do this. They will accept a patch if it doesn't have a performance impact.

edit: There was just another thread on this and someone posted what seems to be a pretty good way to avoid OOM in non-malicious situations:

https://groups.google.com/forum/#!topic/v8-users/vKn1hVs8KNQ

I set the heap limit to 8x the limit I actually want. Then, after each call into the isolate, I check if the memory usage has gone over the intended limit. If so, I invoke garbage collection. If it's still over the limit after that, then I terminate the isolate at that point.

Meanwhile, we also enforce a CPU time limit of 50ms. In practice, a script that allocates tons of memory tends to run out of CPU time before it can hit the 8x heap limit (especially as the GC slows things down when approaching the limit).

xaxxon
  • 19,189
  • 5
  • 50
  • 80
  • Thank you, although I just tried this suggestion, and unfortunately, it did not work. I have updated the description above with the things that I have tried so far. – markt1964 Sep 22 '16 at 16:12
  • 1
    Chrome is multi-process, so they don't have to worry about javascript crashing a process.. since that's only for that one tab and it would be invalid anyhow. – xaxxon Sep 23 '16 at 02:28
  • 1
    Well... that's a bit of a b!tch... it means that v8 may not be the best choice for a js engine. – markt1964 Sep 23 '16 at 05:47
  • 1
    I would suggest posting to v8-users mailing list. https://groups.google.com/forum/#!forum/v8-users if you don't I may. What I read said that it would be difficult (lots of work, not necessarily technically difficult) to detect whether there is a "real" out of memory situation or a "artificial" (meaning exceedign some user-defined bound) out of memory situation and behave differently in the two situations. – xaxxon Sep 23 '16 at 06:28
  • Wow... so it looks like the only thing that v8 is good for is running javascipr inside of a separate process, why even bother having isolates then? It's a bizarre restriction because the OS is certainly capable of terminating the process if it consumes too much memory anyways, and a process can always artificially lower its own resource limits below what the OS allows it to. I cannot for the life of me fathom why the response to what is detected as an OOM situation by v8 would not be simply responded to with an invocation of TerminateExcecution on the offending Isolate. – markt1964 Sep 27 '16 at 16:27
  • 1
    it was built to support chrome. it works well for chrome. – xaxxon Sep 27 '16 at 20:21
  • Separate processes for each thread that needs js is simply infeasible for me because I would require a lot of communication between them and my main program instead of simply issuing function calls from custom native js functions running in different threads that are running javascript, each with their own isolate. I initially chose to use v8 in my project because it seemed to have what I needed, but I am only discovering some two years later or so that it is not. It's going to be painful changing to a new js engine now, but not as painful as dealing with a program that an end-user can crash. – markt1964 Oct 06 '16 at 17:07