summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Sepez <tsepez@chromium.org>2017-02-24 15:31:12 -0800
committerChromium commit bot <commit-bot@chromium.org>2017-02-27 18:57:39 +0000
commitc5a1472ec5a809ff524c8888ac5884498801d06f (patch)
tree0097a8358dd5954fb48f4b2a48ea921a22a300ca
parent73c9f3bb3d82563d6d4496c4b0204d5c0825e8a2 (diff)
downloadpdfium-c5a1472ec5a809ff524c8888ac5884498801d06f.tar.xz
Fix uninitialized memory read in CJS_Object::GetEmbedObject()
The expected way to create native PDFium objects for JS is via the NewFxDynamicObject() call in C++, but that doesn't mean that the corresponding constructors won't be called from JS. In that case, the internal fields will be uninitialized, and subsequent method calls may try to use them. Add a constructor callback for all PDFium objects that nulls out these fields (shame that v8 doesn't do this by default, but probably saves some cycles). Then ensure that we check for this possibility in all the places it might turn up. Conversely, if we've just gotten a successful return from NewFxDynamicObject(), we know the CJS_Object/EmbedObj are good, so avoid checking there. BUG=695826 Change-Id: Iadad644c4af937def967ddc83daac1dad7544d69 Reviewed-on: https://pdfium-review.googlesource.com/2839 Reviewed-by: dsinclair <dsinclair@chromium.org> Commit-Queue: Tom Sepez <tsepez@chromium.org>
-rw-r--r--fpdfsdk/javascript/Document.cpp32
-rw-r--r--fpdfsdk/javascript/Field.cpp6
-rw-r--r--fpdfsdk/javascript/JS_Define.h82
-rw-r--r--fpdfsdk/javascript/JS_EventHandler.cpp30
-rw-r--r--fpdfsdk/javascript/app.cpp11
-rw-r--r--fxjs/fxjs_v8.cpp6
-rw-r--r--testing/resources/javascript/bug_695826.in47
-rw-r--r--testing/resources/javascript/bug_695826_expected.txt1
8 files changed, 140 insertions, 75 deletions
diff --git a/fpdfsdk/javascript/Document.cpp b/fpdfsdk/javascript/Document.cpp
index 536d654463..6928a061a2 100644
--- a/fpdfsdk/javascript/Document.cpp
+++ b/fpdfsdk/javascript/Document.cpp
@@ -299,11 +299,13 @@ bool Document::getField(CJS_Runtime* pRuntime,
v8::Local<v8::Object> pFieldObj =
pRuntime->NewFxDynamicObj(CJS_Field::g_nObjDefnID);
+ if (pFieldObj.IsEmpty())
+ return false;
+
CJS_Field* pJSField =
static_cast<CJS_Field*>(pRuntime->GetObjectPrivate(pFieldObj));
Field* pField = static_cast<Field*>(pJSField->GetEmbedObject());
pField->AttachField(this, wideName);
-
vRet = CJS_Value(pRuntime, pJSField);
return true;
}
@@ -1111,13 +1113,7 @@ bool Document::getAnnot(CJS_Runtime* pRuntime,
CJS_Annot* pJS_Annot =
static_cast<CJS_Annot*>(pRuntime->GetObjectPrivate(pObj));
- if (!pJS_Annot)
- return false;
-
Annot* pAnnot = static_cast<Annot*>(pJS_Annot->GetEmbedObject());
- if (!pAnnot)
- return false;
-
pAnnot->SetSDKAnnot(pSDKBAAnnot);
vRet = CJS_Value(pRuntime, pJS_Annot);
return true;
@@ -1155,13 +1151,7 @@ bool Document::getAnnots(CJS_Runtime* pRuntime,
CJS_Annot* pJS_Annot =
static_cast<CJS_Annot*>(pRuntime->GetObjectPrivate(pObj));
- if (!pJS_Annot)
- return false;
-
Annot* pAnnot = static_cast<Annot*>(pJS_Annot->GetEmbedObject());
- if (!pAnnot)
- return false;
-
pAnnot->SetSDKAnnot(static_cast<CPDFSDK_BAAnnot*>(pSDKAnnotCur.Get()));
annots.SetElement(pRuntime, i, CJS_Value(pRuntime, pJS_Annot));
}
@@ -1258,13 +1248,7 @@ bool Document::icons(CJS_Runtime* pRuntime,
CJS_Icon* pJS_Icon =
static_cast<CJS_Icon*>(pRuntime->GetObjectPrivate(pObj));
- if (!pJS_Icon)
- return false;
-
Icon* pIcon = static_cast<Icon*>(pJS_Icon->GetEmbedObject());
- if (!pIcon)
- return false;
-
pIcon->SetIconName(pIconElement->IconName);
Icons.SetElement(pRuntime, i++, CJS_Value(pRuntime, pJS_Icon));
}
@@ -1297,13 +1281,7 @@ bool Document::getIcon(CJS_Runtime* pRuntime,
CJS_Icon* pJS_Icon =
static_cast<CJS_Icon*>(pRuntime->GetObjectPrivate(pObj));
- if (!pJS_Icon)
- return false;
-
- Icon* pIcon = (Icon*)pJS_Icon->GetEmbedObject();
- if (!pIcon)
- return false;
-
+ Icon* pIcon = static_cast<Icon*>(pJS_Icon->GetEmbedObject());
pIcon->SetIconName(swIconName);
vRet = CJS_Value(pRuntime, pJS_Icon);
return true;
@@ -1473,6 +1451,8 @@ bool Document::getPrintParams(CJS_Runtime* pRuntime,
CFX_WideString& sError) {
v8::Local<v8::Object> pRetObj =
pRuntime->NewFxDynamicObj(CJS_PrintParamsObj::g_nObjDefnID);
+ if (pRetObj.IsEmpty())
+ return false;
// Not implemented yet.
diff --git a/fpdfsdk/javascript/Field.cpp b/fpdfsdk/javascript/Field.cpp
index 92b473b231..f37b3d486b 100644
--- a/fpdfsdk/javascript/Field.cpp
+++ b/fpdfsdk/javascript/Field.cpp
@@ -2835,7 +2835,8 @@ bool Field::buttonGetIcon(CJS_Runtime* pRuntime,
v8::Local<v8::Object> pObj =
pRuntime->NewFxDynamicObj(CJS_Icon::g_nObjDefnID);
- ASSERT(pObj.IsEmpty() == false);
+ if (pObj.IsEmpty())
+ return false;
CJS_Icon* pJS_Icon = static_cast<CJS_Icon*>(pRuntime->GetObjectPrivate(pObj));
vRet = CJS_Value(pRuntime, pJS_Icon);
@@ -2966,7 +2967,8 @@ bool Field::getArray(CJS_Runtime* pRuntime,
for (const auto& pStr : swSort) {
v8::Local<v8::Object> pObj =
pRuntime->NewFxDynamicObj(CJS_Field::g_nObjDefnID);
- ASSERT(!pObj.IsEmpty());
+ if (pObj.IsEmpty())
+ return false;
CJS_Field* pJSField =
static_cast<CJS_Field*>(pRuntime->GetObjectPrivate(pObj));
diff --git a/fpdfsdk/javascript/JS_Define.h b/fpdfsdk/javascript/JS_Define.h
index ee7448c649..375ca3ac35 100644
--- a/fpdfsdk/javascript/JS_Define.h
+++ b/fpdfsdk/javascript/JS_Define.h
@@ -45,6 +45,8 @@ void JSPropGetter(const char* prop_name_string,
return;
CJS_Object* pJSObj =
static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
+ if (!pJSObj)
+ return;
C* pObj = reinterpret_cast<C*>(pJSObj->GetEmbedObject());
CFX_WideString sError;
CJS_PropValue value(pRuntime);
@@ -69,6 +71,8 @@ void JSPropSetter(const char* prop_name_string,
return;
CJS_Object* pJSObj =
static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
+ if (!pJSObj)
+ return;
C* pObj = reinterpret_cast<C*>(pJSObj->GetEmbedObject());
CFX_WideString sError;
CJS_PropValue propValue(pRuntime, CJS_Value(pRuntime, value));
@@ -111,6 +115,8 @@ void JSMethod(const char* method_name_string,
}
CJS_Object* pJSObj =
static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
+ if (!pJSObj)
+ return;
C* pObj = reinterpret_cast<C*>(pJSObj->GetEmbedObject());
CFX_WideString sError;
CJS_Value valueRes(pRuntime);
@@ -210,33 +216,31 @@ void JSMethod(const char* method_name_string,
static JSPropertySpec PropertySpecs[]; \
static JSMethodSpec MethodSpecs[];
-#define IMPLEMENT_JS_CLASS_RICH_PART(js_class_name, class_alternate, \
- class_name) \
- void js_class_name::JSConstructor(CFXJS_Engine* pEngine, \
- v8::Local<v8::Object> obj) { \
- CJS_Object* pObj = new js_class_name(obj); \
- pObj->SetEmbedObject(new class_alternate(pObj)); \
- pEngine->SetObjectPrivate(obj, (void*)pObj); \
- pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine)); \
- } \
- void js_class_name::JSDestructor(CFXJS_Engine* pEngine, \
- v8::Local<v8::Object> obj) { \
- js_class_name* pObj = \
- static_cast<js_class_name*>(pEngine->GetObjectPrivate(obj)); \
- delete pObj; \
- } \
- void js_class_name::DefineProps(CFXJS_Engine* pEngine) { \
- for (size_t i = 0; i < FX_ArraySize(PropertySpecs) - 1; ++i) { \
- pEngine->DefineObjProperty(g_nObjDefnID, PropertySpecs[i].pName, \
- PropertySpecs[i].pPropGet, \
- PropertySpecs[i].pPropPut); \
- } \
- } \
- void js_class_name::DefineMethods(CFXJS_Engine* pEngine) { \
- for (size_t i = 0; i < FX_ArraySize(MethodSpecs) - 1; ++i) { \
- pEngine->DefineObjMethod(g_nObjDefnID, MethodSpecs[i].pName, \
- MethodSpecs[i].pMethodCall); \
- } \
+#define IMPLEMENT_JS_CLASS_RICH_PART(js_class_name, class_alternate, \
+ class_name) \
+ void js_class_name::JSConstructor(CFXJS_Engine* pEngine, \
+ v8::Local<v8::Object> obj) { \
+ CJS_Object* pObj = new js_class_name(obj); \
+ pObj->SetEmbedObject(new class_alternate(pObj)); \
+ pEngine->SetObjectPrivate(obj, (void*)pObj); \
+ pObj->InitInstance(static_cast<CJS_Runtime*>(pEngine)); \
+ } \
+ void js_class_name::JSDestructor(CFXJS_Engine* pEngine, \
+ v8::Local<v8::Object> obj) { \
+ delete static_cast<js_class_name*>(pEngine->GetObjectPrivate(obj)); \
+ } \
+ void js_class_name::DefineProps(CFXJS_Engine* pEngine) { \
+ for (size_t i = 0; i < FX_ArraySize(PropertySpecs) - 1; ++i) { \
+ pEngine->DefineObjProperty(g_nObjDefnID, PropertySpecs[i].pName, \
+ PropertySpecs[i].pPropGet, \
+ PropertySpecs[i].pPropPut); \
+ } \
+ } \
+ void js_class_name::DefineMethods(CFXJS_Engine* pEngine) { \
+ for (size_t i = 0; i < FX_ArraySize(MethodSpecs) - 1; ++i) { \
+ pEngine->DefineObjMethod(g_nObjDefnID, MethodSpecs[i].pName, \
+ MethodSpecs[i].pMethodCall); \
+ } \
}
// Special JS classes implement methods, props, and queries, but not consts.
@@ -310,12 +314,18 @@ void JSSpecialPropQuery(const char*,
const v8::PropertyCallbackInfo<v8::Integer>& info) {
CJS_Runtime* pRuntime =
CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
- v8::String::Utf8Value utf8_value(property);
- CFX_WideString propname = CFX_WideString::FromUTF8(
- CFX_ByteStringC(*utf8_value, utf8_value.length()));
+ if (!pRuntime)
+ return;
+
CJS_Object* pJSObj =
static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
+ if (!pJSObj)
+ return;
+
Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
+ v8::String::Utf8Value utf8_value(property);
+ CFX_WideString propname = CFX_WideString::FromUTF8(
+ CFX_ByteStringC(*utf8_value, utf8_value.length()));
bool bRet = pObj->QueryProperty(propname.c_str());
info.GetReturnValue().Set(bRet ? 4 : 0);
}
@@ -328,8 +338,12 @@ void JSSpecialPropGet(const char* class_name,
CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
if (!pRuntime)
return;
+
CJS_Object* pJSObj =
static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
+ if (!pJSObj)
+ return;
+
Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
v8::String::Utf8Value utf8_value(property);
CFX_WideString propname = CFX_WideString::FromUTF8(
@@ -353,8 +367,12 @@ void JSSpecialPropPut(const char* class_name,
CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
if (!pRuntime)
return;
+
CJS_Object* pJSObj =
static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
+ if (!pJSObj)
+ return;
+
Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
v8::String::Utf8Value utf8_value(property);
CFX_WideString propname = CFX_WideString::FromUTF8(
@@ -375,8 +393,12 @@ void JSSpecialPropDel(const char* class_name,
CJS_Runtime::CurrentRuntimeFromIsolate(info.GetIsolate());
if (!pRuntime)
return;
+
CJS_Object* pJSObj =
static_cast<CJS_Object*>(pRuntime->GetObjectPrivate(info.Holder()));
+ if (!pJSObj)
+ return;
+
Alt* pObj = reinterpret_cast<Alt*>(pJSObj->GetEmbedObject());
v8::String::Utf8Value utf8_value(property);
CFX_WideString propname = CFX_WideString::FromUTF8(
diff --git a/fpdfsdk/javascript/JS_EventHandler.cpp b/fpdfsdk/javascript/JS_EventHandler.cpp
index cec7735dc1..bd1c8e29d0 100644
--- a/fpdfsdk/javascript/JS_EventHandler.cpp
+++ b/fpdfsdk/javascript/JS_EventHandler.cpp
@@ -590,22 +590,25 @@ Field* CJS_EventHandler::Source() {
CJS_Runtime* pRuntime = m_pJSEventContext->GetJSRuntime();
v8::Local<v8::Object> pDocObj =
pRuntime->NewFxDynamicObj(CJS_Document::g_nObjDefnID);
- ASSERT(!pDocObj.IsEmpty());
+ if (pDocObj.IsEmpty())
+ return nullptr;
v8::Local<v8::Object> pFieldObj =
pRuntime->NewFxDynamicObj(CJS_Field::g_nObjDefnID);
- ASSERT(!pFieldObj.IsEmpty());
+ if (pFieldObj.IsEmpty())
+ return nullptr;
CJS_Document* pJSDocument =
static_cast<CJS_Document*>(pRuntime->GetObjectPrivate(pDocObj));
- Document* pDocument = (Document*)pJSDocument->GetEmbedObject();
+ CJS_Field* pJSField =
+ static_cast<CJS_Field*>(pRuntime->GetObjectPrivate(pFieldObj));
+
+ Document* pDocument = static_cast<Document*>(pJSDocument->GetEmbedObject());
pDocument->SetFormFillEnv(m_pTargetFormFillEnv
? m_pTargetFormFillEnv.Get()
: m_pJSEventContext->GetFormFillEnv());
- CJS_Field* pJSField =
- static_cast<CJS_Field*>(pRuntime->GetObjectPrivate(pFieldObj));
- Field* pField = (Field*)pJSField->GetEmbedObject();
+ Field* pField = static_cast<Field*>(pJSField->GetEmbedObject());
pField->AttachField(pDocument, m_strSourceName);
return pField;
}
@@ -614,22 +617,25 @@ Field* CJS_EventHandler::Target_Field() {
CJS_Runtime* pRuntime = m_pJSEventContext->GetJSRuntime();
v8::Local<v8::Object> pDocObj =
pRuntime->NewFxDynamicObj(CJS_Document::g_nObjDefnID);
- ASSERT(!pDocObj.IsEmpty());
+ if (pDocObj.IsEmpty())
+ return nullptr;
v8::Local<v8::Object> pFieldObj =
pRuntime->NewFxDynamicObj(CJS_Field::g_nObjDefnID);
- ASSERT(!pFieldObj.IsEmpty());
+ if (pFieldObj.IsEmpty())
+ return nullptr;
CJS_Document* pJSDocument =
static_cast<CJS_Document*>(pRuntime->GetObjectPrivate(pDocObj));
- Document* pDocument = (Document*)pJSDocument->GetEmbedObject();
+ CJS_Field* pJSField =
+ static_cast<CJS_Field*>(pRuntime->GetObjectPrivate(pFieldObj));
+
+ Document* pDocument = static_cast<Document*>(pJSDocument->GetEmbedObject());
pDocument->SetFormFillEnv(m_pTargetFormFillEnv
? m_pTargetFormFillEnv.Get()
: m_pJSEventContext->GetFormFillEnv());
- CJS_Field* pJSField =
- static_cast<CJS_Field*>(pRuntime->GetObjectPrivate(pFieldObj));
- Field* pField = (Field*)pJSField->GetEmbedObject();
+ Field* pField = static_cast<Field*>(pJSField->GetEmbedObject());
pField->AttachField(pDocument, m_strTargetName);
return pField;
}
diff --git a/fpdfsdk/javascript/app.cpp b/fpdfsdk/javascript/app.cpp
index 0c75f6be4a..3a8bf088f3 100644
--- a/fpdfsdk/javascript/app.cpp
+++ b/fpdfsdk/javascript/app.cpp
@@ -215,13 +215,11 @@ bool app::activeDocs(CJS_Runtime* pRuntime,
CJS_Document* pJSDocument = nullptr;
v8::Local<v8::Object> pObj = pRuntime->GetThisObj();
- if (CFXJS_Engine::GetObjDefnID(pObj) == CJS_Document::g_nObjDefnID) {
+ if (CFXJS_Engine::GetObjDefnID(pObj) == CJS_Document::g_nObjDefnID)
pJSDocument = static_cast<CJS_Document*>(pRuntime->GetObjectPrivate(pObj));
- }
CJS_Array aDocs;
aDocs.SetElement(pRuntime, 0, CJS_Value(pRuntime, pJSDocument));
-
if (aDocs.GetLength(pRuntime) > 0)
vp << aDocs;
else
@@ -472,6 +470,9 @@ bool app::setInterval(CJS_Runtime* pRuntime,
v8::Local<v8::Object> pRetObj =
pRuntime->NewFxDynamicObj(CJS_TimerObj::g_nObjDefnID);
+ if (pRetObj.IsEmpty())
+ return false;
+
CJS_TimerObj* pJS_TimerObj =
static_cast<CJS_TimerObj*>(pRuntime->GetObjectPrivate(pRetObj));
TimerObj* pTimerObj = static_cast<TimerObj*>(pJS_TimerObj->GetEmbedObject());
@@ -504,13 +505,13 @@ bool app::setTimeOut(CJS_Runtime* pRuntime,
v8::Local<v8::Object> pRetObj =
pRuntime->NewFxDynamicObj(CJS_TimerObj::g_nObjDefnID);
+ if (pRetObj.IsEmpty())
+ return false;
CJS_TimerObj* pJS_TimerObj =
static_cast<CJS_TimerObj*>(pRuntime->GetObjectPrivate(pRetObj));
-
TimerObj* pTimerObj = static_cast<TimerObj*>(pJS_TimerObj->GetEmbedObject());
pTimerObj->SetTimer(timerRef);
-
vRet = CJS_Value(pRuntime, pRetObj);
return true;
}
diff --git a/fxjs/fxjs_v8.cpp b/fxjs/fxjs_v8.cpp
index 053db07d3b..0c211a130a 100644
--- a/fxjs/fxjs_v8.cpp
+++ b/fxjs/fxjs_v8.cpp
@@ -56,6 +56,12 @@ class CFXJS_ObjDefinition {
v8::Local<v8::FunctionTemplate> fun = v8::FunctionTemplate::New(isolate);
fun->InstanceTemplate()->SetInternalFieldCount(2);
+ fun->SetCallHandler([](const v8::FunctionCallbackInfo<v8::Value>& info) {
+ v8::Local<v8::Object> holder = info.Holder();
+ ASSERT(holder->InternalFieldCount() == 2);
+ holder->SetAlignedPointerInInternalField(0, nullptr);
+ holder->SetAlignedPointerInInternalField(1, nullptr);
+ });
if (eObjType == FXJSOBJTYPE_GLOBAL) {
fun->InstanceTemplate()->Set(
v8::Symbol::GetToStringTag(isolate),
diff --git a/testing/resources/javascript/bug_695826.in b/testing/resources/javascript/bug_695826.in
new file mode 100644
index 0000000000..b20908bc79
--- /dev/null
+++ b/testing/resources/javascript/bug_695826.in
@@ -0,0 +1,47 @@
+{{header}}
+{{object 1 0}} <<
+ /Type /Catalog
+ /Pages 2 0 R
+ /OpenAction 10 0 R
+>>
+endobj
+{{object 2 0}} <<
+ /Type /Pages
+ /Count 1
+ /Kids [
+ 3 0 R
+ ]
+>>
+endobj
+% Page number 0.
+{{object 3 0}} <<
+ /Type /Page
+ /Parent 2 0 R
+ /Resources <<
+ /Font <</F1 15 0 R>>
+ >>
+ /Contents [21 0 R]
+ /MediaBox [0 0 612 792]
+>>
+% OpenAction action
+{{object 10 0}} <<
+ /Type /Action
+ /S /JavaScript
+ /JS 11 0 R
+>>
+endobj
+% JS program to exexute
+{{object 11 0}} <<
+>>
+stream
+var obj = new this.constructor;
+obj.author = 3;
+app.alert('Done!');
+endstream
+endobj
+{{xref}}
+trailer <<
+ /Root 1 0 R
+>>
+{{startxref}}
+%%EOF
diff --git a/testing/resources/javascript/bug_695826_expected.txt b/testing/resources/javascript/bug_695826_expected.txt
new file mode 100644
index 0000000000..5bb6e85966
--- /dev/null
+++ b/testing/resources/javascript/bug_695826_expected.txt
@@ -0,0 +1 @@
+Alert: Done!