mumble-voip_mumble/src/mumble/os_win.cpp

340 lines
9.0 KiB
C++

// Copyright 2007-2023 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#include "LogEmitter.h"
#include "MumbleApplication.h"
#ifdef _MSC_VER
# include "Utils.h"
#endif
// Must be included before other Windows stuff
#include "win.h"
#include "versionhelpers.h"
#include "Version.h"
#include "Global.h"
#include <cfloat>
#include <cmath>
#include <dbghelp.h>
#include <tlhelp32.h>
#include <wincrypt.h>
#ifdef _MSC_VER
# include <delayimp.h>
#endif
#include <emmintrin.h>
#include <share.h> // For share flags for _wfsopen
#include <shlobj.h>
#include <shobjidl.h>
#include <limits>
extern "C" {
void __cpuid(int a[4], int b);
void mumble_speex_init();
};
#define DUMP_BUFFER_SIZE 1024
static wchar_t wcCrashDumpPath[DUMP_BUFFER_SIZE];
static FILE *fConsole = nullptr;
static wchar_t wcComment[DUMP_BUFFER_SIZE] = L"";
static MINIDUMP_USER_STREAM musComment;
static QSharedPointer< LogEmitter > le;
static int cpuinfo[4];
bool bIsWin7 = false;
bool bIsVistaSP1 = false;
HWND mumble_mw_hwnd = 0;
static void mumbleMessageOutputQString(QtMsgType type, const QString &msg) {
char c;
switch (type) {
case QtDebugMsg:
c = 'D';
break;
case QtWarningMsg:
c = 'W';
break;
case QtFatalMsg:
c = 'F';
break;
default:
c = 'X';
}
QString date = QDateTime::currentDateTime().toString(QLatin1String("yyyy-MM-dd hh:mm:ss.zzz"));
QString fmsg = QString::fromLatin1("<%1>%2 %3").arg(c).arg(date).arg(msg);
fprintf(fConsole, "%s\n", qPrintable(fmsg));
fflush(fConsole);
OutputDebugStringA(qPrintable(fmsg));
le->addLogEntry(fmsg);
if (type == QtFatalMsg) {
::MessageBoxA(nullptr, qPrintable(msg), "Mumble", MB_OK | MB_ICONERROR);
exit(0);
}
}
static void mumbleMessageOutputWithContext(QtMsgType type, const QMessageLogContext &ctx, const QString &msg) {
Q_UNUSED(ctx);
mumbleMessageOutputQString(type, msg);
}
static LONG WINAPI MumbleUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo) {
MINIDUMP_EXCEPTION_INFORMATION i;
i.ThreadId = GetCurrentThreadId();
i.ExceptionPointers = ExceptionInfo;
MINIDUMP_USER_STREAM_INFORMATION musi;
musi.UserStreamCount = 1;
musi.UserStreamArray = &musComment;
HANDLE hMinidump =
CreateFile(wcCrashDumpPath, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hMinidump != INVALID_HANDLE_VALUE) {
if (MiniDumpWriteDump(
GetCurrentProcess(), GetCurrentProcessId(), hMinidump,
static_cast< MINIDUMP_TYPE >(MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithThreadInfo), &i,
&musi, nullptr)) {
FlushFileBuffers(hMinidump);
}
CloseHandle(hMinidump);
}
return EXCEPTION_CONTINUE_SEARCH;
}
static void enableCrashOnCrashes() {
// Makes sure the application actually crashes when one of its callbacks
// called from the kernel crashes.
//
// See http://support.microsoft.com/kb/976038
// http://www.altdevblogaday.com/2012/07/06/when-even-crashing-doesnt-work/
typedef BOOL(WINAPI * tGetPolicy)(LPDWORD lpFlags);
typedef BOOL(WINAPI * tSetPolicy)(DWORD dwFlags);
const DWORD PROCESS_CALLBACK_FILTER_ENABLED = 0x01;
HMODULE kernel32 = LoadLibrary(L"kernel32.dll");
tGetPolicy pGetPolicy = (tGetPolicy) GetProcAddress(kernel32, "GetProcessUserModeExceptionPolicy");
tSetPolicy pSetPolicy = (tSetPolicy) GetProcAddress(kernel32, "SetProcessUserModeExceptionPolicy");
if (pGetPolicy && pSetPolicy) { // Only available as of Vista SP2 / Win7 SP1
DWORD dwFlags;
if (pGetPolicy(&dwFlags)) {
if (!pSetPolicy(dwFlags & ~PROCESS_CALLBACK_FILTER_ENABLED))
qWarning("enableCrashOnCrashes: Failed to set policy");
} else {
qWarning("enableCrashOnCrashes: Failed to get policy");
}
}
}
BOOL SetHeapOptions() {
HMODULE hLib = LoadLibrary(L"kernel32.dll");
if (!hLib)
return FALSE;
typedef BOOL(WINAPI * HSI)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T);
HSI pHsi = (HSI) GetProcAddress(hLib, "HeapSetInformation");
if (!pHsi) {
FreeLibrary(hLib);
return FALSE;
}
#ifndef HeapEnableTerminationOnCorruption
# define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS) 1
#endif
BOOL fRet = (pHsi)(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0) ? TRUE : FALSE;
if (hLib)
FreeLibrary(hLib);
return fRet;
}
// We only support delay-loading on MSVC, not on MinGW.
#ifdef _MSC_VER
FARPROC WINAPI delayHook(unsigned dliNotify, PDelayLoadInfo pdli) {
if (dliNotify != dliNotePreLoadLibrary)
return 0;
size_t length = strlen(pdli->szDll);
if (length < 5)
return 0;
size_t buflen = length + 10;
std::vector< char > filename;
filename.resize(buflen);
strcpy_s(filename.data(), buflen, pdli->szDll);
size_t offset = 0;
if (_stricmp(filename.data() + length - 4, ".dll") == 0)
offset = length - 4;
else
offset = length;
HMODULE hmod = 0;
// SSE?
if (cpuinfo[3] & 0x02000000) {
// SSE2?
if (cpuinfo[3] & 0x04000000) {
// And SSE3?
if (cpuinfo[2] & 0x00000001) {
strcpy_s(filename.data() + offset, 10, ".sse3.dll");
hmod = LoadLibraryA(filename.data());
if (hmod)
return (FARPROC) hmod;
}
strcpy_s(filename.data() + offset, 10, ".sse2.dll");
hmod = LoadLibraryA(filename.data());
if (hmod)
return (FARPROC) hmod;
}
strcpy_s(filename.data() + offset, 10, ".sse.dll");
hmod = LoadLibraryA(filename.data());
if (hmod)
return (FARPROC) hmod;
}
return 0;
}
decltype(__pfnDliNotifyHook2) __pfnDliNotifyHook2 = delayHook;
#endif
void os_init() {
__cpuid(cpuinfo, 1);
#define MMXSSE 0x02800000
if ((cpuinfo[3] & MMXSSE) != MMXSSE) {
::MessageBoxA(nullptr, "Mumble requires a SSE capable processor (Pentium 3 / Ahtlon-XP)", "Mumble",
MB_OK | MB_ICONERROR);
exit(0);
}
bIsWin7 = IsWindows7OrGreater();
bIsVistaSP1 = IsWindowsVistaSP1OrGreater();
#if _MSC_VER == 1800 && defined(_M_X64)
// Disable MSVC 2013's FMA-optimized math routines on Windows
// versions earlier than Windows 8 (6.2).
// There are various issues on OSes that do not support the newer
// instructions.
// See issue mumble-voip/mumble#1615.
if (!IsWindows8OrGreater()) {
_set_FMA3_enable(0);
}
#endif
unsigned int currentControl = 0;
_controlfp_s(&currentControl, _DN_FLUSH, _MCW_DN);
SetHeapOptions();
enableCrashOnCrashes();
mumble_speex_init();
// Make a copy of the global LogEmitter, such that
// os_win.cpp doesn't have to consider the deletion
// of the Global object and its LogEmitter object.
le = Global::get().le;
#ifdef QT_NO_DEBUG
QString console = Global::get().qdBasePath.filePath(QLatin1String("Console.txt"));
fConsole = _wfsopen(console.toStdWString().c_str(), L"a+", _SH_DENYWR);
if (fConsole) {
qInstallMessageHandler(mumbleMessageOutputWithContext);
}
QString hash;
QFile f(qApp->applicationFilePath());
if (!f.open(QIODevice::ReadOnly)) {
qWarning("VersionCheck: Failed to open binary");
} else {
QByteArray a = f.readAll();
if (a.size() > 0) {
QCryptographicHash qch(QCryptographicHash::Sha1);
qch.addData(a);
hash = QLatin1String(qch.result().toHex());
}
}
QString comment = QString::fromLatin1("%1\n%2").arg(Version::getRelease(), hash);
wcscpy_s(wcComment, DUMP_BUFFER_SIZE, comment.toStdWString().c_str());
musComment.Type = CommentStreamW;
musComment.Buffer = wcComment;
musComment.BufferSize = static_cast< ULONG >(wcslen(wcComment) * sizeof(wchar_t));
QString dump = Global::get().qdBasePath.filePath(QLatin1String("mumble.dmp"));
QFileInfo fi(dump);
QDir::root().mkpath(fi.absolutePath());
if (wcscpy_s(wcCrashDumpPath, DUMP_BUFFER_SIZE, dump.toStdWString().c_str()) == 0)
SetUnhandledExceptionFilter(MumbleUnhandledExceptionFilter);
#endif
Global::get().qdBasePath.mkpath(QLatin1String("Snapshots"));
if (bIsWin7)
SetCurrentProcessExplicitAppUserModelID(L"net.sourceforge.mumble.Mumble");
}
DWORD WinVerifySslCert(const QByteArray &cert) {
DWORD errorStatus = std::numeric_limits< DWORD >::max();
PCCERT_CONTEXT certContext = CertCreateCertificateContext(
X509_ASN_ENCODING, reinterpret_cast< const BYTE * >(cert.constData()), cert.size());
if (!certContext) {
return errorStatus;
}
LPCSTR usage[] = { szOID_PKIX_KP_SERVER_AUTH, szOID_SERVER_GATED_CRYPTO, szOID_SGC_NETSCAPE };
CERT_CHAIN_PARA chainParameter;
memset(&chainParameter, 0, sizeof(CERT_CHAIN_PARA));
chainParameter.cbSize = sizeof(CERT_CHAIN_PARA);
chainParameter.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR;
chainParameter.RequestedUsage.Usage.cUsageIdentifier = ARRAYSIZE(usage);
chainParameter.RequestedUsage.Usage.rgpszUsageIdentifier = const_cast< LPSTR * >(usage);
PCCERT_CHAIN_CONTEXT chainContext = nullptr;
CertGetCertificateChain(nullptr, certContext, nullptr, nullptr, &chainParameter, 0, nullptr, &chainContext);
if (chainContext) {
errorStatus = chainContext->TrustStatus.dwErrorStatus;
CertFreeCertificateChain(chainContext);
}
CertFreeCertificateContext(certContext);
return errorStatus;
}
#undef DUMP_BUFFER_SIZE
#undef HeapEnableTerminationOnCorruption
#undef MMXSSE