1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
|
#include "rar.hpp"
/*
To enable, disable or check Large Memory pages manually:
- open "Local Security Policy" from "Start Menu";
- open "Lock Pages in Memory" in "Local Policies\User Rights Assignment";
- add or remove the user and sign out and sign in or restart Windows.
*/
#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(RARDLL)
#define ALLOW_LARGE_PAGES
#endif
LargePageAlloc::LargePageAlloc()
{
UseLargePages=false;
#ifdef ALLOW_LARGE_PAGES
PageSize=0;
#endif
}
void LargePageAlloc::AllowLargePages(bool Allow)
{
#ifdef ALLOW_LARGE_PAGES
if (Allow && PageSize==0)
{
HMODULE hKernel=GetModuleHandle(L"kernel32.dll");
if (hKernel!=nullptr)
{
typedef SIZE_T (*GETLARGEPAGEMINIMUM)();
GETLARGEPAGEMINIMUM pGetLargePageMinimum=(GETLARGEPAGEMINIMUM)GetProcAddress(hKernel, "GetLargePageMinimum");
if (pGetLargePageMinimum!=nullptr)
PageSize=pGetLargePageMinimum();
}
if (PageSize==0 || !SetPrivilege(SE_LOCK_MEMORY_NAME))
{
UseLargePages=false;
return;
}
}
UseLargePages=Allow;
#endif
}
bool LargePageAlloc::IsPrivilegeAssigned()
{
#ifdef ALLOW_LARGE_PAGES
return SetPrivilege(SE_LOCK_MEMORY_NAME);
#else
return true;
#endif
}
bool LargePageAlloc::AssignPrivilege()
{
#ifdef ALLOW_LARGE_PAGES
HANDLE hToken = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
return false;
// Get the required buffer size.
DWORD BufSize=0;
GetTokenInformation(hToken, TokenUser, NULL, 0, &BufSize);
if (BufSize==0 || BufSize>1000000) // Sanity check for returned value.
{
CloseHandle(hToken);
return false;
}
TOKEN_USER *TokenInfo = (TOKEN_USER*)malloc(BufSize);
// Get the current user token information.
if (GetTokenInformation(hToken,TokenUser,TokenInfo,BufSize,&BufSize)==0)
{
CloseHandle(hToken);
return false;
}
// Get SID string for the current user.
LPWSTR ApiSidStr;
ConvertSidToStringSid(TokenInfo->User.Sid, &ApiSidStr);
// Convert SID to C++ string and release API based buffer.
std::wstring SidStr=ApiSidStr;
LocalFree(ApiSidStr);
CloseHandle(hToken);
if (IsUserAdmin())
AssignPrivilegeBySid(SidStr);
else
{
// Define here, so they survive until ShellExecuteEx call.
std::wstring ExeName=GetModuleFileStr();
std::wstring Param=std::wstring(L"-") + LOCKMEM_SWITCH + SidStr;
SHELLEXECUTEINFO shExecInfo{};
shExecInfo.cbSize = sizeof(shExecInfo);
shExecInfo.hwnd = NULL; // Specifying WinRAR main window here does not work well in command line mode.
shExecInfo.lpVerb = L"runas";
shExecInfo.lpFile = ExeName.c_str();
shExecInfo.lpParameters = Param.c_str();
shExecInfo.nShow = SW_SHOWNORMAL;
BOOL Result=ShellExecuteEx(&shExecInfo);
}
#endif
return true;
}
bool LargePageAlloc::AssignPrivilegeBySid(const std::wstring &Sid)
{
#ifdef ALLOW_LARGE_PAGES
LSA_HANDLE PolicyHandle;
LSA_OBJECT_ATTRIBUTES ObjectAttributes{}; // Docs require to zero initalize it.
#ifndef STATUS_SUCCESS // Can be defined through WIL package in WinRAR.
// We define STATUS_SUCCESS here instead of including ntstatus.h to avoid
// macro redefinition warnings. We tried UMDF_USING_NTSTATUS define
// and other workarounds, but it didn't help.
const uint STATUS_SUCCESS=0;
#endif
if (LsaOpenPolicy(NULL,&ObjectAttributes,POLICY_CREATE_ACCOUNT|
POLICY_LOOKUP_NAMES,&PolicyHandle)!=STATUS_SUCCESS)
return false;
PSID UserSid;
ConvertStringSidToSid(Sid.c_str(),&UserSid);
LSA_UNICODE_STRING LsaString;
LsaString.Buffer=(PWSTR)SE_LOCK_MEMORY_NAME;
// It must be in bytes, so multiple it to sizeof(wchar_t).
LsaString.Length=(USHORT)wcslen(LsaString.Buffer)*sizeof(LsaString.Buffer[0]);
LsaString.MaximumLength=LsaString.Length;
bool Success=LsaAddAccountRights(PolicyHandle,UserSid,&LsaString,1)==STATUS_SUCCESS;
LocalFree(UserSid);
LsaClose(PolicyHandle);
mprintf(St(MPrivilegeAssigned));
if (Ask(St(MYesNo)) == 1)
Shutdown(POWERMODE_RESTART);
return Success;
#else
return true;
#endif
}
bool LargePageAlloc::AssignConfirmation()
{
#ifdef ALLOW_LARGE_PAGES
mprintf(St(MLockInMemoryNeeded));
return Ask(St(MYesNo)) == 1;
#else
return false;
#endif
}
void* LargePageAlloc::new_large(size_t Size)
{
void *Allocated=nullptr;
#ifdef ALLOW_LARGE_PAGES
if (UseLargePages && Size>=PageSize)
{
// VirtualAlloc fails if allocation size isn't multiple of page size.
SIZE_T AllocSize=Size%PageSize==0 ? Size:(Size/PageSize+1)*PageSize;
Allocated=VirtualAlloc(nullptr,AllocSize,MEM_COMMIT|MEM_RESERVE|MEM_LARGE_PAGES,PAGE_READWRITE);
if (Allocated!=nullptr)
LargeAlloc.push_back(Allocated);
}
#endif
return Allocated;
}
bool LargePageAlloc::delete_large(void *Addr)
{
#ifdef ALLOW_LARGE_PAGES
if (Addr!=nullptr)
for (size_t I=0;I<LargeAlloc.size();I++)
if (LargeAlloc[I]==Addr)
{
LargeAlloc[I]=nullptr;
VirtualFree(Addr,0,MEM_RELEASE);
return true;
}
#endif
return false;
}
|