Bug 1689191 - restrict nsISiteSecurityService and DataStorage to the main process r=rmf,necko-reviewers,ipc-reviewers,kershaw,nika

Bug 1215723 introduced a mechanism whereby DataStorage data would be propagated
to content processes to avoid the IPC calls involved in querying if hosts were
HSTS. With a low number of content processes, this was a reasonable approach.
However, with Fission and the proliferation of content processes, propagating
changes in DataStorage data to every content process wakes up unrelated
processes and is inefficient. This patch restores the behavior that
nsISiteSecurityService and DataStorage is not available to content processes.
Additionally, bug 1626076 made it possible to use DataStorage directly from the
socket process. However, as of bug 1650356, this is no longer necessary, so
this patch removes that behavior as well.

Differential Revision: https://phabricator.services.mozilla.com/D118207
This commit is contained in:
Dana Keeler 2021-06-24 23:39:52 +00:00
Родитель 57e56287c3
Коммит 02ed64358a
24 изменённых файлов: 91 добавлений и 566 удалений

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

@ -8,6 +8,7 @@
#include "mozilla/SchedulerGroup.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/TaskCategory.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"

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

@ -1389,8 +1389,6 @@ void ContentChild::InitXPCOM(
GfxInfoBase::SetFeatureStatus(std::move(aXPCOMInit.gfxFeatureStatus()));
DataStorage::SetCachedStorageEntries(aXPCOMInit.dataStorage());
// Initialize the RemoteDecoderManager thread and its associated PBackground
// channel.
RemoteDecoderManagerChild::Init();
@ -2263,34 +2261,6 @@ mozilla::ipc::IPCResult ContentChild::RecvCollectPerfStatsJSON(
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvDataStoragePut(
const nsString& aFilename, const DataStorageItem& aItem) {
RefPtr<DataStorage> storage = DataStorage::GetFromRawFileName(aFilename);
if (storage) {
storage->Put(aItem.key(), aItem.value(), aItem.type());
}
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvDataStorageRemove(
const nsString& aFilename, const nsCString& aKey,
const DataStorageType& aType) {
RefPtr<DataStorage> storage = DataStorage::GetFromRawFileName(aFilename);
if (storage) {
storage->Remove(aKey, aType);
}
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvDataStorageClear(
const nsString& aFilename) {
RefPtr<DataStorage> storage = DataStorage::GetFromRawFileName(aFilename);
if (storage) {
storage->Clear();
}
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvNotifyAlertsObserver(
const nsCString& aType, const nsString& aData) {
nsTArray<nsCOMPtr<nsIObserver>> observersToNotify;

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

@ -320,15 +320,6 @@ class ContentChild final : public PContentChild,
mozilla::ipc::IPCResult RecvCollectPerfStatsJSON(
CollectPerfStatsJSONResolver&& aResolver);
mozilla::ipc::IPCResult RecvDataStoragePut(const nsString& aFilename,
const DataStorageItem& aItem);
mozilla::ipc::IPCResult RecvDataStorageRemove(const nsString& aFilename,
const nsCString& aKey,
const DataStorageType& aType);
mozilla::ipc::IPCResult RecvDataStorageClear(const nsString& aFilename);
mozilla::ipc::IPCResult RecvNotifyAlertsObserver(const nsCString& aType,
const nsString& aData);

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

@ -54,7 +54,6 @@
#include "mozilla/BenchmarkStorageParent.h"
#include "mozilla/ContentBlockingUserInteraction.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/DataStorage.h"
#include "mozilla/FOGIPC.h"
#include "mozilla/GlobalStyleSheetCache.h"
#include "mozilla/HangDetails.h"
@ -2932,8 +2931,6 @@ bool ContentParent::InitInternal(ProcessPriority aInitialPriority) {
widget::WinContentSystemParameters::GetSingleton()->GetParentValues();
#endif
DataStorage::GetAllChildProcessData(xpcomInit.dataStorage());
// Send the dynamic scalar definitions to the new process.
TelemetryIPC::GetDynamicScalarDefinitions(xpcomInit.dynamicScalarDefs());

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

@ -67,7 +67,6 @@ include LookAndFeelTypes;
include protocol PSandboxTesting;
#endif
include "ipc/DataStorageIPCUtils.h";
include "ipc/MediaControlIPC.h";
include "mozilla/AntiTrackingIPCUtils.h";
include "mozilla/GfxMessageUtils.h";
@ -340,7 +339,6 @@ struct XPCOMInitData
GfxVarUpdate[] gfxNonDefaultVarUpdates;
ContentDeviceData contentDeviceData;
GfxInfoFeatureStatus[] gfxFeatureStatus;
DataStorageEntry[] dataStorage;
nsCString[] appLocales;
nsCString[] requestedLocales;
DynamicScalarDefinition[] dynamicScalarDefs;
@ -615,10 +613,6 @@ child:
async UpdatePerfStatsCollectionMask(uint64_t aMask);
async CollectPerfStatsJSON() returns (nsCString aStats);
async DataStoragePut(nsString aFilename, DataStorageItem aItem);
async DataStorageRemove(nsString aFilename, nsCString aKey, DataStorageType aType);
async DataStorageClear(nsString aFilename);
async NotifyAlertsObserver(nsCString topic, nsString data);
async GeolocationUpdate(nsIDOMGeoPosition aPosition);

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

@ -10,11 +10,11 @@
#include "nsNetUtil.h"
#include "mozilla/Atomics.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/Components.h"
#include "mozilla/Encoding.h"
#include "mozilla/LoadContext.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/Monitor.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StaticPrefs_privacy.h"
@ -2833,6 +2833,11 @@ nsresult NS_ShouldSecureUpgrade(
const OriginAttributes& aOriginAttributes, bool& aShouldUpgrade,
std::function<void(bool, nsresult)>&& aResultCallback,
bool& aWillCallback) {
MOZ_ASSERT(XRE_IsParentProcess());
if (!XRE_IsParentProcess()) {
return NS_ERROR_NOT_AVAILABLE;
}
aWillCallback = false;
// Even if we're in private browsing mode, we still enforce existing STS

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

@ -878,7 +878,7 @@ void AltSvcCache::EnsureStorageInited() {
return;
}
if (NS_FAILED(mStorage->Init(nullptr))) {
if (NS_FAILED(mStorage->Init())) {
mStorage = nullptr;
} else {
initialized = true;
@ -1251,11 +1251,11 @@ void AltSvcCache::ClearAltServiceMappings() {
nsresult AltSvcCache::GetAltSvcCacheKeys(nsTArray<nsCString>& value) {
MOZ_ASSERT(NS_IsMainThread());
if (gHttpHandler->AllowAltSvc() && mStorage) {
nsTArray<mozilla::psm::DataStorageItem> items;
nsTArray<DataStorageItem> items;
mStorage->GetAll(&items);
for (const auto& item : items) {
value.AppendElement(item.key());
value.AppendElement(item.key);
}
}
return NS_OK;

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

@ -9,8 +9,6 @@
#include "mozilla/Assertions.h"
#include "mozilla/AppShutdown.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/dom/PContent.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/FileUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
@ -60,7 +58,10 @@ class DataStorageMemoryReporter final : public nsIMemoryReporter {
NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) final {
nsTArray<nsString> fileNames;
DataStorage::GetAllFileNames(fileNames);
#define DATA_STORAGE(_) \
fileNames.AppendElement(NS_LITERAL_STRING_FROM_CSTRING(#_ ".txt"));
#include "mozilla/DataStorageList.h"
#undef DATA_STORAGE
for (const auto& file : fileNames) {
RefPtr<DataStorage> ds = DataStorage::GetFromRawFileName(file);
size_t amount = ds->SizeOfIncludingThis(MallocSizeOf);
@ -115,76 +116,6 @@ already_AddRefed<DataStorage> DataStorage::GetFromRawFileName(
aFilename, [&] { return RefPtr{new DataStorage(aFilename)}; }));
}
// static
void DataStorage::GetAllFileNames(nsTArray<nsString>& aItems) {
MOZ_ASSERT(NS_IsMainThread());
if (!sDataStorages) {
return;
}
#define DATA_STORAGE(_) \
aItems.AppendElement(NS_LITERAL_STRING_FROM_CSTRING(#_ ".txt"));
#include "mozilla/DataStorageList.h"
#undef DATA_STORAGE
}
// static
void DataStorage::GetAllChildProcessData(
nsTArray<mozilla::psm::DataStorageEntry>& aEntries) {
nsTArray<nsString> storageFiles;
GetAllFileNames(storageFiles);
for (auto& file : storageFiles) {
psm::DataStorageEntry entry;
entry.filename() = file;
RefPtr<DataStorage> storage = DataStorage::GetFromRawFileName(file);
if (!storage->mInitCalled) {
// Perhaps no consumer has initialized the DataStorage object yet,
// so do that now!
nsresult rv = storage->Init(nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
}
storage->GetAll(&entry.items());
aEntries.AppendElement(std::move(entry));
}
}
// static
void DataStorage::SetCachedStorageEntries(
const nsTArray<mozilla::psm::DataStorageEntry>& aEntries) {
MOZ_ASSERT(XRE_IsContentProcess());
// Make sure to initialize all DataStorage classes.
// For each one, we look through the list of our entries and if we find
// a matching DataStorage object, we initialize it.
//
// Note that this is an O(n^2) operation, but the n here is very small
// (currently 3). There is a comment in the DataStorageList.h header
// about updating the algorithm here to something more fancy if the list
// of DataStorage items grows some day.
nsTArray<psm::DataStorageEntry> entries;
#define DATA_STORAGE(_) \
{ \
psm::DataStorageEntry entry; \
entry.filename() = NS_LITERAL_STRING_FROM_CSTRING(#_ ".txt"); \
for (auto& e : aEntries) { \
if (entry.filename().Equals(e.filename())) { \
entry.items() = e.items().Clone(); \
break; \
} \
} \
entries.AppendElement(std::move(entry)); \
}
#include "mozilla/DataStorageList.h"
#undef DATA_STORAGE
for (auto& entry : entries) {
RefPtr<DataStorage> storage =
DataStorage::GetFromRawFileName(entry.filename());
storage->Init(&entry.items());
}
}
size_t DataStorage::SizeOfIncludingThis(
mozilla::MallocSizeOf aMallocSizeOf) const {
size_t sizeOfExcludingThis =
@ -195,14 +126,18 @@ size_t DataStorage::SizeOfIncludingThis(
return aMallocSizeOf(this) + sizeOfExcludingThis;
}
nsresult DataStorage::Init(const nsTArray<DataStorageItem>* aItems,
mozilla::ipc::FileDescriptor aWriteFd) {
nsresult DataStorage::Init() {
// Don't access the observer service or preferences off the main thread.
if (!NS_IsMainThread()) {
MOZ_ASSERT_UNREACHABLE("DataStorage::Init called off main thread");
return NS_ERROR_NOT_SAME_THREAD;
}
if (!XRE_IsParentProcess()) {
MOZ_ASSERT_UNREACHABLE("DataStorage used in non-parent process");
return NS_ERROR_NOT_AVAILABLE;
}
if (AppShutdown::IsShuttingDown()) {
// Reject new DataStorage instances if the browser is shutting down. There
// is no guarantee that DataStorage writes will be able to be persisted if
@ -229,54 +164,28 @@ nsresult DataStorage::Init(const nsTArray<DataStorageItem>* aItems,
memoryReporterRegistered = true;
}
if (XRE_IsParentProcess() || XRE_IsSocketProcess()) {
nsCOMPtr<nsISerialEventTarget> target;
nsresult rv = NS_CreateBackgroundTaskQueue(
"DataStorage::mBackgroundTaskQueue", getter_AddRefs(target));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mBackgroundTaskQueue = new TaskQueue(target.forget());
nsCOMPtr<nsISerialEventTarget> target;
nsresult rv = NS_CreateBackgroundTaskQueue(
"DataStorage::mBackgroundTaskQueue", getter_AddRefs(target));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mBackgroundTaskQueue = new TaskQueue(target.forget());
// For test purposes, we can set the write timer to be very fast.
uint32_t timerDelayMS = Preferences::GetInt(
"test.datastorage.write_timer_ms", sDataStorageDefaultTimerDelay);
rv = NS_NewTimerWithFuncCallback(
getter_AddRefs(mTimer), DataStorage::TimerCallback, this, timerDelayMS,
nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY, "DataStorageTimer",
mBackgroundTaskQueue);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// For test purposes, we can set the write timer to be very fast.
uint32_t timerDelayMS = Preferences::GetInt("test.datastorage.write_timer_ms",
sDataStorageDefaultTimerDelay);
rv = NS_NewTimerWithFuncCallback(
getter_AddRefs(mTimer), DataStorage::TimerCallback, this, timerDelayMS,
nsITimer::TYPE_REPEATING_SLACK_LOW_PRIORITY, "DataStorageTimer",
mBackgroundTaskQueue);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (XRE_IsParentProcess()) {
MOZ_ASSERT(!aItems);
nsresult rv = AsyncReadData(lock);
if (NS_FAILED(rv)) {
return rv;
}
} else {
// In the child process and socket process, we use the data passed to us by
// the parent process to initialize.
MOZ_ASSERT(XRE_IsContentProcess() || XRE_IsSocketProcess());
MOZ_ASSERT(aItems);
if (XRE_IsSocketProcess() && aWriteFd.IsValid()) {
mWriteFd = aWriteFd;
}
for (auto& item : *aItems) {
Entry entry;
entry.mValue = item.value();
nsresult rv = PutInternal(item.key(), entry, item.type(), lock);
if (NS_FAILED(rv)) {
return rv;
}
}
mReady = true;
NotifyObservers("data-storage-ready");
rv = AsyncReadData(lock);
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
@ -285,101 +194,17 @@ nsresult DataStorage::Init(const nsTArray<DataStorageItem>* aItems,
}
// Clear private data as appropriate.
os->AddObserver(this, "last-pb-context-exited", false);
if (XRE_IsParentProcess() || XRE_IsSocketProcess()) {
// Observe shutdown; save data and prevent any further writes.
// In the parent process, we need to write to the profile directory, so
// we should listen for profile-before-change so that we can safely write to
// the profile.
os->AddObserver(this, "profile-before-change", false);
// In the Parent process, this is a backstop for xpcshell and other cases
// where profile-before-change might not get sent.
os->AddObserver(this, "xpcom-shutdown-threads", false);
}
// Observe shutdown; save data and prevent any further writes.
// We need to write to the profile directory, so we should listen for
// profile-before-change so that we can safely write to the profile.
os->AddObserver(this, "profile-before-change", false);
// This is a backstop for xpcshell and other cases where
// profile-before-change might not get sent.
os->AddObserver(this, "xpcom-shutdown-threads", false);
return NS_OK;
}
class DataStorage::Opener : public Runnable {
public:
explicit Opener(
nsIFile* aFile,
std::function<void(mozilla::ipc::FileDescriptor&&)>&& aResolver)
: Runnable("DataStorage::Opener"),
mFile(aFile),
mResolver(std::move(aResolver)) {
MOZ_ASSERT(mFile);
}
~Opener() = default;
private:
NS_DECL_NSIRUNNABLE
void ResolveFD();
nsCOMPtr<nsIFile> mFile;
std::function<void(mozilla::ipc::FileDescriptor&&)> mResolver;
mozilla::ipc::FileDescriptor mFd;
};
void DataStorage::Opener::ResolveFD() {
MOZ_ASSERT(NS_IsMainThread());
mResolver(std::move(mFd));
}
NS_IMETHODIMP
DataStorage::Opener::Run() {
AutoFDClose prFileDesc;
nsresult rv;
#if defined(XP_WIN)
nsCOMPtr<nsILocalFileWin> winFile = do_QueryInterface(mFile, &rv);
MOZ_ASSERT(winFile);
if (NS_SUCCEEDED(rv)) {
rv = winFile->OpenNSPRFileDescShareDelete(
PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0664, &prFileDesc.rwget());
}
#else
rv = mFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0664,
&prFileDesc.rwget());
#endif /* XP_WIN */
if (NS_SUCCEEDED(rv)) {
mFd = mozilla::ipc::FileDescriptor(
mozilla::ipc::FileDescriptor::PlatformHandleType(
PR_FileDesc2NativeHandle(prFileDesc)));
}
RefPtr<Opener> self = this;
rv = NS_DispatchToMainThread(
NS_NewRunnableFunction("DataStorage::Opener::ResolveFD",
[self]() { self->ResolveFD(); }),
NS_DISPATCH_NORMAL);
MOZ_ASSERT(NS_SUCCEEDED(rv));
return NS_OK;
}
nsresult DataStorage::AsyncTakeFileDesc(
std::function<void(mozilla::ipc::FileDescriptor&&)>&& aResolver) {
MOZ_ASSERT(XRE_IsParentProcess());
WaitForReady();
MutexAutoLock lock(mMutex);
if (!mBackingFile) {
return NS_ERROR_NOT_AVAILABLE;
}
nsCOMPtr<nsIRunnable> job(new Opener(mBackingFile, std::move(aResolver)));
nsresult rv = mBackgroundTaskQueue->Dispatch(job.forget());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mBackingFile = nullptr;
return NS_OK;
}
class DataStorage::Reader : public Runnable {
public:
explicit Reader(DataStorage* aDataStorage)
@ -590,7 +415,6 @@ nsresult DataStorage::Reader::ParseLine(nsDependentCSubstring& aLine,
}
nsresult DataStorage::AsyncReadData(const MutexAutoLock& /*aProofOfLock*/) {
MOZ_ASSERT(XRE_IsParentProcess());
// Allocate a Reader so that even if it isn't dispatched,
// the data-storage-ready notification will be fired and Get
// will be able to proceed (this happens in its destructor).
@ -679,9 +503,9 @@ void DataStorage::ReadAllFromTable(DataStorageType aType,
for (auto iter = GetTableForType(aType, aProofOfLock).Iter(); !iter.Done();
iter.Next()) {
DataStorageItem* item = aItems->AppendElement();
item->key() = iter.Key();
item->value() = iter.Data().mValue;
item->type() = aType;
item->key = iter.Key();
item->value = iter.Data().mValue;
item->type = aType;
}
}
@ -731,26 +555,6 @@ void DataStorage::MaybeEvictOneEntry(DataStorageType aType,
}
}
// NB: Because this may cross a thread boundary, any variables captured by the
// Functor must be captured by copy and not by reference.
template <class Functor>
static void RunOnAllContentParents(Functor func) {
if (!XRE_IsParentProcess()) {
return;
}
using dom::ContentParent;
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableFunction("RunOnAllContentParents", [func]() {
nsTArray<ContentParent*> parents;
ContentParent::GetAll(parents);
for (auto& parent : parents) {
func(parent);
}
});
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
}
nsresult DataStorage::Put(const nsCString& aKey, const nsCString& aValue,
DataStorageType aType) {
WaitForReady();
@ -770,22 +574,7 @@ nsresult DataStorage::Put(const nsCString& aKey, const nsCString& aValue,
MaybeEvictOneEntry(aType, lock);
}
entry.mValue = aValue;
rv = PutInternal(aKey, entry, aType, lock);
if (NS_FAILED(rv)) {
return rv;
}
nsString filename(mFilename);
RunOnAllContentParents(
[aKey, aValue, aType, filename](dom::ContentParent* aParent) {
DataStorageItem item;
item.key() = aKey;
item.value() = aValue;
item.type() = aType;
Unused << aParent->SendDataStoragePut(filename, item);
});
return NS_OK;
return PutInternal(aKey, entry, aType, lock);
}
nsresult DataStorage::PutInternal(const nsCString& aKey, Entry& aEntry,
@ -811,11 +600,6 @@ void DataStorage::Remove(const nsCString& aKey, DataStorageType aType) {
if (aType == DataStorage_Persistent) {
mPendingWrite = true;
}
nsString filename(mFilename);
RunOnAllContentParents([filename, aKey, aType](dom::ContentParent* aParent) {
Unused << aParent->SendDataStorageRemove(filename, aKey, aType);
});
}
class DataStorage::Writer final : public Runnable {
@ -827,31 +611,14 @@ class DataStorage::Writer final : public Runnable {
protected:
NS_DECL_NSIRUNNABLE
nsresult CreateOutputStream(nsIOutputStream** aResult);
nsCString mData;
RefPtr<DataStorage> mDataStorage;
};
nsresult DataStorage::Writer::CreateOutputStream(nsIOutputStream** aResult) {
NS_IMETHODIMP
DataStorage::Writer::Run() {
nsresult rv;
if (XRE_IsSocketProcess()) {
mozilla::ipc::FileDescriptor fd;
{
MutexAutoLock lock(mDataStorage->mMutex);
fd = mDataStorage->mWriteFd;
}
if (!fd.IsValid()) {
return NS_ERROR_NOT_AVAILABLE;
}
return NS_NewLocalFileOutputStream(aResult, fd);
}
MOZ_ASSERT(XRE_IsParentProcess());
// Concurrent operations on nsIFile objects are not guaranteed to be safe,
// so we clone the file while holding the lock and then release the lock.
// At that point, we can safely operate on the clone.
@ -867,15 +634,9 @@ nsresult DataStorage::Writer::CreateOutputStream(nsIOutputStream** aResult) {
return rv;
}
}
return NS_NewLocalFileOutputStream(aResult, file,
PR_CREATE_FILE | PR_TRUNCATE | PR_WRONLY);
}
NS_IMETHODIMP
DataStorage::Writer::Run() {
nsCOMPtr<nsIOutputStream> outputStream;
nsresult rv = CreateOutputStream(getter_AddRefs(outputStream));
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file,
PR_CREATE_FILE | PR_TRUNCATE | PR_WRONLY);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -910,10 +671,7 @@ DataStorage::Writer::Run() {
}
nsresult DataStorage::AsyncWriteData(const MutexAutoLock& /*aProofOfLock*/) {
MOZ_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess());
if (!mPendingWrite || mShuttingDown ||
(!mBackingFile && !mWriteFd.IsValid())) {
if (!mPendingWrite || mShuttingDown || !mBackingFile) {
return NS_OK;
}
@ -948,28 +706,14 @@ nsresult DataStorage::Clear() {
mPrivateDataTable.Clear();
mPendingWrite = true;
if (XRE_IsParentProcess() || XRE_IsSocketProcess()) {
// Asynchronously clear the file. This is similar to the permission manager
// in that it doesn't wait to synchronously remove the data from its backing
// storage either.
nsresult rv = AsyncWriteData(lock);
if (NS_FAILED(rv)) {
return rv;
}
}
nsString filename(mFilename);
RunOnAllContentParents([filename](dom::ContentParent* aParent) {
Unused << aParent->SendDataStorageClear(filename);
});
return NS_OK;
// Asynchronously clear the file. This is similar to the permission manager
// in that it doesn't wait to synchronously remove the data from its backing
// storage either.
return AsyncWriteData(lock);
}
/* static */
void DataStorage::TimerCallback(nsITimer* aTimer, void* aClosure) {
MOZ_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess());
RefPtr<DataStorage> aDataStorage = (DataStorage*)aClosure;
MutexAutoLock lock(aDataStorage->mMutex);
Unused << aDataStorage->AsyncWriteData(lock);
@ -990,7 +734,6 @@ void DataStorage::NotifyObservers(const char* aTopic) {
}
void DataStorage::ShutdownTimer() {
MOZ_ASSERT(XRE_IsParentProcess() || XRE_IsSocketProcess());
MOZ_ASSERT(NS_IsMainThread());
if (mTimer) {
nsresult rv = mTimer->Cancel();
@ -1017,11 +760,6 @@ DataStorage::Observe(nsISupports* /*aSubject*/, const char* aTopic,
return NS_OK;
}
if (!XRE_IsParentProcess() && !XRE_IsSocketProcess()) {
MOZ_ASSERT_UNREACHABLE("unexpected observation topic for content proces");
return NS_ERROR_UNEXPECTED;
}
if (strcmp(aTopic, "profile-before-change") == 0 ||
strcmp(aTopic, "xpcom-shutdown-threads") == 0) {
RefPtr<TaskQueue> taskQueueToAwait;

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

@ -7,9 +7,7 @@
#ifndef mozilla_DataStorage_h
#define mozilla_DataStorage_h
#include <functional>
#include "mozilla/Atomics.h"
#include "mozilla/ipc/FileDescriptor.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Monitor.h"
#include "mozilla/Mutex.h"
@ -27,15 +25,6 @@ namespace mozilla {
class DataStorageMemoryReporter;
class TaskQueue;
namespace dom {
class ContentChild;
} // namespace dom
namespace psm {
class DataStorageEntry;
class DataStorageItem;
} // namespace psm
/**
* DataStorage is a threadsafe, generic, narrow string-based hash map that
* persists data on disk and additionally handles temporary and private data.
@ -100,6 +89,12 @@ enum DataStorageType {
DataStorage_Private
};
struct DataStorageItem final {
nsCString key;
nsCString value;
DataStorageType type;
};
enum class DataStorageClass {
#define DATA_STORAGE(_) _,
#include "mozilla/DataStorageList.h"
@ -107,8 +102,6 @@ enum class DataStorageClass {
};
class DataStorage : public nsIObserver {
typedef psm::DataStorageItem DataStorageItem;
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIOBSERVER
@ -118,21 +111,7 @@ class DataStorage : public nsIObserver {
static already_AddRefed<DataStorage> Get(DataStorageClass aFilename);
// Initializes the DataStorage. Must be called before using.
// aItems is used in the content process and the socket process to initialize
// a cache of the items received from the parent process over IPC. nullptr
// must be passed for the parent process.
// aWriteFd is only used in the socket process for now. The FileDesc is opened
// in parent process and send to socket process. The data storage instance in
// socket process will use this FD to write data to the backing file.
nsresult Init(
const nsTArray<mozilla::psm::DataStorageItem>* aItems,
mozilla::ipc::FileDescriptor aWriteFd = mozilla::ipc::FileDescriptor());
// This function is used to create the file descriptor asynchronously. The FD
// will be sent via the callback. Note that after this call, mBackingFile will
// be nulled to prevent parent process to access the file.
nsresult AsyncTakeFileDesc(
std::function<void(mozilla::ipc::FileDescriptor&&)>&& aResolver);
nsresult Init();
// Given a key and a type of data, returns a value. Returns an empty string if
// the key is not present for that type of data. If Get is called before the
@ -149,20 +128,9 @@ class DataStorage : public nsIObserver {
// Removes all entries of all types of data.
nsresult Clear();
// Read all file names that we know about.
static void GetAllFileNames(nsTArray<nsString>& aItems);
// Read all child process data that we know about.
static void GetAllChildProcessData(
nsTArray<mozilla::psm::DataStorageEntry>& aEntries);
// Read all of the data items.
void GetAll(nsTArray<DataStorageItem>* aItems);
// Set the cached copy of our DataStorage entries in the content process.
static void SetCachedStorageEntries(
const nsTArray<mozilla::psm::DataStorageEntry>& aEntries);
size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
// Return true if this data storage is ready to be used.
@ -178,12 +146,10 @@ class DataStorage : public nsIObserver {
const nsString& aFilename);
friend class ::psm_DataStorageTest;
friend class mozilla::dom::ContentChild;
friend class mozilla::DataStorageMemoryReporter;
class Writer;
class Reader;
class Opener;
class Entry {
public:
@ -247,8 +213,6 @@ class DataStorage : public nsIObserver {
const nsString mFilename;
mozilla::ipc::FileDescriptor mWriteFd;
static StaticAutoPtr<DataStorages> sDataStorages;
};

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

@ -15,5 +15,4 @@
DATA_STORAGE(AlternateServices)
DATA_STORAGE(ClientAuthRememberList)
DATA_STORAGE(SecurityPreloadState)
DATA_STORAGE(SiteSecurityServiceState)

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

@ -5,10 +5,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
include "ipc/DataStorageIPCUtils.h";
using mozilla::DataStorageType from "mozilla/DataStorage.h";
namespace mozilla {
namespace psm {
@ -21,16 +17,5 @@ struct DelegatedCredentialInfoArg {
uint32_t authKeyBits;
};
struct DataStorageItem {
nsCString key;
nsCString value;
DataStorageType type;
};
struct DataStorageEntry {
DataStorageItem[] items;
nsString filename;
};
} // namespace psm
} // namespace mozilla

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

@ -128,6 +128,7 @@ Classes = [
'type': 'nsSiteSecurityService',
'headers': ['/security/manager/ssl/nsSiteSecurityService.h'],
'init_method': 'Init',
'processes': ProcessSelector.MAIN_PROCESS_ONLY,
},
{
'cid': '{57972956-5718-42d2-8070-b3fc72212eaf}',

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

@ -72,7 +72,7 @@ nsresult nsClientAuthRememberService::Init() {
mClientAuthRememberList =
mozilla::DataStorage::Get(DataStorageClass::ClientAuthRememberList);
nsresult rv = mClientAuthRememberList->Init(nullptr);
nsresult rv = mClientAuthRememberList->Init();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@ -96,13 +96,13 @@ nsClientAuthRememberService::ForgetRememberedDecision(const nsACString& key) {
NS_IMETHODIMP
nsClientAuthRememberService::GetDecisions(
nsTArray<RefPtr<nsIClientAuthRememberRecord>>& results) {
nsTArray<mozilla::psm::DataStorageItem> decisions;
nsTArray<DataStorageItem> decisions;
mClientAuthRememberList->GetAll(&decisions);
for (const mozilla::psm::DataStorageItem& decision : decisions) {
if (decision.type() == DataStorageType::DataStorage_Persistent) {
for (const DataStorageItem& decision : decisions) {
if (decision.type == DataStorageType::DataStorage_Persistent) {
RefPtr<nsIClientAuthRememberRecord> tmp =
new nsClientAuthRemember(decision.key(), decision.value());
new nsClientAuthRemember(decision.key, decision.value);
results.AppendElement(tmp);
}
@ -131,17 +131,17 @@ nsClientAuthRememberService::DeleteDecisionsByHost(
}
DataStorageType storageType = GetDataStorageType(attrs);
nsTArray<mozilla::psm::DataStorageItem> decisions;
nsTArray<DataStorageItem> decisions;
mClientAuthRememberList->GetAll(&decisions);
for (const mozilla::psm::DataStorageItem& decision : decisions) {
if (decision.type() == storageType) {
for (const DataStorageItem& decision : decisions) {
if (decision.type == storageType) {
RefPtr<nsIClientAuthRememberRecord> tmp =
new nsClientAuthRemember(decision.key(), decision.value());
new nsClientAuthRemember(decision.key, decision.value);
nsAutoCString asciiHost;
tmp->GetAsciiHost(asciiHost);
if (asciiHost.Equals(aHostName)) {
mClientAuthRememberList->Remove(decision.key(), decision.type());
mClientAuthRememberList->Remove(decision.key, decision.type);
}
}
}

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

@ -258,7 +258,7 @@ nsresult nsSiteSecurityService::Init() {
"test.currentTimeOffsetSeconds");
mSiteStateStorage =
mozilla::DataStorage::Get(DataStorageClass::SiteSecurityServiceState);
nsresult rv = mSiteStateStorage->Init(nullptr);
nsresult rv = mSiteStateStorage->Init();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -394,11 +394,6 @@ NS_IMETHODIMP
nsSiteSecurityService::ResetState(nsIURI* aURI, uint32_t aFlags,
JS::HandleValue aOriginAttributes,
JSContext* aCx, uint8_t aArgc) {
if (!XRE_IsParentProcess()) {
MOZ_CRASH(
"Child process: no direct access to "
"nsISiteSecurityService::ResetState");
}
if (!aURI) {
return NS_ERROR_INVALID_ARG;
}
@ -472,13 +467,6 @@ nsSiteSecurityService::ProcessHeader(
nsITransportSecurityInfo* aSecInfo, uint32_t aFlags, uint32_t aHeaderSource,
const OriginAttributes& aOriginAttributes, uint64_t* aMaxAge,
bool* aIncludeSubdomains, uint32_t* aFailureResult) {
// Child processes are not allowed direct access to this.
if (!XRE_IsParentProcess()) {
MOZ_CRASH(
"Child process: no direct access to "
"nsISiteSecurityService::ProcessHeader");
}
if (aFailureResult) {
*aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
}
@ -901,31 +889,23 @@ nsresult nsSiteSecurityService::IsSecureHost(
}
NS_IMETHODIMP
nsSiteSecurityService::ClearAll() {
// Child processes are not allowed direct access to this.
if (!XRE_IsParentProcess()) {
MOZ_CRASH(
"Child process: no direct access to nsISiteSecurityService::ClearAll");
}
return mSiteStateStorage->Clear();
}
nsSiteSecurityService::ClearAll() { return mSiteStateStorage->Clear(); }
NS_IMETHODIMP
nsSiteSecurityService::Enumerate(nsISimpleEnumerator** aEnumerator) {
NS_ENSURE_ARG(aEnumerator);
nsTArray<mozilla::psm::DataStorageItem> items;
nsTArray<DataStorageItem> items;
mSiteStateStorage->GetAll(&items);
nsCOMArray<nsISiteSecurityState> states;
for (const mozilla::psm::DataStorageItem& item : items) {
if (!StringEndsWith(item.key(), kHSTSKeySuffix)) {
for (const DataStorageItem& item : items) {
if (!StringEndsWith(item.key, kHSTSKeySuffix)) {
// The key does not end with correct suffix, so is not the type we want.
continue;
}
nsCString origin(
StringHead(item.key(), item.key().Length() - kHSTSKeySuffix.Length()));
StringHead(item.key, item.key.Length() - kHSTSKeySuffix.Length()));
nsAutoCString hostname;
OriginAttributes originAttributes;
if (!originAttributes.PopulateFromOrigin(origin, hostname)) {
@ -933,7 +913,7 @@ nsSiteSecurityService::Enumerate(nsISimpleEnumerator** aEnumerator) {
}
nsCOMPtr<nsISiteSecurityState> state(
new SiteHSTSState(hostname, originAttributes, item.value()));
new SiteHSTSState(hostname, originAttributes, item.value));
states.AppendObject(state);
}

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

@ -23,7 +23,7 @@ class psm_DataStorageTest : public ::testing::Test {
::testing::UnitTest::GetInstance()->current_test_info();
NS_ConvertUTF8toUTF16 testName(testInfo->name());
storage = DataStorage::GetFromRawFileName(testName);
storage->Init(nullptr);
storage->Init();
}
RefPtr<DataStorage> storage;

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

@ -32,7 +32,6 @@ const gEVExpected = isDebugBuild;
const CLIENT_AUTH_FILE_NAME = "ClientAuthRememberList.txt";
const SSS_STATE_FILE_NAME = "SiteSecurityServiceState.txt";
const PRELOAD_STATE_FILE_NAME = "SecurityPreloadState.txt";
const CERT_OVERRIDE_FILE_NAME = "cert_override.txt";
const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;

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

@ -1,55 +0,0 @@
/* import-globals-from head_psm.js */
"use strict";
function run_test() {
let SSService = Cc["@mozilla.org/ssservice;1"].getService(
Ci.nsISiteSecurityService
);
ok(
!SSService.isSecureURI(Services.io.newURI("https://expired.example.com"), 0)
);
ok(
SSService.isSecureURI(
Services.io.newURI("https://notexpired.example.com"),
0
)
);
ok(
SSService.isSecureURI(
Services.io.newURI("https://includesubdomains.preloaded.test"),
0
)
);
ok(
!SSService.isSecureURI(
Services.io.newURI("https://sub.includesubdomains.preloaded.test"),
0
)
);
ok(
SSService.isSecureURI(
Services.io.newURI("https://incsubdomain.example.com"),
0
)
);
ok(
SSService.isSecureURI(
Services.io.newURI("https://sub.incsubdomain.example.com"),
0
)
);
ok(
!SSService.isSecureURI(
Services.io.newURI("https://includesubdomains2.preloaded.test"),
0
)
);
ok(
!SSService.isSecureURI(
Services.io.newURI("https://sub.includesubdomains2.preloaded.test"),
0
)
);
do_test_finished();
}

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

@ -10,7 +10,7 @@ var gSSService = null;
var gProfileDir = null;
function do_state_written(aSubject, aTopic, aData) {
if (aData == PRELOAD_STATE_FILE_NAME || aData == CLIENT_AUTH_FILE_NAME) {
if (aData == CLIENT_AUTH_FILE_NAME) {
return;
}
@ -45,7 +45,7 @@ function do_state_written(aSubject, aTopic, aData) {
}
function do_state_read(aSubject, aTopic, aData) {
if (aData == PRELOAD_STATE_FILE_NAME || aData == CLIENT_AUTH_FILE_NAME) {
if (aData == CLIENT_AUTH_FILE_NAME) {
return;
}

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

@ -9,7 +9,7 @@
var gSSService = null;
function checkStateRead(aSubject, aTopic, aData) {
if (aData == PRELOAD_STATE_FILE_NAME || aData == CLIENT_AUTH_FILE_NAME) {
if (aData == CLIENT_AUTH_FILE_NAME) {
return;
}

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

@ -1,40 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// The purpose of this test is to create a site security service state file
// and see that the site security service reads it properly. We also verify
// that state changes are reflected in the child process.
function start_test_in_child() {
run_test_in_child("sss_readstate_child_worker.js");
do_test_finished();
}
function run_test() {
let profileDir = do_get_profile();
let stateFile = profileDir.clone();
stateFile.append(SSS_STATE_FILE_NAME);
// Assuming we're working with a clean slate, the file shouldn't exist
// until we create it.
ok(!stateFile.exists());
let outputStream = FileUtils.openFileOutputStream(stateFile);
let now = Date.now();
let lines = [
`expired.example.com:HSTS\t0\t0\t${now - 100000},1,0`,
`notexpired.example.com:HSTS\t0\t0\t${now + 100000},1,0`,
// This overrides an entry on the preload list.
`includesubdomains.preloaded.test:HSTS\t0\t0\t${now + 100000},1,0`,
`incsubdomain.example.com:HSTS\t0\t0\t${now + 100000},1,1`,
// This overrides an entry on the preload list.
"includesubdomains2.preloaded.test:HSTS\t0\t0\t0,2,0",
];
writeLinesAndClose(lines, outputStream);
Services.obs.addObserver(start_test_in_child, "data-storage-ready");
do_test_pending();
let SSService = Cc["@mozilla.org/ssservice;1"].getService(
Ci.nsISiteSecurityService
);
notEqual(SSService, null);
}

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

@ -9,7 +9,7 @@
var gSSService = null;
function checkStateRead(aSubject, aTopic, aData) {
if (aData == PRELOAD_STATE_FILE_NAME || aData == CLIENT_AUTH_FILE_NAME) {
if (aData == CLIENT_AUTH_FILE_NAME) {
return;
}

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

@ -10,7 +10,7 @@
var gSSService = null;
function checkStateRead(aSubject, aTopic, aData) {
if (aData == PRELOAD_STATE_FILE_NAME || aData == CLIENT_AUTH_FILE_NAME) {
if (aData == CLIENT_AUTH_FILE_NAME) {
return;
}

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

@ -17,7 +17,7 @@ const NON_ISSUED_KEY_HASH = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
// separated by newlines ('\n')
function checkStateWritten(aSubject, aTopic, aData) {
if (aData == PRELOAD_STATE_FILE_NAME || aData == CLIENT_AUTH_FILE_NAME) {
if (aData == CLIENT_AUTH_FILE_NAME) {
return;
}

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

@ -207,10 +207,6 @@ run-sequentially = hardcoded ports
[test_sss_originAttributes.js]
run-sequentially = hardcoded ports
[test_sss_readstate.js]
[test_sss_readstate_child.js]
support-files = sss_readstate_child_worker.js
# bug 1124289 - run_test_in_child violates the sandbox on android
skip-if = toolkit == 'android'
[test_sss_readstate_empty.js]
[test_sss_readstate_garbage.js]
[test_sss_readstate_huge.js]