diff options
Diffstat (limited to 'build/win32')
-rw-r--r-- | build/win32/Makefile.in | 14 | ||||
-rw-r--r-- | build/win32/__init__.py | 0 | ||||
-rw-r--r-- | build/win32/autobinscope.py | 75 | ||||
-rw-r--r-- | build/win32/crashinject.cpp | 96 | ||||
-rw-r--r-- | build/win32/crashinjectdll/crashinjectdll.cpp | 38 | ||||
-rw-r--r-- | build/win32/crashinjectdll/crashinjectdll.def | 7 | ||||
-rw-r--r-- | build/win32/crashinjectdll/moz.build | 15 | ||||
-rw-r--r-- | build/win32/dumpenv4python.pl | 19 | ||||
-rw-r--r-- | build/win32/moz.build | 28 | ||||
-rw-r--r-- | build/win32/mozconfig.vs-latest | 1 | ||||
-rw-r--r-- | build/win32/mozconfig.vs2015-win64 | 25 | ||||
-rwxr-xr-x | build/win32/pgomerge.py | 44 | ||||
-rw-r--r-- | build/win32/procmem.py | 48 |
13 files changed, 410 insertions, 0 deletions
diff --git a/build/win32/Makefile.in b/build/win32/Makefile.in new file mode 100644 index 000000000..c96099d8f --- /dev/null +++ b/build/win32/Makefile.in @@ -0,0 +1,14 @@ +# 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 $(topsrcdir)/config/rules.mk + +# run the binscope tool to make sure the binary and all libraries +# are using all available Windows OS-level security mechanisms +# Don't do this in clang-cl since it doesn't support debug information yet. +ifndef CLANG_CL +check:: + $(PYTHON) $(srcdir)/autobinscope.py $(DIST)/bin/$(MOZ_APP_NAME)$(BIN_SUFFIX) $(DIST)/crashreporter-symbols/ + $(PYTHON) $(srcdir)/autobinscope.py $(DIST)/bin/plugin-container.exe $(DIST)/crashreporter-symbols/ +endif diff --git a/build/win32/__init__.py b/build/win32/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/build/win32/__init__.py diff --git a/build/win32/autobinscope.py b/build/win32/autobinscope.py new file mode 100644 index 000000000..fdba68e0c --- /dev/null +++ b/build/win32/autobinscope.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python + +# 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/. + +# run Microsoft's Binscope tool (http://www.microsoft.com/download/en/details.aspx?id=11910) +# against a fresh Windows build. output a 'binscope.log' file with full details +# of the run and appropriate strings to integrate with the buildbots + +# from the docs : "The error code returned when running under the command line is equal +# to the number of failures the tool reported plus the number of errors. BinScope will return +# 0 only if there are no errors or failures." + +# the symbol dir should point to the symbol dir hierarchy created +# via running make buildsymbols in a windows build's objdir + +import sys +import subprocess +import os + +BINSCOPE_OUTPUT_LOGFILE = r".\binscope_xml_output.log" + +# usage +if len(sys.argv) < 3: + print """usage : autobinscope.by path_to_binary path_to_symbols [log_file_path]" + log_file_path is optional, log will be written to .\binscope_xml_output.log by default""" + sys.exit(0) + +binary_path = sys.argv[1] +symbol_path = sys.argv[2] + +if len(sys.argv) == 4: + log_file_path = sys.argv[3] +else: + log_file_path = BINSCOPE_OUTPUT_LOGFILE + +# execute binscope against the binary, using the BINSCOPE environment +# variable as the path to binscope.exe +try: + binscope_path = os.environ['BINSCOPE'] +except KeyError: + print "BINSCOPE environment variable is not set, can't check DEP/ASLR etc. status." + sys.exit(0) + +try: + proc = subprocess.Popen([binscope_path, "/target", binary_path, + "/output", log_file_path, "/sympath", symbol_path, + "/c", "ATLVersionCheck", "/c", "ATLVulnCheck", "/c", "SharedSectionCheck", "/c", "APTCACheck", "/c", "NXCheck", + "/c", "GSCheck", "/c", "GSFriendlyInitCheck", + "/c", "CompilerVersionCheck", "/c", "SafeSEHCheck", "/c", "SNCheck", + "/c", "DBCheck"], stdout=subprocess.PIPE) + +except WindowsError, (errno, strerror): + if errno != 2 and errno != 3: + print "Unexpected error ! \nError " + str(errno) + " : " + strerror + "\nExiting !\n" + sys.exit(0) + else: + print "Could not locate binscope at location : %s\n" % binscope_path + print "Binscope wasn't installed or the BINSCOPE env variable wasn't set correctly, skipping this check and exiting..." + sys.exit(0) + +proc.wait() + +output = proc.communicate()[0] + +# is this a PASS or a FAIL ? +if proc.returncode != 0: + print "Error count: %d" % proc.returncode + print "TEST-UNEXPECTED-FAIL | autobinscope.py | %s is missing a needed Windows protection, such as /GS or ASLR" % binary_path + logfile = open(log_file_path, "r") + for line in logfile: + print(line), +else: + print "TEST-PASS | autobinscope.py | %s succeeded" % binary_path diff --git a/build/win32/crashinject.cpp b/build/win32/crashinject.cpp new file mode 100644 index 000000000..472e15a12 --- /dev/null +++ b/build/win32/crashinject.cpp @@ -0,0 +1,96 @@ +/* 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/. */ + +/* + * Given a PID, this program attempts to inject a DLL into the process + * with that PID. The DLL it attempts to inject, "crashinjectdll.dll", + * must exist alongside this exe. The DLL will then crash the process. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <windows.h> + +int main(int argc, char** argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: crashinject <PID>\n"); + return 1; + } + + int pid = atoi(argv[1]); + if (pid <= 0) { + fprintf(stderr, "Usage: crashinject <PID>\n"); + return 1; + } + + // find our DLL to inject + wchar_t filename[_MAX_PATH]; + if (GetModuleFileNameW(nullptr, filename, + sizeof(filename) / sizeof(wchar_t)) == 0) + return 1; + + wchar_t* slash = wcsrchr(filename, L'\\'); + if (slash == nullptr) + return 1; + + slash++; + wcscpy(slash, L"crashinjectdll.dll"); + + // now find our target process + HANDLE targetProc = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION, + FALSE, + pid); + if (targetProc == nullptr) { + fprintf(stderr, "Error %d opening target process\n", GetLastError()); + return 1; + } + + /* + * This is sort of insane, but we're implementing a technique described here: + * http://www.codeproject.com/KB/threads/winspy.aspx#section_2 + * + * The gist is to use CreateRemoteThread to create a thread in the other + * process, but cheat and make the thread function kernel32!LoadLibrary, + * so that the only remote data we have to pass to the other process + * is the path to the library we want to load. The library we're loading + * will then do its dirty work inside the other process. + */ + HMODULE hKernel32 = GetModuleHandleW(L"Kernel32"); + // allocate some memory to hold the path in the remote process + void* pLibRemote = VirtualAllocEx(targetProc, nullptr, sizeof(filename), + MEM_COMMIT, PAGE_READWRITE); + if (pLibRemote == nullptr) { + fprintf(stderr, "Error %d in VirtualAllocEx\n", GetLastError()); + CloseHandle(targetProc); + return 1; + } + + if (!WriteProcessMemory(targetProc, pLibRemote, (void*)filename, + sizeof(filename), nullptr)) { + fprintf(stderr, "Error %d in WriteProcessMemory\n", GetLastError()); + VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); + CloseHandle(targetProc); + return 1; + } + // Now create a thread in the target process that will load our DLL + HANDLE hThread = CreateRemoteThread( + targetProc, nullptr, 0, + (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, + "LoadLibraryW"), + pLibRemote, 0, nullptr); + if (hThread == nullptr) { + fprintf(stderr, "Error %d in CreateRemoteThread\n", GetLastError()); + VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); + CloseHandle(targetProc); + return 1; + } + WaitForSingleObject(hThread, INFINITE); + // Cleanup, not that it's going to matter at this point + CloseHandle(hThread); + VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); + CloseHandle(targetProc); + + return 0; +} diff --git a/build/win32/crashinjectdll/crashinjectdll.cpp b/build/win32/crashinjectdll/crashinjectdll.cpp new file mode 100644 index 000000000..09ab9fdba --- /dev/null +++ b/build/win32/crashinjectdll/crashinjectdll.cpp @@ -0,0 +1,38 @@ +/* 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 <stdio.h> +#include <windows.h> + +// make sure we only ever spawn one thread +DWORD tid = -1; + +DWORD WINAPI CrashingThread( + LPVOID lpParameter +) +{ + // not a very friendly DLL + volatile int* x = (int *)0x0; + *x = 1; + return 0; +} + +BOOL WINAPI DllMain( + HANDLE hinstDLL, + DWORD dwReason, + LPVOID lpvReserved +) +{ + if (tid == -1) + // we have to crash on another thread because LoadLibrary() will + // catch memory access errors and return failure to the calling process + CreateThread( + nullptr, // default security attributes + 0, // use default stack size + CrashingThread, // thread function name + nullptr, // argument to thread function + 0, // use default creation flags + &tid); // returns the thread identifier + return TRUE; +} diff --git a/build/win32/crashinjectdll/crashinjectdll.def b/build/win32/crashinjectdll/crashinjectdll.def new file mode 100644 index 000000000..d1ea5602d --- /dev/null +++ b/build/win32/crashinjectdll/crashinjectdll.def @@ -0,0 +1,7 @@ +;+# 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/. + +LIBRARY crashinjectdll +EXPORTS + DllMain diff --git a/build/win32/crashinjectdll/moz.build b/build/win32/crashinjectdll/moz.build new file mode 100644 index 000000000..06817aa96 --- /dev/null +++ b/build/win32/crashinjectdll/moz.build @@ -0,0 +1,15 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +SOURCES += [ + 'crashinjectdll.cpp', +] + +SharedLibrary('crashinjectdll') + +DEFFILE = SRCDIR + '/crashinjectdll.def' + +USE_STATIC_LIBS = True diff --git a/build/win32/dumpenv4python.pl b/build/win32/dumpenv4python.pl new file mode 100644 index 000000000..34a1135a1 --- /dev/null +++ b/build/win32/dumpenv4python.pl @@ -0,0 +1,19 @@ +# 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/. + +# See build/autoconf/hooks.m4 + +use Data::Dumper; + +$Data::Dumper::Terse = 1; +$Data::Dumper::Indent = 0; + +# We can't use perl hashes because Mozilla-Build's perl is 5.6, and perl +# 5.6's Data::Dumper doesn't have Pair to change ' => ' into ' : '. +@data = ( + ['env', [map { [$_, $ENV{$_}] } keys %ENV]], + ['args', \@ARGV], +); + +print Dumper \@data; diff --git a/build/win32/moz.build b/build/win32/moz.build new file mode 100644 index 000000000..cd5110ac6 --- /dev/null +++ b/build/win32/moz.build @@ -0,0 +1,28 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +TEST_DIRS += ['crashinjectdll'] + +if CONFIG['ENABLE_TESTS']: + Program('crashinject') + SOURCES += [ + 'crashinject.cpp', + ] + USE_STATIC_LIBS = True + +NO_PGO = True + +if CONFIG['WIN32_REDIST_DIR'] and CONFIG['COMPILE_ENVIRONMENT']: + for f in ['MSVC_C_RUNTIME_DLL', 'MSVC_CXX_RUNTIME_DLL']: + FINAL_TARGET_FILES += [ + '%%%s/%s' % (CONFIG['WIN32_REDIST_DIR'], CONFIG[f]) + ] + +if CONFIG['WIN_UCRT_REDIST_DIR'] and CONFIG['COMPILE_ENVIRONMENT']: + for f in ['api-ms-win-*.dll', 'ucrtbase.dll']: + FINAL_TARGET_FILES += [ + '%%%s/%s' % (CONFIG['WIN_UCRT_REDIST_DIR'], f) + ] diff --git a/build/win32/mozconfig.vs-latest b/build/win32/mozconfig.vs-latest new file mode 100644 index 000000000..9c8726a8d --- /dev/null +++ b/build/win32/mozconfig.vs-latest @@ -0,0 +1 @@ +. $topsrcdir/build/win32/mozconfig.vs2015-win64 diff --git a/build/win32/mozconfig.vs2015-win64 b/build/win32/mozconfig.vs2015-win64 new file mode 100644 index 000000000..b81afa681 --- /dev/null +++ b/build/win32/mozconfig.vs2015-win64 @@ -0,0 +1,25 @@ +if [ -z "${VSPATH}" ]; then + TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir} + VSPATH="$(cd ${TOOLTOOL_DIR} && pwd)/vs2015u3" +fi + +VSWINPATH="$(cd ${VSPATH} && pwd -W)" + +export WINDOWSSDKDIR="${VSWINPATH}/SDK" +export WIN32_REDIST_DIR="${VSPATH}/VC/redist/x86/Microsoft.VC140.CRT" +export WIN_UCRT_REDIST_DIR="${VSPATH}/SDK/Redist/ucrt/DLLs/x86" + +export PATH="${VSPATH}/VC/bin/amd64_x86:${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x86:${VSPATH}/SDK/bin/x64:${VSPATH}/DIA SDK/bin:${PATH}" +export PATH="${VSPATH}/VC/redist/x86/Microsoft.VC140.CRT:${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x86:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${PATH}" + +export INCLUDE="${VSPATH}/VC/include:${VSPATH}/VC/atlmfc/include:${VSPATH}/SDK/Include/10.0.14393.0/ucrt:${VSPATH}/SDK/Include/10.0.14393.0/shared:${VSPATH}/SDK/Include/10.0.14393.0/um:${VSPATH}/SDK/Include/10.0.14393.0/winrt:${VSPATH}/DIA SDK/include" +export LIB="${VSPATH}/VC/lib:${VSPATH}/VC/atlmfc/lib:${VSPATH}/SDK/lib/10.0.14393.0/ucrt/x86:${VSPATH}/SDK/lib/10.0.14393.0/um/x86:${VSPATH}/DIA SDK/lib" + +. $topsrcdir/build/mozconfig.vs-common + +mk_export_correct_style WINDOWSSDKDIR +mk_export_correct_style INCLUDE +mk_export_correct_style LIB +mk_export_correct_style PATH +mk_export_correct_style WIN32_REDIST_DIR +mk_export_correct_style WIN_UCRT_REDIST_DIR diff --git a/build/win32/pgomerge.py b/build/win32/pgomerge.py new file mode 100755 index 000000000..313d66870 --- /dev/null +++ b/build/win32/pgomerge.py @@ -0,0 +1,44 @@ +#!/usr/bin/python +# 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/. + +# Usage: pgomerge.py <binary basename> <dist/bin> +# Gathers .pgc files from dist/bin and merges them into +# $PWD/$basename.pgd using pgomgr, then deletes them. +# No errors if any of these files don't exist. + +import sys, os, os.path, subprocess +if not sys.platform == "win32": + raise Exception("This script was only meant for Windows.") + +def MergePGOFiles(basename, pgddir, pgcdir): + """Merge pgc files produced from an instrumented binary + into the pgd file for the second pass of profile-guided optimization + with MSVC. |basename| is the name of the DLL or EXE without the + extension. |pgddir| is the path that contains <basename>.pgd + (should be the objdir it was built in). |pgcdir| is the path + containing basename!N.pgc files, which is probably dist/bin. + Calls pgomgr to merge each pgc file into the pgd, then deletes + the pgc files.""" + if not os.path.isdir(pgddir) or not os.path.isdir(pgcdir): + return + pgdfile = os.path.abspath(os.path.join(pgddir, basename + ".pgd")) + if not os.path.isfile(pgdfile): + return + for file in os.listdir(pgcdir): + if file.startswith(basename+"!") and file.endswith(".pgc"): + try: + pgcfile = os.path.normpath(os.path.join(pgcdir, file)) + subprocess.call(['pgomgr', '-merge', + pgcfile, + pgdfile]) + os.remove(pgcfile) + except OSError: + pass + +if __name__ == '__main__': + if len(sys.argv) != 3: + print >>sys.stderr, "Usage: pgomerge.py <binary basename> <dist/bin>" + sys.exit(1) + MergePGOFiles(sys.argv[1], os.getcwd(), sys.argv[2]) diff --git a/build/win32/procmem.py b/build/win32/procmem.py new file mode 100644 index 000000000..8376d07ea --- /dev/null +++ b/build/win32/procmem.py @@ -0,0 +1,48 @@ +# 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/. + +import os, sys, ctypes, ctypes.wintypes + +class VM_COUNTERS(ctypes.Structure): + _fields_ = [("PeakVirtualSize", ctypes.wintypes.ULONG), + ("VirtualSize", ctypes.wintypes.ULONG), + ("PageFaultCount", ctypes.wintypes.ULONG), + ("PeakWorkingSetSize", ctypes.wintypes.ULONG), + ("WorkingSetSize", ctypes.wintypes.ULONG), + ("QuotaPeakPagedPoolUsage", ctypes.wintypes.ULONG), + ("QuotaPagedPoolUsage", ctypes.wintypes.ULONG), + ("QuotaPeakNonPagedPoolUsage", ctypes.wintypes.ULONG), + ("QuotaNonPagedPoolUsage", ctypes.wintypes.ULONG), + ("PagefileUsage", ctypes.wintypes.ULONG), + ("PeakPagefileUsage", ctypes.wintypes.ULONG) + ] + +def get_vmsize(handle): + """ + Return (peak_virtual_size, virtual_size) for the process |handle|. + """ + ProcessVmCounters = 3 + vmc = VM_COUNTERS() + if ctypes.windll.ntdll.NtQueryInformationProcess(int(handle), + ProcessVmCounters, + ctypes.byref(vmc), + ctypes.sizeof(vmc), + None) == 0: + return (vmc.PeakVirtualSize, vmc.VirtualSize) + + return (-1, -1) + +if __name__ == '__main__': + PROCESS_QUERY_INFORMATION = 0x0400 + for pid in sys.argv[1:]: + handle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, + 0, # no inherit + int(pid)) + if handle: + print "Process %s:" % pid + vsize, peak_vsize = get_vmsize(handle) + print "peak vsize: %d" % peak_vsize + ctypes.windll.kernel32.CloseHandle(handle) + else: + print "Couldn't open process %s" % pid |