Prevent fatal error in CFX_V8::PutObjectProperty()
If there is a user-supplied setter that throws, then
PutObjectProperty() will call v8::FromJust() on a Nothing, and
#
# Fatal error in v8::FromJust
# Maybe value is Nothing.
#
results.
- Make CFX_V8 return bool values as success indicators.
- Cover some empty object cases in cfx_v8_unittests.cpp
- Use a local to avoid clunky formatting.
Change-Id: I4ef5f38db4a1ad74491349211dd3b3c6a745bbef
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/62970
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/fxjs/cfx_v8.cpp b/fxjs/cfx_v8.cpp
index 59157fe..72bf361 100644
--- a/fxjs/cfx_v8.cpp
+++ b/fxjs/cfx_v8.cpp
@@ -43,15 +43,15 @@
return result;
}
-void CFX_V8::PutObjectProperty(v8::Local<v8::Object> pObj,
+bool CFX_V8::PutObjectProperty(v8::Local<v8::Object> pObj,
ByteStringView bsUTF8PropertyName,
v8::Local<v8::Value> pPut) {
ASSERT(!pPut.IsEmpty());
if (pObj.IsEmpty())
- return;
- pObj->Set(m_pIsolate->GetCurrentContext(), NewString(bsUTF8PropertyName),
- pPut)
- .FromJust();
+ return false;
+
+ v8::Local<v8::String> name = NewString(bsUTF8PropertyName);
+ return pObj->Set(m_pIsolate->GetCurrentContext(), name, pPut).IsJust();
}
void CFX_V8::DisposeIsolate() {
@@ -67,15 +67,13 @@
return v8::Object::New(GetIsolate());
}
-unsigned CFX_V8::PutArrayElement(v8::Local<v8::Array> pArray,
- unsigned index,
- v8::Local<v8::Value> pValue) {
+bool CFX_V8::PutArrayElement(v8::Local<v8::Array> pArray,
+ unsigned index,
+ v8::Local<v8::Value> pValue) {
ASSERT(!pValue.IsEmpty());
if (pArray.IsEmpty())
- return 0;
- if (pArray->Set(m_pIsolate->GetCurrentContext(), index, pValue).IsNothing())
- return 0;
- return 1;
+ return false;
+ return pArray->Set(m_pIsolate->GetCurrentContext(), index, pValue).IsJust();
}
v8::Local<v8::Value> CFX_V8::GetArrayElement(v8::Local<v8::Array> pArray,
diff --git a/fxjs/cfx_v8.h b/fxjs/cfx_v8.h
index 91a6851..cb152ac 100644
--- a/fxjs/cfx_v8.h
+++ b/fxjs/cfx_v8.h
@@ -44,15 +44,15 @@
unsigned GetArrayLength(v8::Local<v8::Array> pArray);
v8::Local<v8::Value> GetArrayElement(v8::Local<v8::Array> pArray,
unsigned index);
- unsigned PutArrayElement(v8::Local<v8::Array> pArray,
- unsigned index,
- v8::Local<v8::Value> pValue);
+ bool PutArrayElement(v8::Local<v8::Array> pArray,
+ unsigned index,
+ v8::Local<v8::Value> pValue);
// Objects.
std::vector<WideString> GetObjectPropertyNames(v8::Local<v8::Object> pObj);
v8::Local<v8::Value> GetObjectProperty(v8::Local<v8::Object> pObj,
ByteStringView bsUTF8PropertyName);
- void PutObjectProperty(v8::Local<v8::Object> pObj,
+ bool PutObjectProperty(v8::Local<v8::Object> pObj,
ByteStringView bsUTF8PropertyName,
v8::Local<v8::Value> pValue);
diff --git a/fxjs/cfx_v8_unittest.cpp b/fxjs/cfx_v8_unittest.cpp
index 54f4d92..7b9ecb5 100644
--- a/fxjs/cfx_v8_unittest.cpp
+++ b/fxjs/cfx_v8_unittest.cpp
@@ -10,6 +10,11 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/base/ptr_util.h"
+namespace {
+bool getter_sentinel = false;
+bool setter_sentinel = false;
+} // namespace
+
void FXV8UnitTest::V8IsolateDeleter::operator()(v8::Isolate* ptr) const {
ptr->Dispose();
}
@@ -41,6 +46,19 @@
EXPECT_EQ(L"", cfx_v8()->ToWideString(empty));
EXPECT_TRUE(cfx_v8()->ToObject(empty).IsEmpty());
EXPECT_TRUE(cfx_v8()->ToArray(empty).IsEmpty());
+
+ // Can't set properties on empty objects, but does not fault.
+ v8::Local<v8::Value> marker = cfx_v8()->NewNumber(2);
+ v8::Local<v8::Object> empty_object;
+ EXPECT_FALSE(cfx_v8()->PutObjectProperty(empty_object, "clams", marker));
+ EXPECT_TRUE(cfx_v8()->GetObjectProperty(empty_object, "clams").IsEmpty());
+ EXPECT_EQ(0u, cfx_v8()->GetObjectPropertyNames(empty_object).size());
+
+ // Can't set elements in empty arrays, but does not fault.
+ v8::Local<v8::Array> empty_array;
+ EXPECT_FALSE(cfx_v8()->PutArrayElement(empty_array, 0, marker));
+ EXPECT_TRUE(cfx_v8()->GetArrayElement(empty_array, 0).IsEmpty());
+ EXPECT_EQ(0u, cfx_v8()->GetArrayLength(empty_array));
}
TEST_F(FXV8UnitTest, NewNull) {
@@ -162,7 +180,7 @@
EXPECT_TRUE(cfx_v8()->GetArrayElement(array, 2)->IsUndefined());
EXPECT_EQ(0u, cfx_v8()->GetArrayLength(array));
- cfx_v8()->PutArrayElement(array, 3, cfx_v8()->NewNumber(12));
+ EXPECT_TRUE(cfx_v8()->PutArrayElement(array, 3, cfx_v8()->NewNumber(12)));
EXPECT_FALSE(cfx_v8()->GetArrayElement(array, 2).IsEmpty());
EXPECT_TRUE(cfx_v8()->GetArrayElement(array, 2)->IsUndefined());
EXPECT_FALSE(cfx_v8()->GetArrayElement(array, 3).IsEmpty());
@@ -190,7 +208,8 @@
EXPECT_TRUE(cfx_v8()->GetObjectProperty(object, "clams")->IsUndefined());
EXPECT_EQ(0u, cfx_v8()->GetObjectPropertyNames(object).size());
- cfx_v8()->PutObjectProperty(object, "clams", cfx_v8()->NewNumber(12));
+ EXPECT_TRUE(
+ cfx_v8()->PutObjectProperty(object, "clams", cfx_v8()->NewNumber(12)));
EXPECT_FALSE(cfx_v8()->GetObjectProperty(object, "clams").IsEmpty());
EXPECT_TRUE(cfx_v8()->GetObjectProperty(object, "clams")->IsNumber());
EXPECT_EQ(1u, cfx_v8()->GetObjectPropertyNames(object).size());
@@ -204,3 +223,47 @@
EXPECT_TRUE(cfx_v8()->ToObject(object)->IsObject());
EXPECT_TRUE(cfx_v8()->ToArray(object).IsEmpty());
}
+
+TEST_F(FXV8UnitTest, ThrowFromGetter) {
+ v8::Isolate::Scope isolate_scope(isolate());
+ v8::HandleScope handle_scope(isolate());
+ v8::Local<v8::Context> context = v8::Context::New(isolate());
+ v8::Context::Scope context_scope(context);
+
+ v8::Local<v8::Object> object = cfx_v8()->NewObject();
+ v8::Local<v8::String> name = cfx_v8()->NewString("clams");
+ EXPECT_TRUE(
+ object
+ ->SetAccessor(context, name,
+ [](v8::Local<v8::Name> property,
+ const v8::PropertyCallbackInfo<v8::Value>& info) {
+ getter_sentinel = true;
+ info.GetIsolate()->ThrowException(property);
+ })
+ .FromJust());
+ getter_sentinel = false;
+ EXPECT_TRUE(cfx_v8()->GetObjectProperty(object, "clams").IsEmpty());
+ EXPECT_TRUE(getter_sentinel);
+}
+
+TEST_F(FXV8UnitTest, ThrowFromSetter) {
+ v8::Isolate::Scope isolate_scope(isolate());
+ v8::HandleScope handle_scope(isolate());
+ v8::Local<v8::Context> context = v8::Context::New(isolate());
+ v8::Context::Scope context_scope(context);
+
+ v8::Local<v8::Object> object = cfx_v8()->NewObject();
+ v8::Local<v8::String> name = cfx_v8()->NewString("clams");
+ EXPECT_TRUE(object
+ ->SetAccessor(context, name, nullptr,
+ [](v8::Local<v8::Name> property,
+ v8::Local<v8::Value> value,
+ const v8::PropertyCallbackInfo<void>& info) {
+ setter_sentinel = true;
+ info.GetIsolate()->ThrowException(property);
+ })
+ .FromJust());
+ setter_sentinel = false;
+ EXPECT_FALSE(cfx_v8()->PutObjectProperty(object, "clams", name));
+ EXPECT_TRUE(setter_sentinel);
+}