diff options
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', |