-
Notifications
You must be signed in to change notification settings - Fork 29
/
largepage.cpp
201 lines (164 loc) · 4.93 KB
/
largepage.cpp
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;
}