Skip to content

Commit 359cdd5

Browse files
committed
Bug 1555557 - Do cert override file writes off the main thread. r=keeler
Differential Revision: https://phabricator.services.mozilla.com/D35375
1 parent 71061cd commit 359cdd5

File tree

4 files changed

+131
-51
lines changed

4 files changed

+131
-51
lines changed

security/manager/ssl/DataStorage.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -998,7 +998,7 @@ DataStorage::Writer::Run() {
998998
}
999999

10001000
const char* ptr = mData.get();
1001-
int32_t remaining = mData.Length();
1001+
uint32_t remaining = mData.Length();
10021002
uint32_t written = 0;
10031003
while (remaining > 0) {
10041004
rv = outputStream->Write(ptr, remaining, &written);

security/manager/ssl/nsCertOverrideService.cpp

Lines changed: 117 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "ScopedNSSTypes.h"
1111
#include "SharedSSLState.h"
1212
#include "mozilla/Assertions.h"
13+
#include "mozilla/TaskQueue.h"
1314
#include "mozilla/Telemetry.h"
1415
#include "mozilla/TextUtils.h"
1516
#include "mozilla/Unused.h"
@@ -38,6 +39,50 @@ using namespace mozilla::psm;
3839

3940
#define CERT_OVERRIDE_FILE_NAME "cert_override.txt"
4041

42+
class WriterRunnable : public Runnable {
43+
public:
44+
WriterRunnable(nsCertOverrideService* aService, nsCString& aData,
45+
nsCOMPtr<nsIFile> aFile)
46+
: Runnable("nsCertOverrideService::WriterRunnable"),
47+
mCertOverrideService(aService),
48+
mData(aData),
49+
mFile(std::move(aFile)) {}
50+
51+
NS_IMETHOD
52+
Run() override {
53+
mCertOverrideService->AssertOnTaskQueue();
54+
nsresult rv;
55+
56+
nsCOMPtr<nsIOutputStream> outputStream;
57+
rv = NS_NewSafeLocalFileOutputStream(
58+
getter_AddRefs(outputStream), mFile,
59+
PR_CREATE_FILE | PR_TRUNCATE | PR_WRONLY);
60+
NS_ENSURE_SUCCESS(rv, rv);
61+
62+
const char* ptr = mData.get();
63+
uint32_t remaining = mData.Length();
64+
uint32_t written = 0;
65+
while (remaining > 0) {
66+
rv = outputStream->Write(ptr, remaining, &written);
67+
NS_ENSURE_SUCCESS(rv, rv);
68+
remaining -= written;
69+
ptr += written;
70+
}
71+
72+
nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(outputStream);
73+
MOZ_ASSERT(safeStream);
74+
rv = safeStream->Finish();
75+
NS_ENSURE_SUCCESS(rv, rv);
76+
77+
return NS_OK;
78+
}
79+
80+
private:
81+
const RefPtr<nsCertOverrideService> mCertOverrideService;
82+
nsCString mData;
83+
const nsCOMPtr<nsIFile> mFile;
84+
};
85+
4186
void nsCertOverride::convertBitsToString(OverrideBits ob,
4287
/*out*/ nsACString& str) {
4388
str.Truncate();
@@ -83,19 +128,44 @@ void nsCertOverride::convertStringToBits(const nsACString& str,
83128
}
84129

85130
NS_IMPL_ISUPPORTS(nsCertOverrideService, nsICertOverrideService, nsIObserver,
86-
nsISupportsWeakReference)
131+
nsISupportsWeakReference, nsIAsyncShutdownBlocker)
87132

88133
nsCertOverrideService::nsCertOverrideService()
89-
: mDisableAllSecurityCheck(false), mMutex("nsCertOverrideService.mutex") {}
134+
: mDisableAllSecurityCheck(false), mMutex("nsCertOverrideService.mutex") {
135+
nsCOMPtr<nsIEventTarget> target =
136+
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
137+
MOZ_ASSERT(target);
138+
139+
mWriterTaskQueue = new TaskQueue(target.forget());
140+
}
90141

91142
nsCertOverrideService::~nsCertOverrideService() = default;
92143

144+
static nsCOMPtr<nsIAsyncShutdownClient> GetShutdownBarrier() {
145+
nsCOMPtr<nsIAsyncShutdownService> svc =
146+
mozilla::services::GetAsyncShutdownService();
147+
MOZ_RELEASE_ASSERT(svc);
148+
149+
nsCOMPtr<nsIAsyncShutdownClient> barrier;
150+
nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(barrier));
151+
152+
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
153+
MOZ_RELEASE_ASSERT(barrier);
154+
return barrier;
155+
}
156+
93157
nsresult nsCertOverrideService::Init() {
94158
if (!NS_IsMainThread()) {
95159
MOZ_ASSERT_UNREACHABLE("nsCertOverrideService initialized off main thread");
96160
return NS_ERROR_NOT_SAME_THREAD;
97161
}
98162

163+
nsresult rv = GetShutdownBarrier()->AddBlocker(
164+
this, NS_LITERAL_STRING(__FILE__), __LINE__,
165+
NS_LITERAL_STRING("nsCertOverrideService shutdown"));
166+
// If that failed, we're likely getting initialized during shutdown.
167+
NS_ENSURE_SUCCESS(rv, rv);
168+
99169
nsCOMPtr<nsIObserverService> observerService =
100170
mozilla::services::GetObserverService();
101171

@@ -253,31 +323,15 @@ nsresult nsCertOverrideService::Write(const MutexAutoLock& aProofOfLock) {
253323
return NS_OK;
254324
}
255325

256-
nsresult rv;
257-
nsCOMPtr<nsIOutputStream> fileOutputStream;
258-
rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(fileOutputStream),
259-
mSettingsFile, -1, 0600);
260-
if (NS_FAILED(rv)) {
261-
NS_ERROR("failed to open cert_warn_settings.txt for writing");
262-
return rv;
263-
}
264-
265-
// get a buffered output stream 4096 bytes big, to optimize writes
266-
nsCOMPtr<nsIOutputStream> bufferedOutputStream;
267-
rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
268-
fileOutputStream.forget(), 4096);
269-
if (NS_FAILED(rv)) {
270-
return rv;
271-
}
326+
nsCString output;
272327

273328
static const char kHeader[] =
274329
"# PSM Certificate Override Settings file" NS_LINEBREAK
275330
"# This is a generated file! Do not edit." NS_LINEBREAK;
276331

277332
/* see ::Read for file format */
278333

279-
uint32_t unused;
280-
bufferedOutputStream->Write(kHeader, sizeof(kHeader) - 1, &unused);
334+
output.Append(kHeader);
281335

282336
static const char kTab[] = "\t";
283337
for (auto iter = mSettingsTable.Iter(); !iter.Done(); iter.Next()) {
@@ -291,34 +345,29 @@ nsresult nsCertOverrideService::Write(const MutexAutoLock& aProofOfLock) {
291345
nsAutoCString bits_string;
292346
nsCertOverride::convertBitsToString(settings.mOverrideBits, bits_string);
293347

294-
bufferedOutputStream->Write(entry->mHostWithPort.get(),
295-
entry->mHostWithPort.Length(), &unused);
296-
bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused);
297-
bufferedOutputStream->Write(sSHA256OIDString, sizeof(sSHA256OIDString) - 1,
298-
&unused);
299-
bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused);
300-
bufferedOutputStream->Write(settings.mFingerprint.get(),
301-
settings.mFingerprint.Length(), &unused);
302-
bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused);
303-
bufferedOutputStream->Write(bits_string.get(), bits_string.Length(),
304-
&unused);
305-
bufferedOutputStream->Write(kTab, sizeof(kTab) - 1, &unused);
306-
bufferedOutputStream->Write(settings.mDBKey.get(), settings.mDBKey.Length(),
307-
&unused);
308-
bufferedOutputStream->Write(NS_LINEBREAK, NS_LINEBREAK_LEN, &unused);
309-
}
310-
311-
// All went ok. Maybe except for problems in Write(), but the stream detects
312-
// that for us
313-
nsCOMPtr<nsISafeOutputStream> safeStream =
314-
do_QueryInterface(bufferedOutputStream);
315-
MOZ_ASSERT(safeStream, "Expected a safe output stream!");
316-
if (safeStream) {
317-
rv = safeStream->Finish();
318-
if (NS_FAILED(rv)) {
319-
NS_WARNING("failed to save cert warn settings file! possible dataloss");
320-
return rv;
321-
}
348+
output.Append(entry->mHostWithPort);
349+
output.Append(kTab);
350+
output.Append(sSHA256OIDString);
351+
output.Append(kTab);
352+
output.Append(settings.mFingerprint);
353+
output.Append(kTab);
354+
output.Append(bits_string);
355+
output.Append(kTab);
356+
output.Append(settings.mDBKey);
357+
output.Append(NS_LINEBREAK);
358+
}
359+
360+
// Make a clone of the file to pass to the WriterRunnable.
361+
nsCOMPtr<nsIFile> file;
362+
nsresult rv;
363+
rv = mSettingsFile->Clone(getter_AddRefs(file));
364+
NS_ENSURE_SUCCESS(rv, rv);
365+
366+
nsCOMPtr<nsIRunnable> runnable = new WriterRunnable(this, output, file);
367+
rv = mWriterTaskQueue->Dispatch(runnable.forget());
368+
369+
if (NS_FAILED(rv)) {
370+
return rv;
322371
}
323372

324373
return NS_OK;
@@ -678,3 +727,23 @@ void nsCertOverrideService::GetHostWithPort(const nsACString& aHostName,
678727
}
679728
_retval.Assign(hostPort);
680729
}
730+
731+
// nsIAsyncShutdownBlocker implementation
732+
NS_IMETHODIMP
733+
nsCertOverrideService::GetName(nsAString& aName) {
734+
aName = NS_LITERAL_STRING("nsCertOverrideService: shutdown");
735+
return NS_OK;
736+
}
737+
738+
NS_IMETHODIMP
739+
nsCertOverrideService::GetState(nsIPropertyBag**) { return NS_OK; }
740+
741+
NS_IMETHODIMP
742+
nsCertOverrideService::BlockShutdown(nsIAsyncShutdownClient*) {
743+
mWriterTaskQueue->BeginShutdown();
744+
mWriterTaskQueue->AwaitShutdownAndIdle();
745+
746+
nsresult rv = GetShutdownBarrier()->RemoveBlocker(this);
747+
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
748+
return NS_OK;
749+
}

security/manager/ssl/nsCertOverrideService.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111

1212
#include "mozilla/HashFunctions.h"
1313
#include "mozilla/Mutex.h"
14+
#include "mozilla/TaskQueue.h"
1415
#include "mozilla/TypedEnumBits.h"
16+
#include "nsIAsyncShutdown.h"
1517
#include "nsICertOverrideService.h"
1618
#include "nsIFile.h"
1719
#include "nsIObserver.h"
@@ -90,11 +92,13 @@ class nsCertOverrideEntry final : public PLDHashEntryHdr {
9092

9193
class nsCertOverrideService final : public nsICertOverrideService,
9294
public nsIObserver,
93-
public nsSupportsWeakReference {
95+
public nsSupportsWeakReference,
96+
public nsIAsyncShutdownBlocker {
9497
public:
9598
NS_DECL_THREADSAFE_ISUPPORTS
9699
NS_DECL_NSICERTOVERRIDESERVICE
97100
NS_DECL_NSIOBSERVER
101+
NS_DECL_NSIASYNCSHUTDOWNBLOCKER
98102

99103
nsCertOverrideService();
100104

@@ -116,7 +120,11 @@ class nsCertOverrideService final : public nsICertOverrideService,
116120
static void GetHostWithPort(const nsACString& aHostName, int32_t aPort,
117121
nsACString& _retval);
118122

119-
protected:
123+
void AssertOnTaskQueue() const {
124+
MOZ_ASSERT(mWriterTaskQueue->IsOnCurrentThread());
125+
}
126+
127+
private:
120128
~nsCertOverrideService();
121129

122130
bool mDisableAllSecurityCheck;
@@ -136,6 +144,8 @@ class nsCertOverrideService final : public nsICertOverrideService,
136144
nsCertOverride::OverrideBits ob,
137145
const nsACString& dbKey,
138146
const mozilla::MutexAutoLock& aProofOfLock);
147+
148+
RefPtr<TaskQueue> mWriterTaskQueue;
139149
};
140150

141151
#define NS_CERTOVERRIDE_CID \

xpcom/threads/TaskQueue.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ class TaskQueue : public AbstractThread, public nsIDirectTaskDispatcher {
114114
// Returns true if the current thread is currently running a Runnable in
115115
// the task queue.
116116
bool IsCurrentThreadIn() const override;
117+
using nsISerialEventTarget::IsOnCurrentThread;
117118

118119
protected:
119120
virtual ~TaskQueue();

0 commit comments

Comments
 (0)