Skip to content

Interrupt handler not interrupting during array + string #219

@tom288

Description

@tom288

Thanks for building and maintaining quickjs-emscripten. I'm interested in using it to safely run user-defined logic on my server. I've come across a case where one user can make the vm take a very long time to run out of memory, and yet the interrupt callback never runs:

import { getQuickJS, Scope } from "quickjs-emscripten"

const qjs = await getQuickJS()
const scope = new Scope()
const runtime = scope.manage(qjs.newRuntime())
const vm = scope.manage(
    runtime.newContext()
)
runtime.setInterruptHandler(() => true) // Always interrupt
runtime.setMemoryLimit(1024 * 640)
runtime.setMaxStackSize(1024 * 320)

// "Warm up" the vm - this DOES get interrupted, but only during the first run
try {
    vm.unwrapResult(vm.evalCode("{}")).dispose()
} catch {}

// Do something slow
try {
    // Make a large array and convert it to a string
    // The array is huge so this could take 10-30s
    vm.unwrapResult(vm.evalCode("new Array(1_000_000_000) + ''\nundefined")).consume(vm.dump) // <--
} catch(e) {
    // Eventually I get InternalError: out of memory, but not soon enough!
    console.error(`${e}`)
}

scope.dispose()

This is a big issue because one user can affect the availability of the server for other users. I'd like to get the interrupt callback to run during my evalCode call, and would happily sacrifice performance to do so. Is this possible?

I've seen multiple places state that the interrupt handler runs every 1000 or so "instructions" or "cycles", is this 20s long operation really a single cycle?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions