summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--application/palemoon/components/downloads/DownloadsCommon.jsm22
-rw-r--r--application/palemoon/components/downloads/moz.build7
-rw-r--r--dom/media/PeerConnection.js17
-rw-r--r--dom/performance/PerformanceNavigationTiming.cpp17
-rw-r--r--dom/plugins/base/nsPluginStreamListenerPeer.cpp18
-rw-r--r--gfx/qcms/chain.c6
-rw-r--r--gfx/thebes/gfxFontUtils.cpp89
-rw-r--r--gfx/thebes/gfxFontUtils.h2
-rw-r--r--gfx/thebes/gfxHarfBuzzShaper.cpp8
-rw-r--r--gfx/thebes/gfxPlatform.cpp6
-rwxr-xr-xmedia/webrtc/signaling/src/media-conduit/AudioConduit.h2
-rwxr-xr-xmedia/webrtc/signaling/src/media-conduit/MediaConduitInterface.h2
-rwxr-xr-xmedia/webrtc/signaling/src/media-conduit/VideoConduit.cpp4
-rwxr-xr-xmedia/webrtc/signaling/src/media-conduit/VideoConduit.h3
-rw-r--r--media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp5
-rw-r--r--media/webrtc/signaling/src/sdp/sipcc/sdp_private.h2
-rw-r--r--media/webrtc/signaling/src/sdp/sipcc/sdp_token.c20
-rw-r--r--media/webrtc/signaling/src/sdp/sipcc/sdp_utils.c63
-rw-r--r--media/webrtc/signaling/test/mediaconduit_unittests.cpp2
-rw-r--r--mfbt/BufferList.h136
-rw-r--r--mfbt/tests/TestBufferList.cpp45
-rwxr-xr-xnetwerk/sctp/src/netinet/sctp_auth.c2
-rwxr-xr-xnetwerk/sctp/src/netinet/sctp_pcb.c2
-rw-r--r--security/nss/lib/pk11wrap/pk11merge.c5
-rw-r--r--toolkit/xre/nsAppRunner.cpp5
-rw-r--r--toolkit/xre/nsEmbedFunctions.cpp4
-rw-r--r--xpcom/io/FilePreferences.cpp272
-rw-r--r--xpcom/io/FilePreferences.h25
-rw-r--r--xpcom/io/moz.build5
-rw-r--r--xpcom/io/nsLocalFileWin.cpp10
-rw-r--r--xpcom/tests/gtest/TestFilePreferencesWin.cpp141
-rw-r--r--xpcom/tests/gtest/moz.build5
32 files changed, 747 insertions, 205 deletions
diff --git a/application/palemoon/components/downloads/DownloadsCommon.jsm b/application/palemoon/components/downloads/DownloadsCommon.jsm
index c6614e780..156578572 100644
--- a/application/palemoon/components/downloads/DownloadsCommon.jsm
+++ b/application/palemoon/components/downloads/DownloadsCommon.jsm
@@ -77,7 +77,6 @@ const nsIDM = Ci.nsIDownloadManager;
const kDownloadsStringBundleUrl =
"chrome://browser/locale/downloads/downloads.properties";
-const kPrefBdmScanWhenDone = "browser.download.manager.scanWhenDone";
const kPrefBdmAlertOnExeOpen = "browser.download.manager.alertOnEXEOpen";
const kDownloadsStringsRequiringFormatting = {
@@ -518,23 +517,22 @@ this.DownloadsCommon = {
if (!(aOwnerWindow instanceof Ci.nsIDOMWindow))
throw new Error("aOwnerWindow must be a dom-window object");
+#ifdef XP_WIN
+ // On Windows, the system will provide a native confirmation prompt
+ // for .exe files. Exclude this from our prompt, but prompt on other
+ // executable types.
+ let isWindowsExe = aFile.leafName.toLowerCase().endsWith(".exe");
+#else
+ let isWindowsExe = false;
+#endif
+
// Confirm opening executable files if required.
- if (aFile.isExecutable()) {
+ if (aFile.isExecutable() && !isWindowsExe) {
let showAlert = true;
try {
showAlert = Services.prefs.getBoolPref(kPrefBdmAlertOnExeOpen);
} catch (ex) { }
- // On Vista and above, we rely on native security prompting for
- // downloaded content unless it's disabled.
- if (DownloadsCommon.isWinVistaOrHigher) {
- try {
- if (Services.prefs.getBoolPref(kPrefBdmScanWhenDone)) {
- showAlert = false;
- }
- } catch (ex) { }
- }
-
if (showAlert) {
let name = aFile.leafName;
let message =
diff --git a/application/palemoon/components/downloads/moz.build b/application/palemoon/components/downloads/moz.build
index 3bebfd6d1..61d8c0f62 100644
--- a/application/palemoon/components/downloads/moz.build
+++ b/application/palemoon/components/downloads/moz.build
@@ -13,7 +13,10 @@ EXTRA_COMPONENTS += [
]
EXTRA_JS_MODULES += [
- 'DownloadsCommon.jsm',
'DownloadsLogger.jsm',
'DownloadsTaskbar.jsm',
-] \ No newline at end of file
+]
+
+EXTRA_PP_JS_MODULES += [
+ 'DownloadsCommon.jsm',
+]
diff --git a/dom/media/PeerConnection.js b/dom/media/PeerConnection.js
index 0c3021799..0569b15ae 100644
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -516,6 +516,18 @@ RTCPeerConnection.prototype = {
};
},
+ // This implements the fairly common "Queue a task" logic
+ async _queueTaskWithClosedCheck(func) {
+ return new Promise(resolve => {
+ Services.tm.mainThread.dispatch({ run() {
+ if (!this._closed) {
+ func();
+ resolve();
+ }
+ }}, Ci.nsIThread.DISPATCH_NORMAL);
+ });
+ },
+
/**
* An RTCConfiguration may look like this:
*
@@ -1445,7 +1457,10 @@ PeerConnectionObserver.prototype = {
break;
case "IceConnectionState":
- this.handleIceConnectionStateChange(this._dompc._pc.iceConnectionState);
+ let connState = this._dompc._pc.iceConnectionState;
+ this._dompc._queueTaskWithClosedCheck(() => {
+ this.handleIceConnectionStateChange(connState);
+ });
break;
case "IceGatheringState":
diff --git a/dom/performance/PerformanceNavigationTiming.cpp b/dom/performance/PerformanceNavigationTiming.cpp
index 4e00b2bb2..d7e16725a 100644
--- a/dom/performance/PerformanceNavigationTiming.cpp
+++ b/dom/performance/PerformanceNavigationTiming.cpp
@@ -6,6 +6,7 @@
#include "mozilla/dom/PerformanceNavigationTiming.h"
#include "mozilla/dom/PerformanceNavigationTimingBinding.h"
+#include "mozilla/TimerClamping.h"
using namespace mozilla::dom;
@@ -24,49 +25,49 @@ PerformanceNavigationTiming::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aG
DOMHighResTimeStamp
PerformanceNavigationTiming::UnloadEventStart() const
{
- return mTiming->GetDOMTiming()->GetUnloadEventStartHighRes();
+ return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetUnloadEventStartHighRes());
}
DOMHighResTimeStamp
PerformanceNavigationTiming::UnloadEventEnd() const
{
- return mTiming->GetDOMTiming()->GetUnloadEventEndHighRes();
+ return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetUnloadEventEndHighRes());
}
DOMHighResTimeStamp
PerformanceNavigationTiming::DomInteractive() const
{
- return mTiming->GetDOMTiming()->GetDomInteractiveHighRes();
+ return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetDomInteractiveHighRes());
}
DOMHighResTimeStamp
PerformanceNavigationTiming::DomContentLoadedEventStart() const
{
- return mTiming->GetDOMTiming()->GetDomContentLoadedEventStartHighRes();
+ return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetDomContentLoadedEventStartHighRes());
}
DOMHighResTimeStamp
PerformanceNavigationTiming::DomContentLoadedEventEnd() const
{
- return mTiming->GetDOMTiming()->GetDomContentLoadedEventEndHighRes();
+ return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetDomContentLoadedEventEndHighRes());
}
DOMHighResTimeStamp
PerformanceNavigationTiming::DomComplete() const
{
- return mTiming->GetDOMTiming()->GetDomCompleteHighRes();
+ return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetDomCompleteHighRes());
}
DOMHighResTimeStamp
PerformanceNavigationTiming::LoadEventStart() const
{
- return mTiming->GetDOMTiming()->GetLoadEventStartHighRes();
+ return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetLoadEventStartHighRes());
}
DOMHighResTimeStamp
PerformanceNavigationTiming::LoadEventEnd() const
{
- return mTiming->GetDOMTiming()->GetLoadEventEndHighRes();
+ return TimerClamping::ReduceMsTimeValue(mTiming->GetDOMTiming()->GetLoadEventEndHighRes());
}
NavigationType
diff --git a/dom/plugins/base/nsPluginStreamListenerPeer.cpp b/dom/plugins/base/nsPluginStreamListenerPeer.cpp
index 26e0318e3..665e11ec1 100644
--- a/dom/plugins/base/nsPluginStreamListenerPeer.cpp
+++ b/dom/plugins/base/nsPluginStreamListenerPeer.cpp
@@ -1381,15 +1381,6 @@ nsPluginStreamListenerPeer::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsICh
return NS_ERROR_FAILURE;
}
- nsCOMPtr<nsIAsyncVerifyRedirectCallback> proxyCallback =
- new ChannelRedirectProxyCallback(this, callback, oldChannel, newChannel);
-
- // Give NPAPI a chance to control redirects.
- bool notificationHandled = mPStreamListener->HandleRedirectNotification(oldChannel, newChannel, proxyCallback);
- if (notificationHandled) {
- return NS_OK;
- }
-
// Don't allow cross-origin 307 POST redirects.
nsCOMPtr<nsIHttpChannel> oldHttpChannel(do_QueryInterface(oldChannel));
if (oldHttpChannel) {
@@ -1413,6 +1404,15 @@ nsPluginStreamListenerPeer::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsICh
}
}
+ nsCOMPtr<nsIAsyncVerifyRedirectCallback> proxyCallback =
+ new ChannelRedirectProxyCallback(this, callback, oldChannel, newChannel);
+
+ // Give NPAPI a chance to control redirects.
+ bool notificationHandled = mPStreamListener->HandleRedirectNotification(oldChannel, newChannel, proxyCallback);
+ if (notificationHandled) {
+ return NS_OK;
+ }
+
// Fall back to channel event sink for window.
nsCOMPtr<nsIChannelEventSink> channelEventSink;
nsresult rv = GetInterfaceGlobal(NS_GET_IID(nsIChannelEventSink), getter_AddRefs(channelEventSink));
diff --git a/gfx/qcms/chain.c b/gfx/qcms/chain.c
index e382fbe00..dbae18378 100644
--- a/gfx/qcms/chain.c
+++ b/gfx/qcms/chain.c
@@ -972,6 +972,12 @@ static float* qcms_modular_transform_data(struct qcms_modular_transform *transfo
assert(0 && "Unsupported transform module");
return NULL;
}
+ if (transform->grid_size <= 0 &&
+ (transform_fn == qcms_transform_module_clut ||
+ transform_fn == qcms_transform_module_clut_only)) {
+ assert(0 && "Invalid transform");
+ return NULL;
+ }
transform->transform_module_fn(transform,src,dest,len);
dest = src;
src = new_src;
diff --git a/gfx/thebes/gfxFontUtils.cpp b/gfx/thebes/gfxFontUtils.cpp
index cb505e87b..54ca03ff6 100644
--- a/gfx/thebes/gfxFontUtils.cpp
+++ b/gfx/thebes/gfxFontUtils.cpp
@@ -575,55 +575,64 @@ typedef struct {
#pragma pack()
uint32_t
-gfxFontUtils::MapCharToGlyphFormat4(const uint8_t *aBuf, char16_t aCh)
+gfxFontUtils::MapCharToGlyphFormat4(const uint8_t* aBuf, uint32_t aLength,
+ char16_t aCh)
{
const Format4Cmap *cmap4 = reinterpret_cast<const Format4Cmap*>(aBuf);
- uint16_t segCount;
- const AutoSwap_PRUint16 *endCodes;
- const AutoSwap_PRUint16 *startCodes;
- const AutoSwap_PRUint16 *idDelta;
- const AutoSwap_PRUint16 *idRangeOffset;
- uint16_t probe;
- uint16_t rangeShiftOver2;
- uint16_t index;
-
- segCount = (uint16_t)(cmap4->segCountX2) / 2;
-
- endCodes = &cmap4->arrays[0];
- startCodes = &cmap4->arrays[segCount + 1]; // +1 for reserved word between arrays
- idDelta = &startCodes[segCount];
- idRangeOffset = &idDelta[segCount];
-
- probe = 1 << (uint16_t)(cmap4->entrySelector);
- rangeShiftOver2 = (uint16_t)(cmap4->rangeShift) / 2;
-
- if ((uint16_t)(startCodes[rangeShiftOver2]) <= aCh) {
- index = rangeShiftOver2;
- } else {
- index = 0;
- }
-
- while (probe > 1) {
- probe >>= 1;
- if ((uint16_t)(startCodes[index + probe]) <= aCh) {
- index += probe;
+
+ uint16_t segCount = (uint16_t)(cmap4->segCountX2) / 2;
+
+ const AutoSwap_PRUint16* endCodes = &cmap4->arrays[0];
+ const AutoSwap_PRUint16* startCodes = &cmap4->arrays[segCount + 1];
+ const AutoSwap_PRUint16* idDelta = &startCodes[segCount];
+ const AutoSwap_PRUint16* idRangeOffset = &idDelta[segCount];
+
+ // Sanity-check that the fixed-size arrays don't exceed the buffer.
+ const uint8_t* const limit = aBuf + aLength;
+ if ((const uint8_t*)(&idRangeOffset[segCount]) > limit) {
+ return 0; // broken font, just bail out safely
+ }
+
+ // For most efficient binary search, we want to work on a range of segment
+ // indexes that is a power of 2 so that we can always halve it by shifting.
+ // So we find the largest power of 2 that is <= segCount.
+ // We will offset this range by segOffset so as to reach the end
+ // of the table, provided that doesn't put us beyond the target
+ // value from the outset.
+ uint32_t powerOf2 = mozilla::FindHighestBit(segCount);
+ uint32_t segOffset = segCount - powerOf2;
+ uint32_t idx = 0;
+
+ if (uint16_t(startCodes[segOffset]) <= aCh) {
+ idx = segOffset;
+ }
+
+ // Repeatedly halve the size of the range until we find the target group
+ while (powerOf2 > 1) {
+ powerOf2 >>= 1;
+ if (uint16_t(startCodes[idx + powerOf2]) <= aCh) {
+ idx += powerOf2;
}
}
- if (aCh >= (uint16_t)(startCodes[index]) && aCh <= (uint16_t)(endCodes[index])) {
+ if (aCh >= uint16_t(startCodes[idx]) && aCh <= uint16_t(endCodes[idx])) {
uint16_t result;
- if ((uint16_t)(idRangeOffset[index]) == 0) {
+ if (uint16_t(idRangeOffset[idx]) == 0) {
result = aCh;
} else {
- uint16_t offset = aCh - (uint16_t)(startCodes[index]);
- const AutoSwap_PRUint16 *glyphIndexTable =
- (const AutoSwap_PRUint16*)((const char*)&idRangeOffset[index] +
- (uint16_t)(idRangeOffset[index]));
+ uint16_t offset = aCh - uint16_t(startCodes[idx]);
+ const AutoSwap_PRUint16* glyphIndexTable =
+ (const AutoSwap_PRUint16*)((const char*)&idRangeOffset[idx] +
+ uint16_t(idRangeOffset[idx]));
+ if ((const uint8_t*)(glyphIndexTable + offset + 1) > limit) {
+ return 0; // broken font, just bail out safely
+ }
result = glyphIndexTable[offset];
}
- // note that this is unsigned 16-bit arithmetic, and may wrap around
- result += (uint16_t)(idDelta[index]);
+ // Note that this is unsigned 16-bit arithmetic, and may wrap around
+ // (which is required behavior per spec)
+ result += uint16_t(idDelta[idx]);
return result;
}
@@ -761,7 +770,8 @@ gfxFontUtils::MapCharToGlyph(const uint8_t *aCmapBuf, uint32_t aBufLength,
switch (format) {
case 4:
gid = aUnicode < UNICODE_BMP_LIMIT ?
- MapCharToGlyphFormat4(aCmapBuf + offset, char16_t(aUnicode)) : 0;
+ MapCharToGlyphFormat4(aCmapBuf + offset, aBufLength - offset,
+ char16_t(aUnicode)) : 0;
break;
case 10:
gid = MapCharToGlyphFormat10(aCmapBuf + offset, aUnicode);
@@ -785,6 +795,7 @@ gfxFontUtils::MapCharToGlyph(const uint8_t *aCmapBuf, uint32_t aBufLength,
case 4:
if (aUnicode < UNICODE_BMP_LIMIT) {
varGID = MapCharToGlyphFormat4(aCmapBuf + offset,
+ aBufLength - offset,
char16_t(aUnicode));
}
break;
diff --git a/gfx/thebes/gfxFontUtils.h b/gfx/thebes/gfxFontUtils.h
index dd6a76558..1e87e5436 100644
--- a/gfx/thebes/gfxFontUtils.h
+++ b/gfx/thebes/gfxFontUtils.h
@@ -804,7 +804,7 @@ public:
bool& aUnicodeFont, bool& aSymbolFont);
static uint32_t
- MapCharToGlyphFormat4(const uint8_t *aBuf, char16_t aCh);
+ MapCharToGlyphFormat4(const uint8_t *aBuf, uint32_t aLength, char16_t aCh);
static uint32_t
MapCharToGlyphFormat10(const uint8_t *aBuf, uint32_t aCh);
diff --git a/gfx/thebes/gfxHarfBuzzShaper.cpp b/gfx/thebes/gfxHarfBuzzShaper.cpp
index 4b9dbbc14..f2264bc1f 100644
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -111,13 +111,15 @@ gfxHarfBuzzShaper::GetNominalGlyph(hb_codepoint_t unicode) const
NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
"cmap data not correctly set up, expect disaster");
+ uint32_t length;
const uint8_t* data =
- (const uint8_t*)hb_blob_get_data(mCmapTable, nullptr);
+ (const uint8_t*)hb_blob_get_data(mCmapTable, &length);
switch (mCmapFormat) {
case 4:
gid = unicode < UNICODE_BMP_LIMIT ?
gfxFontUtils::MapCharToGlyphFormat4(data + mSubtableOffset,
+ length - mSubtableOffset,
unicode) : 0;
break;
case 10:
@@ -157,8 +159,9 @@ gfxHarfBuzzShaper::GetVariationGlyph(hb_codepoint_t unicode,
NS_ASSERTION(mCmapTable && (mCmapFormat > 0) && (mSubtableOffset > 0),
"cmap data not correctly set up, expect disaster");
+ uint32_t length;
const uint8_t* data =
- (const uint8_t*)hb_blob_get_data(mCmapTable, nullptr);
+ (const uint8_t*)hb_blob_get_data(mCmapTable, &length);
if (mUVSTableOffset) {
hb_codepoint_t gid =
@@ -176,6 +179,7 @@ gfxHarfBuzzShaper::GetVariationGlyph(hb_codepoint_t unicode,
case 4:
if (compat < UNICODE_BMP_LIMIT) {
return gfxFontUtils::MapCharToGlyphFormat4(data + mSubtableOffset,
+ length - mSubtableOffset,
compat);
}
break;
diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp
index 684117788..171d1bec9 100644
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -156,6 +156,7 @@ static Mutex* gGfxPlatformPrefsLock = nullptr;
static qcms_profile *gCMSOutputProfile = nullptr;
static qcms_profile *gCMSsRGBProfile = nullptr;
+static bool gCMSRGBTransformFailed = false;
static qcms_transform *gCMSRGBTransform = nullptr;
static qcms_transform *gCMSInverseRGBTransform = nullptr;
static qcms_transform *gCMSRGBATransform = nullptr;
@@ -1856,7 +1857,7 @@ gfxPlatform::GetCMSsRGBProfile()
qcms_transform *
gfxPlatform::GetCMSRGBTransform()
{
- if (!gCMSRGBTransform) {
+ if (!gCMSRGBTransform && !gCMSRGBTransformFailed) {
qcms_profile *inProfile, *outProfile;
outProfile = GetCMSOutputProfile();
inProfile = GetCMSsRGBProfile();
@@ -1867,6 +1868,9 @@ gfxPlatform::GetCMSRGBTransform()
gCMSRGBTransform = qcms_transform_create(inProfile, QCMS_DATA_RGB_8,
outProfile, QCMS_DATA_RGB_8,
QCMS_INTENT_PERCEPTUAL);
+ if (!gCMSRGBTransform) {
+ gCMSRGBTransformFailed = true;
+ }
}
return gCMSRGBTransform;
diff --git a/media/webrtc/signaling/src/media-conduit/AudioConduit.h b/media/webrtc/signaling/src/media-conduit/AudioConduit.h
index 228736dcc..fcc7e0f37 100755
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.h
@@ -161,6 +161,8 @@ public:
virtual uint64_t CodecPluginID() override { return 0; }
+ virtual void DeleteStreams() override {}
+
WebrtcAudioConduit():
mVoiceEngine(nullptr),
mTransportMonitor("WebrtcAudioConduit"),
diff --git a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
index 05c34fea0..0654b1175 100755
--- a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
+++ b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
@@ -227,6 +227,8 @@ public:
uint64_t* bytesSent) = 0;
virtual uint64_t CodecPluginID() = 0;
+
+ virtual void DeleteStreams() = 0;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaSessionConduit)
diff --git a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
index 3f0445122..b406fded5 100755
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -109,7 +109,7 @@ WebrtcVideoConduit::~WebrtcVideoConduit()
// Release AudioConduit first by dropping reference on MainThread, where it expects to be
SyncTo(nullptr);
- Destroy();
+ MOZ_ASSERT(!mSendStream && !mRecvStream, "Call DeleteStreams prior to ~WebrtcVideoConduit.");
}
bool WebrtcVideoConduit::SetLocalSSRC(unsigned int ssrc)
@@ -478,7 +478,7 @@ WebrtcVideoConduit::Init()
}
void
-WebrtcVideoConduit::Destroy()
+WebrtcVideoConduit::DeleteStreams()
{
// The first one of a pair to be deleted shuts down media for both
//Deal with External Capturer
diff --git a/media/webrtc/signaling/src/media-conduit/VideoConduit.h b/media/webrtc/signaling/src/media-conduit/VideoConduit.h
index 323a6a284..ff50d80b5 100755
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.h
@@ -269,6 +269,8 @@ public:
return mSendingHeight;
}
+ virtual void DeleteStreams() override;
+
unsigned int SendingMaxFs() override {
if(mCurSendCodecConfig) {
return mCurSendCodecConfig->mEncodingConstraints.maxFs;
@@ -288,7 +290,6 @@ public:
MediaConduitErrorCode InitMain();
virtual MediaConduitErrorCode Init();
- virtual void Destroy();
int GetChannel() { return mChannel; }
webrtc::VideoEngine* GetVideoEngine() { return mVideoEngine; }
diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
index 4f42b0bb7..0d388a8f4 100644
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -1036,6 +1036,11 @@ PeerConnectionMedia::SelfDestruct_m()
mLocalSourceStreams.Clear();
mRemoteSourceStreams.Clear();
+ // Clean up our send and receive streams
+ for (auto i = mConduits.begin(); i != mConduits.end(); ++i) {
+ i->second.second->DeleteStreams();
+ }
+
mMainThread = nullptr;
// Final self-destruct.
diff --git a/media/webrtc/signaling/src/sdp/sipcc/sdp_private.h b/media/webrtc/signaling/src/sdp/sipcc/sdp_private.h
index a98f4b119..3459b0c0e 100644
--- a/media/webrtc/signaling/src/sdp/sipcc/sdp_private.h
+++ b/media/webrtc/signaling/src/sdp/sipcc/sdp_private.h
@@ -347,8 +347,6 @@ extern uint32_t sdp_getnextnumtok(const char *str, const char **str_end,
extern uint32_t sdp_getnextnumtok_or_null(const char *str, const char **str_end,
const char *delim, tinybool *null_ind,
sdp_result_e *result);
-extern tinybool sdp_getchoosetok(const char *str, const char **str_end,
- const char *delim, sdp_result_e *result);
extern
tinybool verify_sdescriptions_mki(char *buf, char *mkiVal, uint16_t *mkiLen);
diff --git a/media/webrtc/signaling/src/sdp/sipcc/sdp_token.c b/media/webrtc/signaling/src/sdp/sipcc/sdp_token.c
index a002f9a73..f9eb04198 100644
--- a/media/webrtc/signaling/src/sdp/sipcc/sdp_token.c
+++ b/media/webrtc/signaling/src/sdp/sipcc/sdp_token.c
@@ -1162,15 +1162,11 @@ sdp_result_e sdp_parse_media (sdp_t *sdp_p, uint16_t level, const char *ptr)
}
port_ptr = port;
for (i=0; i < SDP_MAX_PORT_PARAMS; i++) {
- if (sdp_getchoosetok(port_ptr, &port_ptr, "/ \t", &result) == TRUE) {
- num[i] = SDP_CHOOSE_PARAM;
- } else {
- num[i] = sdp_getnextnumtok(port_ptr, (const char **)&port_ptr,
- "/ \t", &result);
- if (result != SDP_SUCCESS) {
- break;
- }
- }
+ num[i] = sdp_getnextnumtok(port_ptr, (const char **)&port_ptr,
+ "/ \t", &result);
+ if (result != SDP_SUCCESS) {
+ break;
+ }
num_port_params++;
}
@@ -1396,10 +1392,8 @@ sdp_result_e sdp_parse_media (sdp_t *sdp_p, uint16_t level, const char *ptr)
}
port_ptr = port;
- if (sdp_getchoosetok(port_ptr, &port_ptr, "/ \t", &result)) {
- sctp_port = SDP_CHOOSE_PARAM;
- } else {
- sctp_port = sdp_getnextnumtok(port_ptr, (const char **)&port_ptr,
+ {
+ sctp_port = sdp_getnextnumtok(port_ptr, (const char **)&port_ptr,
"/ \t", &result);
if (result != SDP_SUCCESS) {
sdp_parse_error(sdp_p,
diff --git a/media/webrtc/signaling/src/sdp/sipcc/sdp_utils.c b/media/webrtc/signaling/src/sdp/sipcc/sdp_utils.c
index a02035c72..8acb3dbbf 100644
--- a/media/webrtc/signaling/src/sdp/sipcc/sdp_utils.c
+++ b/media/webrtc/signaling/src/sdp/sipcc/sdp_utils.c
@@ -431,69 +431,6 @@ uint32_t sdp_getnextnumtok (const char *str, const char **str_end,
}
-/* See if the next token in a string is the choose character. The delim
- * characters are passed in as a param. The check also will not go past
- * a new line char or the end of the string. Skip any delimiters before
- * the token.
- */
-tinybool sdp_getchoosetok (const char *str, const char **str_end,
- const char *delim, sdp_result_e *result)
-{
- const char *b;
- int flag2moveon;
-
- if ((str == NULL) || (str_end == NULL)) {
- *result = SDP_FAILURE;
- return(FALSE);
- }
-
- /* Locate front of token, skipping any delimiters */
- for ( ; ((*str != '\0') && (*str != '\n') && (*str != '\r')); str++) {
- flag2moveon = 1; /* Default to move on unless we find a delimiter */
- for (b=delim; *b; b++) {
- if (*str == *b) {
- flag2moveon = 0;
- break;
- }
- }
- if( flag2moveon ) {
- break; /* We're at the beginning of the token */
- }
- }
-
- /* Make sure there's really a token present. */
- if ((*str == '\0') || (*str == '\n') || (*str == '\r')) {
- *result = SDP_FAILURE;
- *str_end = (char *)str;
- return(FALSE);
- }
-
- /* See if the token is '$' followed by a delimiter char or end of str. */
- if (*str == '$') {
- str++;
- if ((*str == '\0') || (*str == '\n') || (*str == '\r')) {
- *result = SDP_SUCCESS;
- /* skip the choose char in the string. */
- *str_end = (char *)(str+1);
- return(TRUE);
- }
- for (b=delim; *b; b++) {
- if (*str == *b) {
- *result = SDP_SUCCESS;
- /* skip the choose char in the string. */
- *str_end = (char *)(str+1);
- return(TRUE);
- }
- }
- }
-
- /* If the token was not '$' followed by a delim, token is not choose */
- *result = SDP_SUCCESS;
- *str_end = (char *)str;
- return(FALSE);
-
-}
-
/*
* SDP Crypto Utility Functions.
*
diff --git a/media/webrtc/signaling/test/mediaconduit_unittests.cpp b/media/webrtc/signaling/test/mediaconduit_unittests.cpp
index adcc838bf..07e3b3975 100644
--- a/media/webrtc/signaling/test/mediaconduit_unittests.cpp
+++ b/media/webrtc/signaling/test/mediaconduit_unittests.cpp
@@ -810,6 +810,8 @@ class TransportConduitTest : public ::testing::Test
err = videoSession->ConfigureSendMediaCodec(nullptr);
EXPECT_TRUE(err != mozilla::kMediaConduitNoError);
+
+ videoSession->DeleteStreams();
mozilla::SyncRunnable::DispatchToThread(gMainThread,
WrapRunnable(
diff --git a/mfbt/BufferList.h b/mfbt/BufferList.h
index 26b335957..7faf5a2ae 100644
--- a/mfbt/BufferList.h
+++ b/mfbt/BufferList.h
@@ -9,6 +9,7 @@
#include <algorithm>
#include "mozilla/AllocPolicy.h"
+#include "mozilla/Maybe.h"
#include "mozilla/Move.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/Types.h"
@@ -456,61 +457,108 @@ BufferList<AllocPolicy>::Extract(IterImpl& aIter, size_t aSize, bool* aSuccess)
MOZ_ASSERT(aSize % kSegmentAlignment == 0);
MOZ_ASSERT(intptr_t(aIter.mData) % kSegmentAlignment == 0);
- IterImpl iter = aIter;
- size_t size = aSize;
- size_t toCopy = std::min(size, aIter.RemainingInSegment());
- MOZ_ASSERT(toCopy % kSegmentAlignment == 0);
+ auto failure = [this, aSuccess]() {
+ *aSuccess = false;
+ return BufferList(0, 0, mStandardCapacity);
+ };
- BufferList result(0, toCopy, mStandardCapacity);
- BufferList error(0, 0, mStandardCapacity);
+ // Number of segments we'll need to copy data from to satisfy the request.
+ size_t segmentsNeeded = 0;
+ // If this is None then the last segment is a full segment, otherwise we need
+ // to copy this many bytes.
+ Maybe<size_t> lastSegmentSize;
+ {
+ // Copy of the iterator to walk the BufferList and see how many segments we
+ // need to copy.
+ IterImpl iter = aIter;
+ size_t remaining = aSize;
+ while (!iter.Done() && remaining &&
+ remaining >= iter.RemainingInSegment()) {
+ remaining -= iter.RemainingInSegment();
+ iter.Advance(*this, iter.RemainingInSegment());
+ segmentsNeeded++;
+ }
- // Copy the head
- if (!result.WriteBytes(aIter.mData, toCopy)) {
- *aSuccess = false;
- return error;
+ if (remaining) {
+ if (iter.Done()) {
+ // We reached the end of the BufferList and there wasn't enough data to
+ // satisfy the request.
+ return failure();
+ }
+ lastSegmentSize.emplace(remaining);
+ // The last block also counts as a segment. This makes the conditionals
+ // on segmentsNeeded work in the rest of the function.
+ segmentsNeeded++;
+ }
}
- iter.Advance(*this, toCopy);
- size -= toCopy;
- // Move segments to result
- auto resultGuard = MakeScopeExit([&] {
- *aSuccess = false;
- result.mSegments.erase(result.mSegments.begin()+1, result.mSegments.end());
- });
-
- size_t movedSize = 0;
- uintptr_t toRemoveStart = iter.mSegment;
- uintptr_t toRemoveEnd = iter.mSegment;
- while (!iter.Done() &&
- !iter.HasRoomFor(size)) {
- if (!result.mSegments.append(Segment(mSegments[iter.mSegment].mData,
- mSegments[iter.mSegment].mSize,
- mSegments[iter.mSegment].mCapacity))) {
- return error;
- }
- movedSize += iter.RemainingInSegment();
- size -= iter.RemainingInSegment();
- toRemoveEnd++;
- iter.Advance(*this, iter.RemainingInSegment());
+ BufferList result(0, 0, mStandardCapacity);
+ if (!result.mSegments.reserve(segmentsNeeded + lastSegmentSize.isSome())) {
+ return failure();
}
- if (size) {
- if (!iter.HasRoomFor(size) ||
- !result.WriteBytes(iter.Data(), size)) {
- return error;
+ // Copy the first segment, it's special because we can't just steal the
+ // entire Segment struct from this->mSegments.
+ size_t firstSegmentSize = std::min(aSize, aIter.RemainingInSegment());
+ if (!result.WriteBytes(aIter.Data(), firstSegmentSize)) {
+ return failure();
+ }
+ aIter.Advance(*this, firstSegmentSize);
+ segmentsNeeded--;
+
+ // The entirety of the request wasn't in the first segment, now copy the
+ // rest.
+ if (segmentsNeeded) {
+ char* finalSegment = nullptr;
+ // Pre-allocate the final segment so that if this fails, we return before
+ // we delete the elements from |this->mSegments|.
+ if (lastSegmentSize.isSome()) {
+ MOZ_RELEASE_ASSERT(mStandardCapacity >= *lastSegmentSize);
+ finalSegment = this->template pod_malloc<char>(mStandardCapacity);
+ if (!finalSegment) {
+ return failure();
+ }
+ }
+
+ size_t copyStart = aIter.mSegment;
+ // Copy segments from this over to the result and remove them from our
+ // storage. Not needed if the only segment we need to copy is the last
+ // partial one.
+ size_t segmentsToCopy = segmentsNeeded - lastSegmentSize.isSome();
+ for (size_t i = 0; i < segmentsToCopy; ++i) {
+ result.mSegments.infallibleAppend(
+ Segment(mSegments[aIter.mSegment].mData,
+ mSegments[aIter.mSegment].mSize,
+ mSegments[aIter.mSegment].mCapacity));
+ aIter.Advance(*this, aIter.RemainingInSegment());
+ }
+ // Due to the way IterImpl works, there are two cases here: (1) if we've
+ // consumed the entirety of the BufferList, then the iterator is pointed at
+ // the end of the final segment, (2) otherwise it is pointed at the start
+ // of the next segment. We want to verify that we really consumed all
+ // |segmentsToCopy| segments.
+ MOZ_RELEASE_ASSERT(
+ (aIter.mSegment == copyStart + segmentsToCopy) ||
+ (aIter.Done() && aIter.mSegment == copyStart + segmentsToCopy - 1));
+ mSegments.erase(mSegments.begin() + copyStart,
+ mSegments.begin() + copyStart + segmentsToCopy);
+
+ // Reset the iter's position for what we just deleted.
+ aIter.mSegment -= segmentsToCopy;
+
+ if (lastSegmentSize.isSome()) {
+ // We called reserve() on result.mSegments so infallibleAppend is safe.
+ result.mSegments.infallibleAppend(
+ Segment(finalSegment, 0, mStandardCapacity));
+ bool r = result.WriteBytes(aIter.Data(), *lastSegmentSize);
+ MOZ_RELEASE_ASSERT(r);
+ aIter.Advance(*this, *lastSegmentSize);
}
- iter.Advance(*this, size);
}
- mSegments.erase(mSegments.begin() + toRemoveStart, mSegments.begin() + toRemoveEnd);
- mSize -= movedSize;
- aIter.mSegment = iter.mSegment - (toRemoveEnd - toRemoveStart);
- aIter.mData = iter.mData;
- aIter.mDataEnd = iter.mDataEnd;
- MOZ_ASSERT(aIter.mDataEnd == mSegments[aIter.mSegment].End());
+ mSize -= aSize;
result.mSize = aSize;
- resultGuard.release();
*aSuccess = true;
return result;
}
diff --git a/mfbt/tests/TestBufferList.cpp b/mfbt/tests/TestBufferList.cpp
index cccaac021..325f8a3aa 100644
--- a/mfbt/tests/TestBufferList.cpp
+++ b/mfbt/tests/TestBufferList.cpp
@@ -245,12 +245,51 @@ int main(void)
BufferList bl3 = bl.Extract(iter, kExtractOverSize, &success);
MOZ_RELEASE_ASSERT(!success);
- MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl, kSmallWrite * 3 - kExtractSize - kExtractStart));
- MOZ_RELEASE_ASSERT(iter.Done());
-
iter = bl2.Iter();
MOZ_RELEASE_ASSERT(iter.AdvanceAcrossSegments(bl2, kExtractSize));
MOZ_RELEASE_ASSERT(iter.Done());
+ BufferList bl4(8, 8, 8);
+ bl4.WriteBytes("abcd1234", 8);
+ iter = bl4.Iter();
+ iter.Advance(bl4, 8);
+
+ BufferList bl5 = bl4.Extract(iter, kExtractSize, &success);
+ MOZ_RELEASE_ASSERT(!success);
+
+ BufferList bl6(0, 0, 16);
+ bl6.WriteBytes("abcdefgh12345678", 16);
+ bl6.WriteBytes("ijklmnop87654321", 16);
+ iter = bl6.Iter();
+ iter.Advance(bl6, 8);
+ BufferList bl7 = bl6.Extract(iter, 16, &success);
+ MOZ_RELEASE_ASSERT(success);
+ char data[16];
+ MOZ_RELEASE_ASSERT(bl6.ReadBytes(iter, data, 8));
+ MOZ_RELEASE_ASSERT(memcmp(data, "87654321", 8) == 0);
+ iter = bl7.Iter();
+ MOZ_RELEASE_ASSERT(bl7.ReadBytes(iter, data, 16));
+ MOZ_RELEASE_ASSERT(memcmp(data, "12345678ijklmnop", 16) == 0);
+
+ BufferList bl8(0, 0, 16);
+ bl8.WriteBytes("abcdefgh12345678", 16);
+ iter = bl8.Iter();
+ BufferList bl9 = bl8.Extract(iter, 8, &success);
+ MOZ_RELEASE_ASSERT(success);
+ MOZ_RELEASE_ASSERT(bl9.Size() == 8);
+ MOZ_RELEASE_ASSERT(!iter.Done());
+
+ BufferList bl10(0, 0, 8);
+ bl10.WriteBytes("abcdefgh", 8);
+ bl10.WriteBytes("12345678", 8);
+ iter = bl10.Iter();
+ BufferList bl11 = bl10.Extract(iter, 16, &success);
+ MOZ_RELEASE_ASSERT(success);
+ MOZ_RELEASE_ASSERT(bl11.Size() == 16);
+ MOZ_RELEASE_ASSERT(iter.Done());
+ iter = bl11.Iter();
+ MOZ_RELEASE_ASSERT(bl11.ReadBytes(iter, data, 16));
+ MOZ_RELEASE_ASSERT(memcmp(data, "abcdefgh12345678", 16) == 0);
+
return 0;
}
diff --git a/netwerk/sctp/src/netinet/sctp_auth.c b/netwerk/sctp/src/netinet/sctp_auth.c
index 50432ad8a..ee5ca36ce 100755
--- a/netwerk/sctp/src/netinet/sctp_auth.c
+++ b/netwerk/sctp/src/netinet/sctp_auth.c
@@ -1525,6 +1525,8 @@ sctp_auth_get_cookie_params(struct sctp_tcb *stcb, struct mbuf *m,
if (p_random != NULL) {
keylen = sizeof(*p_random) + random_len;
bcopy(p_random, new_key->key, keylen);
+ } else {
+ keylen = 0;
}
/* append in the AUTH chunks */
if (chunks != NULL) {
diff --git a/netwerk/sctp/src/netinet/sctp_pcb.c b/netwerk/sctp/src/netinet/sctp_pcb.c
index 297097025..58c164f50 100755
--- a/netwerk/sctp/src/netinet/sctp_pcb.c
+++ b/netwerk/sctp/src/netinet/sctp_pcb.c
@@ -7688,6 +7688,8 @@ sctp_load_addresses_from_init(struct sctp_tcb *stcb, struct mbuf *m,
if (p_random != NULL) {
keylen = sizeof(*p_random) + random_len;
bcopy(p_random, new_key->key, keylen);
+ } else {
+ keylen = 0;
}
/* append in the AUTH chunks */
if (chunks != NULL) {
diff --git a/security/nss/lib/pk11wrap/pk11merge.c b/security/nss/lib/pk11wrap/pk11merge.c
index b2101b819..c6125b6e6 100644
--- a/security/nss/lib/pk11wrap/pk11merge.c
+++ b/security/nss/lib/pk11wrap/pk11merge.c
@@ -74,7 +74,7 @@ pk11_copyAttributes(PLArenaPool *arena,
return SECFailure;
}
/* remove the unknown attributes. If we don't have enough attributes
- * PK11_CreateNewObject() will fail */
+ * PK11_CreateNewObject() will fail */
for (i = 0, j = 0; i < copyTemplateCount; i++) {
if (copyTemplate[i].ulValueLen != -1) {
newTemplate[j] = copyTemplate[i];
@@ -88,6 +88,7 @@ pk11_copyAttributes(PLArenaPool *arena,
}
if (crv != CKR_OK) {
PORT_SetError(PK11_MapError(crv));
+ PORT_Free(newTemplate);
return SECFailure;
}
if (targetID == CK_INVALID_HANDLE) {
@@ -100,7 +101,7 @@ pk11_copyAttributes(PLArenaPool *arena,
copyTemplate, copyTemplateCount);
}
if (newTemplate) {
- free(newTemplate);
+ PORT_Free(newTemplate);
}
return rv;
}
diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp
index e43aea926..40f9ead79 100644
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -9,6 +9,7 @@
#include "mozilla/ArrayUtils.h"
#include "mozilla/Attributes.h"
+#include "mozilla/FilePreferences.h"
#include "mozilla/ChaosMode.h"
#include "mozilla/IOInterposer.h"
#include "mozilla/Likely.h"
@@ -3740,6 +3741,10 @@ XREMain::XRE_mainRun()
mDirProvider.DoStartup();
+ // As FilePreferences need the profile directory, we must initialize right here.
+ mozilla::FilePreferences::InitDirectoriesWhitelist();
+ mozilla::FilePreferences::InitPrefs();
+
OverrideDefaultLocaleIfNeeded();
appStartup->GetShuttingDown(&mShuttingDown);
diff --git a/toolkit/xre/nsEmbedFunctions.cpp b/toolkit/xre/nsEmbedFunctions.cpp
index 1498b0d17..3757dec2f 100644
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -52,6 +52,7 @@
#include "base/process_util.h"
#include "chrome/common/child_process.h"
+#include "mozilla/FilePreferences.h"
#include "mozilla/ipc/BrowserProcessSubThread.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/ipc/IOThreadChild.h"
@@ -546,6 +547,9 @@ XRE_InitChildProcess(int aArgc,
::SetProcessShutdownParameters(0x280 - 1, SHUTDOWN_NORETRY);
#endif
+ mozilla::FilePreferences::InitDirectoriesWhitelist();
+ mozilla::FilePreferences::InitPrefs();
+
OverrideDefaultLocaleIfNeeded();
// Run the UI event loop on the main thread.
diff --git a/xpcom/io/FilePreferences.cpp b/xpcom/io/FilePreferences.cpp
new file mode 100644
index 000000000..ef942beb2
--- /dev/null
+++ b/xpcom/io/FilePreferences.cpp
@@ -0,0 +1,272 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "FilePreferences.h"
+
+#include "mozilla/Preferences.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace FilePreferences {
+
+static bool sBlockUNCPaths = false;
+typedef nsTArray<nsString> Paths;
+
+static Paths& PathArray()
+{
+ static Paths sPaths;
+ return sPaths;
+}
+
+static void AllowDirectory(char const* directory)
+{
+ nsCOMPtr<nsIFile> file;
+ NS_GetSpecialDirectory(directory, getter_AddRefs(file));
+ if (!file) {
+ return;
+ }
+
+ nsString path;
+ if (NS_FAILED(file->GetTarget(path))) {
+ return;
+ }
+
+ // The whitelist makes sense only for UNC paths, because this code is used
+ // to block only UNC paths, hence, no need to add non-UNC directories here
+ // as those would never pass the check.
+ if (!StringBeginsWith(path, NS_LITERAL_STRING("\\\\"))) {
+ return;
+ }
+
+ if (!PathArray().Contains(path)) {
+ PathArray().AppendElement(path);
+ }
+}
+
+void InitPrefs()
+{
+ sBlockUNCPaths = Preferences::GetBool("network.file.disable_unc_paths", false);
+}
+
+void InitDirectoriesWhitelist()
+{
+ // NS_GRE_DIR is the installation path where the binary resides.
+ AllowDirectory(NS_GRE_DIR);
+ // NS_APP_USER_PROFILE_50_DIR and NS_APP_USER_PROFILE_LOCAL_50_DIR are the two
+ // parts of the profile we store permanent and local-specific data.
+ AllowDirectory(NS_APP_USER_PROFILE_50_DIR);
+ AllowDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR);
+}
+
+namespace { // anon
+
+class Normalizer
+{
+public:
+ Normalizer(const nsAString& aFilePath, const char16_t aSeparator);
+ bool Get(nsAString& aNormalizedFilePath);
+
+private:
+ bool ConsumeItem();
+ bool ConsumeSeparator();
+ bool IsEOF() { return mFilePathCursor == mFilePathEnd; }
+
+ bool ConsumeName();
+ bool CheckParentDir();
+ bool CheckCurrentDir();
+
+ nsString::const_char_iterator mFilePathCursor;
+ nsString::const_char_iterator mFilePathEnd;
+
+ nsDependentSubstring mItem;
+ char16_t const mSeparator;
+ nsTArray<nsDependentSubstring> mStack;
+};
+
+Normalizer::Normalizer(const nsAString& aFilePath, const char16_t aSeparator)
+ : mFilePathCursor(aFilePath.BeginReading())
+ , mFilePathEnd(aFilePath.EndReading())
+ , mSeparator(aSeparator)
+{
+}
+
+bool Normalizer::ConsumeItem()
+{
+ if (IsEOF()) {
+ return false;
+ }
+
+ nsString::const_char_iterator nameBegin = mFilePathCursor;
+ while (mFilePathCursor != mFilePathEnd) {
+ if (*mFilePathCursor == mSeparator) {
+ break; // don't include the separator
+ }
+ ++mFilePathCursor;
+ }
+
+ mItem.Rebind(nameBegin, mFilePathCursor);
+ return true;
+}
+
+bool Normalizer::ConsumeSeparator()
+{
+ if (IsEOF()) {
+ return false;
+ }
+
+ if (*mFilePathCursor != mSeparator) {
+ return false;
+ }
+
+ ++mFilePathCursor;
+ return true;
+}
+
+bool Normalizer::Get(nsAString& aNormalizedFilePath)
+{
+ aNormalizedFilePath.Truncate();
+
+ if (IsEOF()) {
+ return true;
+ }
+ if (ConsumeSeparator()) {
+ aNormalizedFilePath.Append(mSeparator);
+ }
+
+ if (IsEOF()) {
+ return true;
+ }
+ if (ConsumeSeparator()) {
+ aNormalizedFilePath.Append(mSeparator);
+ }
+
+ while (!IsEOF()) {
+ if (!ConsumeName()) {
+ return false;
+ }
+ }
+
+ for (auto const& name : mStack) {
+ aNormalizedFilePath.Append(name);
+ }
+
+ return true;
+}
+
+bool Normalizer::ConsumeName()
+{
+ if (!ConsumeItem()) {
+ return true;
+ }
+
+ if (CheckCurrentDir()) {
+ return true;
+ }
+
+ if (CheckParentDir()) {
+ if (!mStack.Length()) {
+ // This means there are more \.. than valid names
+ return false;
+ }
+
+ mStack.RemoveElementAt(mStack.Length() - 1);
+ return true;
+ }
+
+ if (mItem.IsEmpty()) {
+ // this means an empty name (a lone slash), which is illegal
+ return false;
+ }
+
+ if (ConsumeSeparator()) {
+ mItem.Rebind(mItem.BeginReading(), mFilePathCursor);
+ }
+ mStack.AppendElement(mItem);
+
+ return true;
+}
+
+bool Normalizer::CheckCurrentDir()
+{
+ if (mItem == NS_LITERAL_STRING(".")) {
+ ConsumeSeparator();
+ // EOF is acceptable
+ return true;
+ }
+
+ return false;
+}
+
+bool Normalizer::CheckParentDir()
+{
+ if (mItem == NS_LITERAL_STRING("..")) {
+ ConsumeSeparator();
+ // EOF is acceptable
+ return true;
+ }
+
+ return false;
+}
+
+} // anon
+
+bool IsBlockedUNCPath(const nsAString& aFilePath)
+{
+ if (!sBlockUNCPaths) {
+ return false;
+ }
+
+ if (!StringBeginsWith(aFilePath, NS_LITERAL_STRING("\\\\"))) {
+ return false;
+ }
+
+ nsAutoString normalized;
+ if (!Normalizer(aFilePath, L'\\').Get(normalized)) {
+ // Broken paths are considered invalid and thus inaccessible
+ return true;
+ }
+
+ for (const auto& allowedPrefix : PathArray()) {
+ if (StringBeginsWith(normalized, allowedPrefix)) {
+ if (normalized.Length() == allowedPrefix.Length()) {
+ return false;
+ }
+ if (normalized[allowedPrefix.Length()] == L'\\') {
+ return false;
+ }
+
+ // When we are here, the path has a form "\\path\prefixevil"
+ // while we have an allowed prefix of "\\path\prefix".
+ // Note that we don't want to add a slash to the end of a prefix
+ // so that opening the directory (no slash at the end) still works.
+ break;
+ }
+ }
+
+ return true;
+}
+
+void testing::SetBlockUNCPaths(bool aBlock)
+{
+ sBlockUNCPaths = aBlock;
+}
+
+void testing::AddDirectoryToWhitelist(nsAString const & aPath)
+{
+ PathArray().AppendElement(aPath);
+}
+
+bool testing::NormalizePath(nsAString const & aPath, nsAString & aNormalized)
+{
+ Normalizer normalizer(aPath, L'\\');
+ return normalizer.Get(aNormalized);
+}
+
+} // ::FilePreferences
+} // ::mozilla
diff --git a/xpcom/io/FilePreferences.h b/xpcom/io/FilePreferences.h
new file mode 100644
index 000000000..fa281f9e6
--- /dev/null
+++ b/xpcom/io/FilePreferences.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#include "nsIObserver.h"
+
+namespace mozilla {
+namespace FilePreferences {
+
+void InitPrefs();
+void InitDirectoriesWhitelist();
+bool IsBlockedUNCPath(const nsAString& aFilePath);
+
+namespace testing {
+
+void SetBlockUNCPaths(bool aBlock);
+void AddDirectoryToWhitelist(nsAString const& aPath);
+bool NormalizePath(nsAString const & aPath, nsAString & aNormalized);
+
+}
+
+} // FilePreferences
+} // mozilla
diff --git a/xpcom/io/moz.build b/xpcom/io/moz.build
index 6f21e0a72..fdefa841b 100644
--- a/xpcom/io/moz.build
+++ b/xpcom/io/moz.build
@@ -84,6 +84,7 @@ EXPORTS += [
EXPORTS.mozilla += [
'Base64.h',
+ 'FilePreferences.h',
'SnappyCompressOutputStream.h',
'SnappyFrameUtils.h',
'SnappyUncompressInputStream.h',
@@ -119,6 +120,10 @@ UNIFIED_SOURCES += [
'SpecialSystemDirectory.cpp',
]
+SOURCES += [
+ 'FilePreferences.cpp',
+]
+
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
SOURCES += [
'CocoaFileUtils.mm',
diff --git a/xpcom/io/nsLocalFileWin.cpp b/xpcom/io/nsLocalFileWin.cpp
index 66e267807..e73e15950 100644
--- a/xpcom/io/nsLocalFileWin.cpp
+++ b/xpcom/io/nsLocalFileWin.cpp
@@ -45,6 +45,7 @@
#include "prproces.h"
#include "prlink.h"
+#include "mozilla/FilePreferences.h"
#include "mozilla/Mutex.h"
#include "SpecialSystemDirectory.h"
@@ -1166,6 +1167,10 @@ nsLocalFile::InitWithPath(const nsAString& aFilePath)
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
}
+ if (FilePreferences::IsBlockedUNCPath(aFilePath)) {
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ }
+
if (secondChar != L':' && (secondChar != L'\\' || firstChar != L'\\')) {
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
}
@@ -1976,6 +1981,10 @@ nsLocalFile::CopySingleFile(nsIFile* aSourceFile, nsIFile* aDestParent,
dwCopyFlags |= COPY_FILE_NO_BUFFERING;
}
+ if (FilePreferences::IsBlockedUNCPath(destPath)) {
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ }
+
if (!move) {
copyOK = ::CopyFileExW(filePath.get(), destPath.get(), nullptr,
nullptr, nullptr, dwCopyFlags);
@@ -3062,6 +3071,7 @@ nsLocalFile::IsExecutable(bool* aResult)
"scf", // Windows explorer command
"scr",
"sct",
+ "settingcontent-ms",
"shb",
"shs",
"url",
diff --git a/xpcom/tests/gtest/TestFilePreferencesWin.cpp b/xpcom/tests/gtest/TestFilePreferencesWin.cpp
new file mode 100644
index 000000000..b7d3a3159
--- /dev/null
+++ b/xpcom/tests/gtest/TestFilePreferencesWin.cpp
@@ -0,0 +1,141 @@
+#include "gtest/gtest.h"
+
+#include "mozilla/FilePreferences.h"
+#include "nsIFile.h"
+#include "nsXPCOMCID.h"
+
+TEST(FilePreferencesWin, Normalization)
+{
+ nsAutoString normalized;
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("foo"), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("foo"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\foo"), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("\\foo"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\foo"), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("\\\\foo"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("foo\\some"), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("foo\\some"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\.\\foo"), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("\\\\foo"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\."), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("\\\\"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\.\\"), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("\\\\"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\.\\."), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("\\\\"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\foo\\bar"), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("\\\\foo\\bar"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\foo\\bar\\"), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("\\\\foo\\bar\\"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\foo\\bar\\."), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("\\\\foo\\bar\\"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\foo\\bar\\.\\"), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("\\\\foo\\bar\\"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\foo\\bar\\..\\"), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("\\\\foo\\"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\foo\\bar\\.."), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("\\\\foo\\"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\foo\\..\\bar\\..\\"), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("\\\\"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\foo\\..\\bar"), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("\\\\bar"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\foo\\bar\\..\\..\\"), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("\\\\"));
+
+ mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\foo\\bar\\.\\..\\.\\..\\"), normalized);
+ ASSERT_TRUE(normalized == NS_LITERAL_STRING("\\\\"));
+
+ bool result;
+
+ result = mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\.."), normalized);
+ ASSERT_FALSE(result);
+
+ result = mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\..\\"), normalized);
+ ASSERT_FALSE(result);
+
+ result = mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\.\\..\\"), normalized);
+ ASSERT_FALSE(result);
+
+ result = mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\foo\\\\bar"), normalized);
+ ASSERT_FALSE(result);
+
+ result = mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\foo\\bar\\..\\..\\..\\..\\"), normalized);
+ ASSERT_FALSE(result);
+
+ result = mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\\\"), normalized);
+ ASSERT_FALSE(result);
+
+ result = mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\.\\\\"), normalized);
+ ASSERT_FALSE(result);
+
+ result = mozilla::FilePreferences::testing::NormalizePath(
+ NS_LITERAL_STRING("\\\\..\\\\"), normalized);
+ ASSERT_FALSE(result);
+}
+
+TEST(FilePreferencesWin, AccessUNC)
+{
+ nsCOMPtr<nsIFile> lf = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+
+ nsresult rv;
+
+ mozilla::FilePreferences::testing::SetBlockUNCPaths(false);
+
+ rv = lf->InitWithPath(NS_LITERAL_STRING("\\\\nice\\..\\evil\\share"));
+ ASSERT_EQ(rv, NS_OK);
+
+ mozilla::FilePreferences::testing::SetBlockUNCPaths(true);
+
+ rv = lf->InitWithPath(NS_LITERAL_STRING("\\\\nice\\..\\evil\\share"));
+ ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
+
+ mozilla::FilePreferences::testing::AddDirectoryToWhitelist(NS_LITERAL_STRING("\\\\nice"));
+
+ rv = lf->InitWithPath(NS_LITERAL_STRING("\\\\nice\\share"));
+ ASSERT_EQ(rv, NS_OK);
+
+ rv = lf->InitWithPath(NS_LITERAL_STRING("\\\\nice\\..\\evil\\share"));
+ ASSERT_EQ(rv, NS_ERROR_FILE_ACCESS_DENIED);
+}
diff --git a/xpcom/tests/gtest/moz.build b/xpcom/tests/gtest/moz.build
index 53836eaef..ac98c2217 100644
--- a/xpcom/tests/gtest/moz.build
+++ b/xpcom/tests/gtest/moz.build
@@ -56,6 +56,11 @@ if CONFIG['MOZ_DEBUG'] and CONFIG['OS_ARCH'] not in ('WINNT') and CONFIG['OS_TAR
'TestDeadlockDetectorScalability.cpp',
]
+if CONFIG['OS_TARGET'] == 'WINNT':
+ UNIFIED_SOURCES += [
+ 'TestFilePreferencesWin.cpp',
+ ]
+
if CONFIG['WRAP_STL_INCLUDES'] and not CONFIG['CLANG_CL']:
UNIFIED_SOURCES += [
'TestSTLWrappers.cpp',