Bug 1422335 - Using a sandbox on main-thread when deserializing data for IDB - part 2 - Upgrade value, r=asuth

This commit is contained in:
Andrea Marchesini 2018-01-18 13:19:07 +01:00
Родитель f3cbbe9985
Коммит 29ae60f412
3 изменённых файлов: 154 добавлений и 250 удалений

Просмотреть файл

@ -3648,13 +3648,10 @@ UpgradeSchemaFrom18_0To19_0(mozIStorageConnection* aConnection)
return NS_OK;
}
class NormalJSContext;
class UpgradeFileIdsFunction final
: public mozIStorageFunction
{
RefPtr<FileManager> mFileManager;
nsAutoPtr<NormalJSContext> mContext;
public:
UpgradeFileIdsFunction()
@ -8057,53 +8054,6 @@ private:
DoDatabaseWork(DatabaseConnection* aConnection) override;
};
class NormalJSContext
{
friend class nsAutoPtr<NormalJSContext>;
static const JSClass sGlobalClass;
static const uint32_t kContextHeapSize = 768 * 1024;
JSContext* mContext;
JSObject* mGlobal;
public:
static NormalJSContext*
Create();
JSContext*
Context() const
{
return mContext;
}
JSObject*
Global() const
{
return mGlobal;
}
protected:
NormalJSContext()
: mContext(nullptr)
, mGlobal(nullptr)
{
MOZ_COUNT_CTOR(NormalJSContext);
}
~NormalJSContext()
{
MOZ_COUNT_DTOR(NormalJSContext);
if (mContext) {
JS_DestroyContext(mContext);
}
}
bool
Init();
};
class CreateIndexOp::UpdateIndexDataValuesFunction final
: public mozIStorageFunction
{
@ -19535,13 +19485,7 @@ UpgradeFileIdsFunction::Init(nsIFile* aFMDirectory,
return rv;
}
nsAutoPtr<NormalJSContext> context(NormalJSContext::Create());
if (NS_WARN_IF(!context)) {
return NS_ERROR_FAILURE;
}
mFileManager.swap(fileManager);
mContext = context;
return NS_OK;
}
@ -19554,7 +19498,6 @@ UpgradeFileIdsFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
MOZ_ASSERT(aArguments);
MOZ_ASSERT(aResult);
MOZ_ASSERT(mFileManager);
MOZ_ASSERT(mContext);
AUTO_PROFILER_LABEL("UpgradeFileIdsFunction::OnFunctionCall", STORAGE);
@ -19576,30 +19519,10 @@ UpgradeFileIdsFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
mFileManager,
&cloneInfo);
JSContext* cx = mContext->Context();
JSAutoRequest ar(cx);
JSAutoCompartment ac(cx, mContext->Global());
JS::Rooted<JS::Value> clone(cx);
if (NS_WARN_IF(!IDBObjectStore::DeserializeUpgradeValue(cx, cloneInfo,
&clone))) {
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
nsAutoString fileIds;
for (uint32_t count = cloneInfo.mFiles.Length(), index = 0;
index < count;
index++) {
StructuredCloneFile& file = cloneInfo.mFiles[index];
MOZ_ASSERT(file.mFileInfo);
const int64_t id = file.mFileInfo->Id();
if (index) {
fileIds.Append(' ');
}
fileIds.AppendInt(file.mType == StructuredCloneFile::eBlob ? id : -id);
rv = IDBObjectStore::DeserializeUpgradeValueToFileIds(cloneInfo, fileIds);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
nsCOMPtr<nsIVariant> result = new mozilla::storage::TextVariant(fileIds);
@ -25005,74 +24928,6 @@ CreateIndexOp::DoDatabaseWork(DatabaseConnection* aConnection)
return NS_OK;
}
static const JSClassOps sNormalJSContextGlobalClassOps = {
/* addProperty */ nullptr,
/* delProperty */ nullptr,
/* enumerate */ nullptr,
/* newEnumerate */ nullptr,
/* resolve */ nullptr,
/* mayResolve */ nullptr,
/* finalize */ nullptr,
/* call */ nullptr,
/* hasInstance */ nullptr,
/* construct */ nullptr,
/* trace */ JS_GlobalObjectTraceHook
};
const JSClass NormalJSContext::sGlobalClass = {
"IndexedDBTransactionThreadGlobal",
JSCLASS_GLOBAL_FLAGS,
&sNormalJSContextGlobalClassOps
};
bool
NormalJSContext::Init()
{
MOZ_ASSERT(!IsOnBackgroundThread());
mContext = JS_NewContext(kContextHeapSize);
if (NS_WARN_IF(!mContext)) {
return false;
}
// Let everyone know that we might be able to call JS. This alerts the
// profiler about certain possible deadlocks.
NS_GetCurrentThread()->SetCanInvokeJS(true);
// Not setting this will cause JS_CHECK_RECURSION to report false positives.
JS_SetNativeStackQuota(mContext, 128 * sizeof(size_t) * 1024);
if (NS_WARN_IF(!JS::InitSelfHostedCode(mContext))) {
return false;
}
JSAutoRequest ar(mContext);
JS::CompartmentOptions options;
mGlobal = JS_NewGlobalObject(mContext, &sGlobalClass, nullptr,
JS::FireOnNewGlobalHook, options);
if (NS_WARN_IF(!mGlobal)) {
return false;
}
return true;
}
// static
NormalJSContext*
NormalJSContext::Create()
{
MOZ_ASSERT(!IsOnBackgroundThread());
nsAutoPtr<NormalJSContext> newContext(new NormalJSContext());
if (NS_WARN_IF(!newContext->Init())) {
return nullptr;
}
return newContext.forget();
}
NS_IMPL_ISUPPORTS(CreateIndexOp::UpdateIndexDataValuesFunction,
mozIStorageFunction);

Просмотреть файл

@ -872,6 +872,24 @@ public:
JS::MutableHandle<JSObject*> aResult)
{
MOZ_ASSERT(aCx);
// If we have eBlob, we are in an IDB SQLite schema upgrade where we don't
// care about a real 'MutableFile', but we just care of having a propert
// |mType| flag.
if (aFile.mType == StructuredCloneFile::eBlob) {
aFile.mType = StructuredCloneFile::eMutableFile;
// Just make a dummy object.
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
if (NS_WARN_IF(!obj)) {
return false;
}
aResult.set(obj);
return true;
}
MOZ_ASSERT(aFile.mType == StructuredCloneFile::eMutableFile);
if (!aFile.mMutableFile || !NS_IsMainThread()) {
@ -1009,74 +1027,6 @@ public:
}
};
// We don't need to upgrade database on B2G. See the comment in ActorsParent.cpp,
// UpgradeSchemaFrom18_0To19_0()
class UpgradeDeserializationHelper
{
public:
static bool
CreateAndWrapMutableFile(JSContext* aCx,
StructuredCloneFile& aFile,
const MutableFileData& aData,
JS::MutableHandle<JSObject*> aResult)
{
MOZ_ASSERT(aCx);
MOZ_ASSERT(aFile.mType == StructuredCloneFile::eBlob);
aFile.mType = StructuredCloneFile::eMutableFile;
// Just make a dummy object. The file_ids upgrade function is only
// interested in the |mType| flag.
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
if (NS_WARN_IF(!obj)) {
return false;
}
aResult.set(obj);
return true;
}
static bool
CreateAndWrapBlobOrFile(JSContext* aCx,
IDBDatabase* aDatabase,
StructuredCloneFile& aFile,
const BlobOrFileData& aData,
JS::MutableHandle<JSObject*> aResult)
{
MOZ_ASSERT(aCx);
MOZ_ASSERT(aFile.mType == StructuredCloneFile::eBlob);
MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
aData.tag == SCTAG_DOM_BLOB);
// Just make a dummy object. The file_ids upgrade function is only interested
// in the |mType| flag.
JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
if (NS_WARN_IF(!obj)) {
return false;
}
aResult.set(obj);
return true;
}
static bool
CreateAndWrapWasmModule(JSContext* aCx,
StructuredCloneFile& aFile,
const WasmModuleData& aData,
JS::MutableHandle<JSObject*> aResult)
{
MOZ_ASSERT(aCx);
MOZ_ASSERT(aFile.mType == StructuredCloneFile::eBlob);
MOZ_ASSERT(false, "This should never be possible!");
return false;
}
};
template <class Traits>
JSObject*
CommonStructuredCloneReadCallback(JSContext* aCx,
@ -1569,6 +1519,130 @@ private:
nsresult mStatus;
};
class DeserializeUpgradeValueHelper final : public Runnable
{
public:
explicit DeserializeUpgradeValueHelper(StructuredCloneReadInfo& aCloneReadInfo)
: Runnable("DeserializeUpgradeValueHelper")
, mMonitor("DeserializeUpgradeValueHelper::mMonitor")
, mCloneReadInfo(aCloneReadInfo)
, mStatus(NS_ERROR_FAILURE)
{}
nsresult
DispatchAndWait(nsAString& aFileIds)
{
// We don't need to go to the main-thread and use the sandbox.
if (!mCloneReadInfo.mData.Size()) {
PopulateFileIds(aFileIds);
return NS_OK;
}
// The operation will continue on the main-thread.
MOZ_ASSERT(!(mCloneReadInfo.mData.Size() % sizeof(uint64_t)));
MonitorAutoLock lock(mMonitor);
RefPtr<Runnable> self = this;
nsresult rv = SystemGroup::Dispatch(TaskCategory::Other, self.forget());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
lock.Wait();
if (NS_FAILED(mStatus)) {
return mStatus;
}
PopulateFileIds(aFileIds);
return NS_OK;
}
NS_IMETHOD
Run() override
{
MOZ_ASSERT(NS_IsMainThread());
AutoJSAPI jsapi;
jsapi.Init();
JSContext* cx = jsapi.cx();
JS::Rooted<JSObject*> global(cx, SandboxHolder::GetSandbox(cx));
if (NS_WARN_IF(!global)) {
OperationCompleted(NS_ERROR_FAILURE);
return NS_OK;
}
JSAutoCompartment ac(cx, global);
JS::Rooted<JS::Value> value(cx);
nsresult rv = DeserializeUpgradeValue(cx, &value);
if (NS_WARN_IF(NS_FAILED(rv))) {
OperationCompleted(rv);
return NS_OK;
}
OperationCompleted(NS_OK);
return NS_OK;
}
private:
nsresult
DeserializeUpgradeValue(JSContext* aCx, JS::MutableHandle<JS::Value> aValue)
{
static const JSStructuredCloneCallbacks callbacks = {
CommonStructuredCloneReadCallback<ValueDeserializationHelper>,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr
};
if (!JS_ReadStructuredClone(aCx, mCloneReadInfo.mData,
JS_STRUCTURED_CLONE_VERSION,
JS::StructuredCloneScope::SameProcessSameThread,
aValue, &callbacks, &mCloneReadInfo)) {
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
return NS_OK;
}
void
PopulateFileIds(nsAString& aFileIds)
{
for (uint32_t count = mCloneReadInfo.mFiles.Length(), index = 0;
index < count;
index++) {
StructuredCloneFile& file = mCloneReadInfo.mFiles[index];
MOZ_ASSERT(file.mFileInfo);
const int64_t id = file.mFileInfo->Id();
if (index) {
aFileIds.Append(' ');
}
aFileIds.AppendInt(file.mType == StructuredCloneFile::eBlob ? id : -id);
}
}
void
OperationCompleted(nsresult aStatus)
{
mStatus = aStatus;
MonitorAutoLock lock(mMonitor);
lock.Notify();
}
Monitor mMonitor;
StructuredCloneReadInfo& mCloneReadInfo;
nsresult mStatus;
};
} // anonymous
// static
@ -1590,39 +1664,15 @@ IDBObjectStore::DeserializeIndexValueToUpdateInfos(int64_t aIndexID,
}
// static
bool
IDBObjectStore::DeserializeUpgradeValue(JSContext* aCx,
StructuredCloneReadInfo& aCloneReadInfo,
JS::MutableHandle<JS::Value> aValue)
nsresult
IDBObjectStore::DeserializeUpgradeValueToFileIds(StructuredCloneReadInfo& aCloneReadInfo,
nsAString& aFileIds)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aCx);
if (!aCloneReadInfo.mData.Size()) {
aValue.setUndefined();
return true;
}
MOZ_ASSERT(!(aCloneReadInfo.mData.Size() % sizeof(uint64_t)));
JSAutoRequest ar(aCx);
static JSStructuredCloneCallbacks callbacks = {
CommonStructuredCloneReadCallback<UpgradeDeserializationHelper>,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr
};
if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION,
JS::StructuredCloneScope::SameProcessSameThread,
aValue, &callbacks, &aCloneReadInfo)) {
return false;
}
return true;
RefPtr<DeserializeUpgradeValueHelper> helper =
new DeserializeUpgradeValueHelper(aCloneReadInfo);
return helper->DispatchAndWait(aFileIds);
}
#ifdef DEBUG

Просмотреть файл

@ -105,10 +105,9 @@ public:
StructuredCloneReadInfo& aCloneReadInfo,
JS::MutableHandle<JS::Value> aValue);
static bool
DeserializeUpgradeValue(JSContext* aCx,
StructuredCloneReadInfo& aCloneReadInfo,
JS::MutableHandle<JS::Value> aValue);
static nsresult
DeserializeUpgradeValueToFileIds(StructuredCloneReadInfo& aCloneReadInfo,
nsAString& aFileIds);
static const JSClass*
DummyPropClass()