Commit 80d7ce6e authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[bigint] Add interrupt checks to slow operations

Multiplication, division, and toString can take a very long
time for large inputs. This patch adds stack checks to each
of these operations so embedders can interrupt them.

Bug: chromium:922032
Change-Id: Idae9d32d6f78a028de4d2ba3abdb79c624f0dca1
Reviewed-on: https://chromium-review.googlesource.com/c/1444913
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59262}
parent 70c94dfa
......@@ -479,8 +479,23 @@ MaybeHandle<BigInt> BigInt::Multiply(Isolate* isolate, Handle<BigInt> x,
return MaybeHandle<BigInt>();
}
result->InitializeDigits(result_length);
uintptr_t work_estimate = 0;
for (int i = 0; i < x->length(); i++) {
MutableBigInt::MultiplyAccumulate(y, x->digit(i), result, i);
// Multiplication can take a long time. Check for interrupt requests
// every now and then (roughly every 10-20 of milliseconds -- rarely
// enough not to create noticeable overhead, frequently enough not to
// appear frozen).
work_estimate += y->length();
if (work_estimate > 5000000) {
work_estimate = 0;
StackLimitCheck interrupt_check(isolate);
if (interrupt_check.InterruptRequested() &&
isolate->stack_guard()->HandleInterrupts()->IsException(isolate)) {
return MaybeHandle<BigInt>();
}
}
}
result->set_sign(x->sign() != y->sign());
return MutableBigInt::MakeImmutable(result);
......@@ -1525,6 +1540,7 @@ bool MutableBigInt::AbsoluteDivLarge(Isolate* isolate,
// Iterate over the dividend's digit (like the "grad school" algorithm).
// {vn1} is the divisor's most significant digit.
digit_t vn1 = divisor->digit(n - 1);
uintptr_t work_estimate = 0;
for (int j = m; j >= 0; j--) {
// D3.
// Estimate the current iteration's quotient digit (see Knuth for details).
......@@ -1568,6 +1584,20 @@ bool MutableBigInt::AbsoluteDivLarge(Isolate* isolate,
}
if (quotient != nullptr) q->set_digit(j, qhat);
// Division can take a long time. Check for interrupt requests every
// now and then (roughly every 10-20 of milliseconds -- rarely enough
// not to create noticeable overhead, frequently enough not to appear
// frozen).
work_estimate += n;
if (work_estimate > 5000000) {
work_estimate = 0;
StackLimitCheck interrupt_check(isolate);
if (interrupt_check.InterruptRequested() &&
isolate->stack_guard()->HandleInterrupts()->IsException(isolate)) {
return false;
}
}
}
if (quotient != nullptr) {
*quotient = q; // Caller will right-trim.
......@@ -2080,6 +2110,7 @@ MaybeHandle<String> MutableBigInt::ToStringGeneric(Isolate* isolate,
// In the first round, divide the input, allocating a new BigInt for
// the result == rest; from then on divide the rest in-place.
Handle<BigIntBase>* dividend = &x;
uintptr_t work_estimate = 0;
do {
digit_t chunk;
AbsoluteDivSmall(isolate, *dividend, chunk_divisor, &rest, &chunk);
......@@ -2096,6 +2127,32 @@ MaybeHandle<String> MutableBigInt::ToStringGeneric(Isolate* isolate,
// We can never clear more than one digit per iteration, because
// chunk_divisor is smaller than max digit value.
DCHECK_GT(rest->digit(nonzero_digit), 0);
// String formatting can take a long time. Check for interrupt requests
// every now and then (roughly every 10-20 of milliseconds -- rarely
// enough not to create noticeable overhead, frequently enough not to
// appear frozen).
work_estimate += length;
if (work_estimate > 500000) {
work_estimate = 0;
StackLimitCheck interrupt_check(isolate);
if (interrupt_check.InterruptRequested()) {
{
AllowHeapAllocation might_throw;
if (isolate->stack_guard()->HandleInterrupts()->IsException(
isolate)) {
return MaybeHandle<String>();
}
}
// If there was an interrupt request but no termination, reload
// the raw characters pointer (as the string might have moved).
chars = result->GetChars(no_gc);
}
if (interrupt_check.InterruptRequested() &&
isolate->stack_guard()->HandleInterrupts()->IsException(isolate)) {
return MaybeHandle<String>();
}
}
} while (nonzero_digit > 0);
last_digit = rest->digit(0);
}
......
......@@ -176,10 +176,7 @@ class TerminatorThread : public v8::base::Thread {
v8::Isolate* isolate_;
};
// Test that a single thread of JavaScript execution can be terminated
// from the side by another thread.
TEST(TerminateOnlyV8ThreadFromOtherThread) {
void TestTerminatingSlowOperation(const char* source) {
semaphore = new v8::base::Semaphore(0);
TerminatorThread thread(CcTest::i_isolate());
thread.Start();
......@@ -191,40 +188,55 @@ TEST(TerminateOnlyV8ThreadFromOtherThread) {
v8::Context::New(CcTest::isolate(), nullptr, global);
v8::Context::Scope context_scope(context);
CHECK(!CcTest::isolate()->IsExecutionTerminating());
// Run a loop that will be infinite if thread termination does not work.
v8::MaybeLocal<v8::Value> result =
CompileRun(CcTest::isolate()->GetCurrentContext(),
"try { loop(); fail(); } catch(e) { fail(); }");
CompileRun(CcTest::isolate()->GetCurrentContext(), source);
CHECK(result.IsEmpty());
thread.Join();
delete semaphore;
semaphore = nullptr;
}
// Test that a single thread of JavaScript execution can be terminated
// from the side by another thread.
TEST(TerminateOnlyV8ThreadFromOtherThread) {
// Run a loop that will be infinite if thread termination does not work.
TestTerminatingSlowOperation("try { loop(); fail(); } catch(e) { fail(); }");
}
// Test that execution can be terminated from within JSON.stringify.
TEST(TerminateJsonStringify) {
semaphore = new v8::base::Semaphore(0);
TerminatorThread thread(CcTest::i_isolate());
thread.Start();
v8::HandleScope scope(CcTest::isolate());
v8::Local<v8::ObjectTemplate> global =
CreateGlobalTemplate(CcTest::isolate(), Signal, DoLoop);
v8::Local<v8::Context> context =
v8::Context::New(CcTest::isolate(), nullptr, global);
v8::Context::Scope context_scope(context);
CHECK(!CcTest::isolate()->IsExecutionTerminating());
v8::MaybeLocal<v8::Value> result =
CompileRun(CcTest::isolate()->GetCurrentContext(),
"var x = [];"
"x[2**31]=1;"
"terminate();"
"JSON.stringify(x);"
"fail();");
CHECK(result.IsEmpty());
thread.Join();
delete semaphore;
semaphore = nullptr;
TestTerminatingSlowOperation(
"var x = [];"
"x[2**31]=1;"
"terminate();"
"JSON.stringify(x);"
"fail();");
}
TEST(TerminateBigIntMultiplication) {
TestTerminatingSlowOperation(
"terminate();"
"var a = 5n ** 555555n;"
"var b = 3n ** 3333333n;"
"a * b;"
"fail();");
}
TEST(TerminateBigIntDivision) {
TestTerminatingSlowOperation(
"var a = 2n ** 2222222n;"
"var b = 3n ** 333333n;"
"terminate();"
"a / b;"
"fail();");
}
TEST(TerminateBigIntToString) {
TestTerminatingSlowOperation(
"var a = 2n ** 2222222n;"
"terminate();"
"a.toString();"
"fail();");
}
int call_count = 0;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment