diff --git a/js/xpconnect/shell/xpcshell.cpp b/js/xpconnect/shell/xpcshell.cpp index 7fbd8bf3b574..1bbc2cee3a0a 100644 --- a/js/xpconnect/shell/xpcshell.cpp +++ b/js/xpconnect/shell/xpcshell.cpp @@ -1760,6 +1760,10 @@ main(int argc, char **argv, char **envp) setbuf(stdout, 0); #endif +#ifdef XRE_HAS_DLL_BLOCKLIST + XRE_SetupDllBlocklist(); +#endif + gErrFile = stderr; gOutFile = stdout; gInFile = stdin; diff --git a/toolkit/xre/nsWindowsDllBlocklist.cpp b/toolkit/xre/nsWindowsDllBlocklist.cpp index 77fe237bb61d..ac64a52f6252 100644 --- a/toolkit/xre/nsWindowsDllBlocklist.cpp +++ b/toolkit/xre/nsWindowsDllBlocklist.cpp @@ -50,6 +50,7 @@ #endif #include "nsAutoPtr.h" +#include "nsThreadUtils.h" #include "prlog.h" @@ -151,12 +152,76 @@ static DllBlockInfo sWindowsDllBlocklist[] = { // define this for very verbose dll load debug spew #undef DEBUG_very_verbose +extern bool gInXPCOMLoadOnMainThread; + namespace { typedef NTSTATUS (NTAPI *LdrLoadDll_func) (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle); static LdrLoadDll_func stub_LdrLoadDll = 0; +template +struct RVAMap { + RVAMap(HANDLE map, DWORD offset) { + SYSTEM_INFO info; + GetSystemInfo(&info); + + DWORD alignedOffset = (offset / info.dwAllocationGranularity) * + info.dwAllocationGranularity; + + NS_ASSERTION(offset - alignedOffset < info.dwAllocationGranularity, "Wtf"); + + mRealView = ::MapViewOfFile(map, FILE_MAP_READ, 0, alignedOffset, + sizeof(T) + (offset - alignedOffset)); + + mMappedView = reinterpret_cast((char*)mRealView + (offset - alignedOffset)); + } + ~RVAMap() { + if (mRealView) { + ::UnmapViewOfFile(mRealView); + } + } + operator const T*() const { return mMappedView; } + const T* operator->() const { return mMappedView; } +private: + const T* mMappedView; + void* mRealView; +}; + +bool +CheckASLR(const wchar_t* path) +{ + bool retval = false; + + HANDLE file = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + NULL); + if (file != INVALID_HANDLE_VALUE) { + HANDLE map = ::CreateFileMappingW(file, NULL, PAGE_READONLY, 0, 0, NULL); + if (map) { + RVAMap peHeader(map, 0); + if (peHeader) { + RVAMap ntHeader(map, peHeader->e_lfanew); + if (ntHeader) { + // If the DLL has no code, permit it regardless of ASLR status. + if (ntHeader->OptionalHeader.SizeOfCode == 0) { + retval = true; + } + // Check to see if the DLL supports ASLR + else if ((ntHeader->OptionalHeader.DllCharacteristics & + IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0) { + retval = true; + } + } + } + ::CloseHandle(map); + } + ::CloseHandle(file); + } + + return retval; +} + /** * Some versions of Windows call LoadLibraryEx to get the version information * for a DLL, which causes our patched LdrLoadDll implementation to re-enter @@ -225,6 +290,29 @@ patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileNam int len = moduleFileName->Length / 2; wchar_t *fname = moduleFileName->Buffer; + // In Windows 8, the first parameter seems to be used for more than just the + // path name. For example, its numerical value can be 1. Passing a non-valid + // pointer to SearchPathW will cause a crash, so we need to check to see if we + // are handed a valid pointer, and otherwise just pass NULL to SearchPathW. + PWCHAR sanitizedFilePath = (intptr_t(filePath) < 1024) ? NULL : filePath; + + // figure out the length of the string that we need + DWORD pathlen = SearchPathW(sanitizedFilePath, fname, L".dll", 0, NULL, NULL); + if (pathlen == 0) { + // uh, we couldn't find the DLL at all, so... + printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName); + return STATUS_DLL_NOT_FOUND; + } + + nsAutoArrayPtr full_fname(new wchar_t[pathlen+1]); + if (!full_fname) { + // couldn't allocate memory? + return STATUS_DLL_NOT_FOUND; + } + + // now actually grab it + SearchPathW(sanitizedFilePath, fname, L".dll", pathlen+1, full_fname, NULL); + // The filename isn't guaranteed to be null terminated, but in practice // it always will be; ensure that this is so, and bail if not. // This is done instead of the more robust approach because of bug 527122, @@ -305,29 +393,6 @@ patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileNam goto continue_loading; } - // In Windows 8, the first parameter seems to be used for more than just the - // path name. For example, its numerical value can be 1. Passing a non-valid - // pointer to SearchPathW will cause a crash, so we need to check to see if we - // are handed a valid pointer, and otherwise just pass NULL to SearchPathW. - PWCHAR sanitizedFilePath = (intptr_t(filePath) < 1024) ? NULL : filePath; - - // figure out the length of the string that we need - DWORD pathlen = SearchPathW(sanitizedFilePath, fname, L".dll", 0, NULL, NULL); - if (pathlen == 0) { - // uh, we couldn't find the DLL at all, so... - printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName); - return STATUS_DLL_NOT_FOUND; - } - - wchar_t *full_fname = (wchar_t*) malloc(sizeof(wchar_t)*(pathlen+1)); - if (!full_fname) { - // couldn't allocate memory? - return STATUS_DLL_NOT_FOUND; - } - - // now actually grab it - SearchPathW(sanitizedFilePath, fname, L".dll", pathlen+1, full_fname, NULL); - DWORD zero; DWORD infoSize = GetFileVersionInfoSizeW(full_fname, &zero); @@ -351,8 +416,6 @@ patched_LdrLoadDll (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileNam load_ok = true; } } - - free(full_fname); } if (!load_ok) { @@ -368,6 +431,14 @@ continue_loading: NS_SetHasLoadedNewDLLs(); + if (gInXPCOMLoadOnMainThread && NS_IsMainThread()) { + // Check to ensure that the DLL has ASLR. + if (!CheckASLR(full_fname)) { + printf_stderr("LdrLoadDll: Blocking load of '%s'. XPCOM components must support ASLR.\n", dllName); + return STATUS_DLL_NOT_FOUND; + } + } + return stub_LdrLoadDll(filePath, flags, moduleFileName, handle); } diff --git a/xpcom/components/nsNativeComponentLoader.cpp b/xpcom/components/nsNativeComponentLoader.cpp index beb85dfafe00..b8c0b7abdd7f 100644 --- a/xpcom/components/nsNativeComponentLoader.cpp +++ b/xpcom/components/nsNativeComponentLoader.cpp @@ -87,6 +87,8 @@ using namespace mozilla; static PRLogModuleInfo *nsNativeModuleLoaderLog = PR_NewLogModule("nsNativeModuleLoader"); +bool gInXPCOMLoadOnMainThread = false; + #define LOG(level, args) PR_LOG(nsNativeModuleLoaderLog, level, args) NS_IMPL_QUERY_INTERFACE1(nsNativeModuleLoader, @@ -167,7 +169,9 @@ nsNativeModuleLoader::LoadModule(FileLocation &aFile) // We haven't loaded this module before + gInXPCOMLoadOnMainThread = true; rv = file->Load(&data.library); + gInXPCOMLoadOnMainThread = false; if (NS_FAILED(rv)) { char errorMsg[1024] = ""; diff --git a/xpcom/tests/Makefile.in b/xpcom/tests/Makefile.in index 35c53170fd4a..117bd526e256 100644 --- a/xpcom/tests/Makefile.in +++ b/xpcom/tests/Makefile.in @@ -45,7 +45,12 @@ include $(DEPTH)/config/autoconf.mk MODULE = xpcom -DIRS = external component bug656331_component +DIRS = \ + external \ + component \ + bug656331_component \ + component_no_aslr \ + $(NULL) ifeq ($(OS_ARCH),WINNT) DIRS += windows diff --git a/xpcom/tests/component_no_aslr/Makefile.in b/xpcom/tests/component_no_aslr/Makefile.in new file mode 100644 index 000000000000..122616350ddd --- /dev/null +++ b/xpcom/tests/component_no_aslr/Makefile.in @@ -0,0 +1,81 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Benjamin Smedberg +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +LIBRARY_NAME = testcompnoaslr +IS_COMPONENT = 1 +CPPSRCS = TestComponent.cpp +NO_DIST_INSTALL = 1 +FORCE_SHARED_LIB = 1 + +include $(topsrcdir)/config/config.mk + +MANIFEST_FILE = testcompnoaslr.manifest + +EXTRA_DSO_LDOPTS = \ + $(DIST)/lib/$(LIB_PREFIX)xpcomglue_s.$(LIB_SUFFIX) \ + $(XPCOM_FROZEN_LDOPTS) \ + $(NSPR_LIBS) \ + $(NULL) + +# Need to link with CoreFoundation on Mac +ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) +EXTRA_DSO_LDOPTS += \ + $(TK_LIBS) \ + $(NULL) +endif + +include $(topsrcdir)/config/rules.mk + +LDFLAGS := $(filter-out -DYNAMICBASE,$(LDFLAGS)) -DYNAMICBASE:NO + +DEFINES += -DLIBRARY_FILENAME="$(SHARED_LIBRARY)" + +unittestlocation = xpcom/tests/unit + +libs:: $(SHARED_LIBRARY) + $(INSTALL) $^ $(testxpcobjdir)/$(unittestlocation) + +libs:: $(MANIFEST_FILE) + $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) $< > $(testxpcobjdir)/$(unittestlocation)/$( + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "mozilla/ModuleUtils.h" + +#define NS_TESTING_CID \ +{ 0x335fb596, 0xe52d, 0x418f, \ + { 0xb0, 0x1c, 0x1b, 0xf1, 0x6c, 0xe5, 0xe7, 0xe4 } } + +NS_DEFINE_NAMED_CID(NS_TESTING_CID); + +static nsresult +DummyConstructorFunc(nsISupports* aOuter, const nsIID& aIID, void** aResult) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +static const mozilla::Module::CIDEntry kTestCIDs[] = { + { &kNS_TESTING_CID, false, NULL, DummyConstructorFunc }, + { NULL } +}; + +static const mozilla::Module kTestModule = { + mozilla::Module::kVersion, + kTestCIDs +}; + +NSMODULE_DEFN(dummy) = &kTestModule; + + diff --git a/xpcom/tests/component_no_aslr/testcompnoaslr.manifest b/xpcom/tests/component_no_aslr/testcompnoaslr.manifest new file mode 100644 index 000000000000..fb1991a56b49 --- /dev/null +++ b/xpcom/tests/component_no_aslr/testcompnoaslr.manifest @@ -0,0 +1,2 @@ +#filter substitution +binary-component @LIBRARY_FILENAME@ diff --git a/xpcom/tests/unit/test_comp_no_aslr.js b/xpcom/tests/unit/test_comp_no_aslr.js new file mode 100644 index 000000000000..b85ad71782af --- /dev/null +++ b/xpcom/tests/unit/test_comp_no_aslr.js @@ -0,0 +1,10 @@ +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +const Cc = Components.classes; +const Ci = Components.interfaces; + +function run_test() { + let manifest = do_get_file('testcompnoaslr.manifest'); + Components.manager.autoRegister(manifest); + do_check_false("{335fb596-e52d-418f-b01c-1bf16ce5e7e4}" in Components.classesByID); +} diff --git a/xpcom/tests/unit/xpcshell.ini b/xpcom/tests/unit/xpcshell.ini index 6de72ee27437..06c522844841 100644 --- a/xpcom/tests/unit/xpcshell.ini +++ b/xpcom/tests/unit/xpcshell.ini @@ -42,3 +42,5 @@ fail-if = os == "android" # Bug 676998: test fails consistently on Android fail-if = os == "android" [test_versioncomparator.js] +[test_comp_no_aslr.js] +fail-if = os != "win"