Skip to content

Commit

Permalink
Bug,解决DllMain DLL_THREAD_ATTACH时间接加载msvcrt导致死锁风险
Browse files Browse the repository at this point in the history
  • Loading branch information
mingkuang-Chuyu committed May 25, 2024
1 parent cf300d8 commit 3610737
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 105 deletions.
78 changes: 36 additions & 42 deletions Sources/ucrt/inc/corecrt_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1488,7 +1488,38 @@ __declspec(dllimport) void __cdecl _amsg_exit(


__acrt_ptd* __cdecl __acrt_getptd_head(void);
#ifdef __BuildWithMSVCRT
#if WindowsTargetPlatformMinVersion < WindowsTargetPlatformWindows10_10240
__declspec(noinline) __inline bool __fastcall IsPointerInMsvcrtDll(const void* p)
{
if (!p)
return false;

static uintptr_t s_pBegin;
static uintptr_t s_pEnd;

if (s_pBegin == (uintptr_t)0 || s_pEnd == (uintptr_t)0)
{
MEMORY_BASIC_INFORMATION _BaseInfo;
if (VirtualQuery(&_amsg_exit, &_BaseInfo, sizeof(_BaseInfo)) == 0)
{
return false;
}
auto _pBegin = (char*)_BaseInfo.AllocationBase;
auto _pEnd = (char*)_BaseInfo.AllocationBase + _BaseInfo.RegionSize;
for (; VirtualQuery(_pEnd + 1, &_BaseInfo, sizeof(_BaseInfo));)
{
if (_pBegin != _BaseInfo.AllocationBase)
break;

_pEnd = (char*)_BaseInfo.BaseAddress + _BaseInfo.RegionSize;
}
s_pBegin = (uintptr_t)_pBegin;
s_pEnd = (uintptr_t)_pEnd;
}

return s_pBegin <= (uintptr_t)p && (uintptr_t)p < s_pEnd;
}

__declspec(noinline) __inline __acrt_ptd* __cdecl __acrt_getptd_noexit(void)
{
// ptd->_thandle 一共有3中情况:
Expand All @@ -1500,46 +1531,9 @@ __declspec(noinline) __inline __acrt_ptd* __cdecl __acrt_getptd_noexit(void)
// 这时必须借助GetThreadId,可是它开销是惊人的,不容易做到快速判断。
// 结合总总情况,我们现在通过判断 _errno() 返回地址是否在msvcrt模块范围来判断 __getptd_noexit 是否发生内存申请失败。
// 具体Bug请参考:https://github.com/Chuyu-Team/VC-LTL5/issues/49
typedef struct _DllAddressInfo
{
uintptr_t uBaseAddress;
uintptr_t uEndAddress;
} DllAddressInfo;

static DllAddressInfo s_DllAddressCache;

if (s_DllAddressCache.uBaseAddress == 0)
{
DllAddressInfo _DllAddressInfo = { (uintptr_t)-1, (uintptr_t)-1};
MEMORY_BASIC_INFORMATION _BaseInfo;
if (VirtualQuery(&_amsg_exit, &_BaseInfo, sizeof(_BaseInfo)))
{
_DllAddressInfo.uBaseAddress = (uintptr_t)_BaseInfo.AllocationBase;
_DllAddressInfo.uEndAddress = (uintptr_t)_BaseInfo.BaseAddress + _BaseInfo.RegionSize;

for (; VirtualQuery((void*)(_DllAddressInfo.uEndAddress + 1), &_BaseInfo, sizeof(_BaseInfo));)
{
if (_DllAddressInfo.uBaseAddress != (uintptr_t)_BaseInfo.AllocationBase)
break;

_DllAddressInfo.uEndAddress = (uintptr_t)_BaseInfo.BaseAddress + _BaseInfo.RegionSize;
}
}

#if defined(_X86_) || defined(_ARM_)
static_assert(sizeof(s_DllAddressCache) == sizeof(LONGLONG), "");
InterlockedCompareExchange64((volatile LONGLONG*)&s_DllAddressCache, *(LONGLONG*)&_DllAddressInfo, 0);
#elif defined(_IA64_) || defined(_AMD64_) || defined(_ARM64_)
// AMD 早期不支持 InterlockedCompareExchange128,所以不用……
InterlockedCompareExchange64((volatile LONGLONG*)&s_DllAddressCache.uEndAddress, _DllAddressInfo.uEndAddress, 0);
InterlockedCompareExchange64((volatile LONGLONG*)&s_DllAddressCache.uBaseAddress, _DllAddressInfo.uBaseAddress, 0);
#else
#error unsrpport!
#endif
}

uintptr_t _p_errno_value = (uintptr_t)_errno();
if (_p_errno_value == 0 || (s_DllAddressCache.uBaseAddress <= _p_errno_value && _p_errno_value < s_DllAddressCache.uEndAddress))
auto _p_errno_value = (unsigned char*)_errno();
if (_p_errno_value == 0 || IsPointerInMsvcrtDll(_p_errno_value))
{
return NULL;
}
Expand All @@ -1551,7 +1545,7 @@ __declspec(noinline) __inline __acrt_ptd* __cdecl __acrt_getptd_noexit(void)
__acrt_ptd* __cdecl __acrt_getptd_noexit(void);
#endif

#ifdef __BuildWithMSVCRT
#if WindowsTargetPlatformMinVersion < WindowsTargetPlatformWindows10_10240
__declspec(noinline) __inline __acrt_ptd* __cdecl __acrt_getptd(void)
{
__acrt_ptd* ptd = __acrt_getptd_noexit();
Expand Down Expand Up @@ -2785,4 +2779,4 @@ windowing_model_policy __cdecl __acrt_get_windowing_model_policy(void);

_CRT_END_C_HEADER

//#include <corecrt_internal_state_isolation.h>
//#include <corecrt_internal_state_isolation.h>
11 changes: 9 additions & 2 deletions Sources/ucrt/internal/initialization.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//
//
// initialization.cpp
//
// Copyright (c) Microsoft Corporation. All rights reserved.
Expand All @@ -13,6 +13,10 @@
#include <stdlib.h>
#include <stdio.h>

#if WindowsTargetPlatformMinVersion < __MakeVersion(10, 0, 10240)
bool __cdecl __LTL_ThunksInit();
#endif

extern "C" {


Expand Down Expand Up @@ -192,7 +196,7 @@ static bool __cdecl uninitialize_allocated_io_buffers(bool const /* terminating
_free_crt(__acrt_stderr_buffer);
__acrt_stderr_buffer = nullptr;

#if 0 //msvcrt.dl»áÊÍ·ÅËûÃÇ
#if 0 //msvcrt.dl会释放他们
_free_crt(__argv);
__argv = nullptr;

Expand Down Expand Up @@ -229,6 +233,9 @@ static __acrt_initializer const __acrt_initializers[] =
// Global pointers are stored in encoded form; they must be dynamically
// initialized to the encoded nullptr value before they are used by the CRT.
{ initialize_pointers, nullptr },
#if WindowsTargetPlatformMinVersion < __MakeVersion(10, 0, 10240)
{ __LTL_ThunksInit, nullptr },
#endif
// Enclaves only require initializers for supported features.
#ifndef _UCRT_ENCLAVE_BUILD
{ __acrt_initialize_winapi_thunks, __acrt_uninitialize_winapi_thunks },
Expand Down
16 changes: 7 additions & 9 deletions ucrtbase.msvcrt/fputwc_thunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,44 +6,42 @@

extern "C" __declspec(dllimport) extern _iobuf _iob[20];

__EXPAND_MSVCRT_FUN(fputc);

static int __cdecl fputc_msvcrt(int const c, FILE* const stream)
{
__EXPAND_MSVCRT_FUN(fputc);

if (auto pfputc_msvcrt = __Get_MSVCRT_FUN(fputc))
if (auto pfputc_msvcrt = try_get_fputc())
{
return pfputc_msvcrt(c, stream);
}

return EOF;
}

__EXPAND_MSVCRT_FUN(fputwc);

static wint_t __cdecl fputwc_msvcrt(
wchar_t _Character,
FILE* _Stream)
{
__EXPAND_MSVCRT_FUN(fputwc);

if (auto pfputwc_msvcrt = __Get_MSVCRT_FUN(fputwc))
if (auto pfputwc_msvcrt = try_get_fputwc())
{
return pfputwc_msvcrt(_Character, _Stream);
}

return WEOF;
}

__EXPAND_MSVCRT_FUN(fwrite);

static size_t __cdecl fwrite_msvcrt(
void const* const buffer,
size_t const size,
size_t const count,
FILE* const stream
)
{
__EXPAND_MSVCRT_FUN(fwrite);

if (auto pfwrite_msvcrt = __Get_MSVCRT_FUN(fwrite))
if (auto pfwrite_msvcrt = try_get_fwrite())
{
return pfwrite_msvcrt(buffer, size, count, stream);
}
Expand Down
7 changes: 4 additions & 3 deletions ucrtbase.msvcrt/localeconv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,11 @@ static wchar_t* __fastcall MultiByteToWideCharHelper(UINT CodePage, LPCCH lpMult

//
#if WindowsTargetPlatformMinVersion < WindowsTargetPlatformWindows7
__EXPAND_MSVCRT_FUN(localeconv);

extern "C" lconv* __cdecl localeconv(void)
{
__EXPAND_MSVCRT_FUN(localeconv);

auto pMSVCRT_localeconv = try_get_localeconv();
if (pMSVCRT_localeconv == nullptr)
{
//正常不应该为空!!
Expand Down Expand Up @@ -171,4 +172,4 @@ extern "C" lconv* __cdecl localeconv(void)
}

_LCRT_DEFINE_IAT_SYMBOL(localeconv);
#endif
#endif
12 changes: 8 additions & 4 deletions ucrtbase.msvcrt/matherr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,19 @@ typedef int(__cdecl* _HANDLE_MATH_ERROR)(struct _exception*);

static _HANDLE_MATH_ERROR user_matherr;

extern "C" void __cdecl __setusermatherr(
_HANDLE_MATH_ERROR pf
);

__EXPAND_MSVCRT_FUN(__setusermatherr);

extern "C" void __cdecl __setusermatherr(
_HANDLE_MATH_ERROR pf
)
{
//继续调用msvcrt版本是为了能接管 msvcrt的user_matherr,但是有一个问题,如果其他人调用了 __setusermatherr,这会覆盖msvcrt的user_matherr
//二者会发生不一致……这类问题无法一种健全的解决方案,但是可以暂不处理
__EXPAND_MSVCRT_FUN(__setusermatherr);

if (auto p_setusermatherr_msvcrt = __Get_MSVCRT_FUN(__setusermatherr))
if (auto p_setusermatherr_msvcrt = try_get___setusermatherr())
{
p_setusermatherr_msvcrt(pf);
}
Expand Down Expand Up @@ -45,4 +49,4 @@ extern "C" int __cdecl __acrt_invoke_user_matherr(struct _exception* ex)


return 0;
}
}
68 changes: 66 additions & 2 deletions ucrtbase.msvcrt/msvcrt_loadhelper.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
#include <Windows.h>

#include <internal_shared.h>

HMODULE __fastcall __LTL_GetMSVCRTModule()
#include "msvcrt_loadhelper.h"

#pragma comment(linker, "/merge:.YYLT1=.data")
#pragma comment(linker, "/merge:.YYLT2=.rdata")

typedef void* (__cdecl* InitFunType)();

static HMODULE __fastcall __LTL_GetMSVCRTModule()
{
static HMODULE MSVCRTModuleCache;

Expand All @@ -21,4 +29,60 @@ HMODULE __fastcall __LTL_GetMSVCRTModule()
MSVCRTModuleCache = hMSVCRT ? hMSVCRT : (HMODULE)INVALID_HANDLE_VALUE;

return hMSVCRT;
}
}

__declspec(allocate(".YYLT1$DAA")) static void* s_pfnReserve[] = { nullptr };
#define __LTL_THUNKS_FUN_START (s_pfnReserve + 1) //指针缓存开始位置
__declspec(allocate(".YYLT1$DAC")) static void* __LTL_THUNKS_FUN_END[] = { nullptr }; //指针缓存结束位置

__declspec(allocate(".YYLT2$IFA")) static void* const s_pfnInitReserve[] = { nullptr };
#define __LTL_THUNKS_INIT_FUN_START (s_pfnInitReserve + 1) //函数初始化开始位置
__declspec(allocate(".YYLT2$IFC")) static void* const __LTL_THUNKS_INIT_FUN_END[] = { nullptr }; //函数初始化开始位置

bool __cdecl __LTL_ThunksInit()
{
for (auto p = __LTL_THUNKS_FUN_START; p != __LTL_THUNKS_FUN_END; ++p)
{
*p = __crt_fast_encode_pointer(nullptr);
}

for (auto p = (InitFunType*)__LTL_THUNKS_INIT_FUN_START; p != (InitFunType*)__LTL_THUNKS_INIT_FUN_END; ++p)
{
if (*p == nullptr)
continue;

(*p)();
}

return true;
}

namespace YY
{
namespace VC_LTL
{
void* __fastcall try_get_function(
void** _ppFunAddress,
char const* const _szName)
{
auto _p = __crt_fast_decode_pointer(*_ppFunAddress);
if (_p)
{
return _p == INVALID_HANDLE_VALUE ? nullptr : _p;
}

do
{
auto _hModule = __LTL_GetMSVCRTModule();
if (!_hModule)
break;

_p = GetProcAddress(_hModule, _szName);

InterlockedExchange((uintptr_t*)_ppFunAddress, (uintptr_t)__crt_fast_encode_pointer(_p ? _p : INVALID_HANDLE_VALUE));
} while (false);

return _p;
}
}
}
34 changes: 24 additions & 10 deletions ucrtbase.msvcrt/msvcrt_loadhelper.h
Original file line number Diff line number Diff line change
@@ -1,16 +1,30 @@
#pragma once
#include <Windows.h>

HMODULE __fastcall __LTL_GetMSVCRTModule();

#define __EXPAND_MSVCRT_FUN(name) \
static decltype(name)* _CRT_CONCATENATE(pMSVCRT_, name); \
if(_CRT_CONCATENATE(pMSVCRT_, name) == NULL)\
{ \
auto fun = GetProcAddress(__LTL_GetMSVCRTModule(), #name);\
_CRT_CONCATENATE(pMSVCRT_, name) = fun ? (decltype(name)*)fun : (decltype(name)*)INVALID_HANDLE_VALUE;\
}
#pragma section(".YYLT1$DAA", long, read, write)
#pragma section(".YYLT1$DAB", long, read, write) //LTL函数缓存节点
#pragma section(".YYLT1$DAC", long, read, write) //保留,暂时用于边界结束

#pragma section(".YYLT2$IFA", long, read)
#pragma section(".YYLT2$IFB", long, read) //LTL函数缓存初始化函数
#pragma section(".YYLT2$IFC", long, read) //LTL函数缓存初始化函数结束

#define __Get_MSVCRT_FUN(name) (_CRT_CONCATENATE(pMSVCRT_, name) != (decltype(name)*)INVALID_HANDLE_VALUE ? _CRT_CONCATENATE(pMSVCRT_, name) : (decltype(name)*)NULL)
namespace YY
{
namespace VC_LTL
{
void* __fastcall try_get_function(
void** _ppFunAddress,
char const* const _szName);
}
}

#define __EXPAND_MSVCRT_FUN(name) \
static decltype(name)* _CRT_CONCATENATE(try_get_, name)() \
{ \
__declspec(allocate(".YYLT2$IFB")) static void* const s_pfnInitFun = &_CRT_CONCATENATE(try_get_, name); \
__pragma(warning(suppress:6031)) \
_bittest((LONG*)(&s_pfnInitFun), 0); \
__declspec(allocate(".YYLT1$DAB")) static void* _CRT_CONCATENATE(s_pfn_, name); \
return (decltype(name)*)YY::VC_LTL::try_get_function(&_CRT_CONCATENATE(s_pfn_, name), #name); \
}
Loading

0 comments on commit 3610737

Please sign in to comment.