NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome_elf / blacklist / blacklist.cc
blob32d3cb20c632b10a14f15b540bdeceb612d6ea75
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome_elf/blacklist/blacklist.h"
7 #include <string.h>
9 #include "base/basictypes.h"
10 #include "chrome_elf/blacklist/blacklist_interceptions.h"
11 #include "sandbox/win/src/interception_internal.h"
12 #include "sandbox/win/src/internal_types.h"
13 #include "sandbox/win/src/sandbox_utils.h"
14 #include "sandbox/win/src/service_resolver.h"
15 #include "version.h" // NOLINT
17 // http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx
18 extern "C" IMAGE_DOS_HEADER __ImageBase;
20 namespace blacklist{
22 const wchar_t* g_troublesome_dlls[kTroublesomeDllsMaxCount] = {
23 L"libsvn_tsvn32.dll",
24 // Keep this null pointer here to mark the end of the list.
25 NULL,
28 const wchar_t kRegistryBeaconPath[] = L"SOFTWARE\\Google\\Chrome\\BLBeacon";
29 const wchar_t kBeaconVersion[] = L"version";
30 const wchar_t kBeaconState[] = L"state";
32 } // namespace blacklist
34 // Allocate storage for thunks in a page of this module to save on doing
35 // an extra allocation at run time.
36 #pragma section(".crthunk",read,execute)
37 __declspec(allocate(".crthunk")) sandbox::ThunkData g_thunk_storage;
39 namespace {
41 enum Version {
42 VERSION_PRE_XP_SP2 = 0, // Not supported.
43 VERSION_XP_SP2,
44 VERSION_SERVER_2003, // Also includes XP Pro x64 and Server 2003 R2.
45 VERSION_VISTA, // Also includes Windows Server 2008.
46 VERSION_WIN7, // Also includes Windows Server 2008 R2.
47 VERSION_WIN8, // Also includes Windows Server 2012.
48 VERSION_WIN8_1,
49 VERSION_WIN_LAST, // Indicates error condition.
52 // Whether a process is running under WOW64 (the wrapper that allows 32-bit
53 // processes to run on 64-bit versions of Windows). This will return
54 // WOW64_DISABLED for both "32-bit Chrome on 32-bit Windows" and "64-bit
55 // Chrome on 64-bit Windows". WOW64_UNKNOWN means "an error occurred", e.g.
56 // the process does not have sufficient access rights to determine this.
57 enum WOW64Status {
58 WOW64_DISABLED,
59 WOW64_ENABLED,
60 WOW64_UNKNOWN,
63 // Record if the blacklist was successfully initialized so processes can easily
64 // determine if the blacklist is enabled for them.
65 bool g_blacklist_initialized = false;
67 WOW64Status GetWOW64StatusForCurrentProcess() {
68 typedef BOOL (WINAPI* IsWow64ProcessFunc)(HANDLE, PBOOL);
69 IsWow64ProcessFunc is_wow64_process = reinterpret_cast<IsWow64ProcessFunc>(
70 GetProcAddress(GetModuleHandle(L"kernel32.dll"), "IsWow64Process"));
71 if (!is_wow64_process)
72 return WOW64_DISABLED;
73 BOOL is_wow64 = FALSE;
74 if (!(*is_wow64_process)(GetCurrentProcess(), &is_wow64))
75 return WOW64_UNKNOWN;
76 return is_wow64 ? WOW64_ENABLED : WOW64_DISABLED;
79 class OSInfo {
80 public:
81 struct VersionNumber {
82 int major;
83 int minor;
84 int build;
87 struct ServicePack {
88 int major;
89 int minor;
92 OSInfo() {
93 OSVERSIONINFOEX version_info = { sizeof(version_info) };
94 GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info));
95 version_number_.major = version_info.dwMajorVersion;
96 version_number_.minor = version_info.dwMinorVersion;
97 version_number_.build = version_info.dwBuildNumber;
98 if ((version_number_.major == 5) && (version_number_.minor > 0)) {
99 // Treat XP Pro x64, Home Server, and Server 2003 R2 as Server 2003.
100 version_ = (version_number_.minor == 1) ? VERSION_XP_SP2 :
101 VERSION_SERVER_2003;
102 if (version_ == VERSION_XP_SP2 && version_info.wServicePackMajor < 2)
103 version_ = VERSION_PRE_XP_SP2;
104 } else if (version_number_.major == 6) {
105 switch (version_number_.minor) {
106 case 0:
107 // Treat Windows Server 2008 the same as Windows Vista.
108 version_ = VERSION_VISTA;
109 break;
110 case 1:
111 // Treat Windows Server 2008 R2 the same as Windows 7.
112 version_ = VERSION_WIN7;
113 break;
114 case 2:
115 // Treat Windows Server 2012 the same as Windows 8.
116 version_ = VERSION_WIN8;
117 break;
118 default:
119 version_ = VERSION_WIN8_1;
120 break;
122 } else if (version_number_.major > 6) {
123 version_ = VERSION_WIN_LAST;
124 } else {
125 version_ = VERSION_PRE_XP_SP2;
128 service_pack_.major = version_info.wServicePackMajor;
129 service_pack_.minor = version_info.wServicePackMinor;
132 Version version() const { return version_; }
133 VersionNumber version_number() const { return version_number_; }
134 ServicePack service_pack() const { return service_pack_; }
136 private:
137 Version version_;
138 VersionNumber version_number_;
139 ServicePack service_pack_;
141 DISALLOW_COPY_AND_ASSIGN(OSInfo);
144 bool IsNonBrowserProcess() {
145 typedef bool (*IsSandboxedProcessFunc)();
146 IsSandboxedProcessFunc is_sandboxed_process =
147 reinterpret_cast<IsSandboxedProcessFunc>(
148 GetProcAddress(GetModuleHandle(NULL), "IsSandboxedProcess"));
149 if (is_sandboxed_process && is_sandboxed_process())
150 return true;
152 return false;
155 // Record that the thunk setup completed succesfully and close the registry
156 // key handle since it is no longer needed.
157 void RecordSuccessfulThunkSetup(HKEY* key) {
158 if (key != NULL) {
159 DWORD blacklist_state = blacklist::BLACKLIST_SETUP_RUNNING;
160 ::RegSetValueEx(*key,
161 blacklist::kBeaconState,
163 REG_DWORD,
164 reinterpret_cast<LPBYTE>(&blacklist_state),
165 sizeof(blacklist_state));
166 ::RegCloseKey(*key);
167 key = NULL;
171 } // namespace
173 namespace blacklist {
175 #if defined(_WIN64)
176 // Allocate storage for the pointer to the old NtMapViewOfSectionFunction.
177 #pragma section(".oldntmap",write,read)
178 __declspec(allocate(".oldntmap"))
179 NtMapViewOfSectionFunction g_nt_map_view_of_section_func = NULL;
180 #endif
182 bool LeaveSetupBeacon() {
183 HKEY key = NULL;
184 DWORD disposition = 0;
185 LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER,
186 kRegistryBeaconPath,
188 NULL,
189 REG_OPTION_NON_VOLATILE,
190 KEY_QUERY_VALUE | KEY_SET_VALUE,
191 NULL,
192 &key,
193 &disposition);
194 if (result != ERROR_SUCCESS)
195 return false;
197 // Retrieve the current blacklist state.
198 DWORD blacklist_state = BLACKLIST_DISABLED;
199 DWORD blacklist_state_size = sizeof(blacklist_state);
200 DWORD type = 0;
201 result = ::RegQueryValueEx(key,
202 kBeaconState,
204 &type,
205 reinterpret_cast<LPBYTE>(&blacklist_state),
206 &blacklist_state_size);
208 if (blacklist_state != BLACKLIST_ENABLED ||
209 result != ERROR_SUCCESS || type != REG_DWORD) {
210 ::RegCloseKey(key);
211 return false;
214 // If the blacklist wasn't set as enabled for this version, don't
215 // use it.
216 wchar_t key_data[255] = {};
217 DWORD key_data_size = sizeof(key_data);
218 result = ::RegQueryValueEx(key,
219 blacklist::kBeaconVersion,
221 &type,
222 reinterpret_cast<LPBYTE>(key_data),
223 &key_data_size);
225 if (wcscmp(key_data, TEXT(CHROME_VERSION_STRING)) != 0 ||
226 result != ERROR_SUCCESS || type != REG_SZ) {
227 ::RegCloseKey(key);
228 return false;
231 // Mark the blacklist setup code as running so if it crashes the blacklist
232 // won't be enabled for the next run.
233 blacklist_state = BLACKLIST_SETUP_RUNNING;
234 result = ::RegSetValueEx(key,
235 kBeaconState,
237 REG_DWORD,
238 reinterpret_cast<LPBYTE>(&blacklist_state),
239 sizeof(blacklist_state));
240 ::RegCloseKey(key);
242 return (result == ERROR_SUCCESS);
245 bool ResetBeacon() {
246 HKEY key = NULL;
247 DWORD disposition = 0;
248 LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER,
249 kRegistryBeaconPath,
251 NULL,
252 REG_OPTION_NON_VOLATILE,
253 KEY_QUERY_VALUE | KEY_SET_VALUE,
254 NULL,
255 &key,
256 &disposition);
257 if (result != ERROR_SUCCESS)
258 return false;
260 DWORD blacklist_state = BLACKLIST_ENABLED;
261 result = ::RegSetValueEx(key,
262 kBeaconState,
264 REG_DWORD,
265 reinterpret_cast<LPBYTE>(&blacklist_state),
266 sizeof(blacklist_state));
267 ::RegCloseKey(key);
269 return (result == ERROR_SUCCESS);
272 int BlacklistSize() {
273 int size = -1;
274 while (blacklist::g_troublesome_dlls[++size] != NULL) {}
276 return size;
279 bool IsBlacklistInitialized() {
280 return g_blacklist_initialized;
283 bool AddDllToBlacklist(const wchar_t* dll_name) {
284 int blacklist_size = BlacklistSize();
285 // We need to leave one space at the end for the null pointer.
286 if (blacklist_size + 1 >= kTroublesomeDllsMaxCount)
287 return false;
288 for (int i = 0; i < blacklist_size; ++i) {
289 if (!_wcsicmp(g_troublesome_dlls[i], dll_name))
290 return true;
293 // Copy string to blacklist.
294 wchar_t* str_buffer = new wchar_t[wcslen(dll_name) + 1];
295 wcscpy(str_buffer, dll_name);
297 g_troublesome_dlls[blacklist_size] = str_buffer;
298 return true;
301 bool RemoveDllFromBlacklist(const wchar_t* dll_name) {
302 int blacklist_size = BlacklistSize();
303 for (int i = 0; i < blacklist_size; ++i) {
304 if (!_wcsicmp(g_troublesome_dlls[i], dll_name)) {
305 // Found the thing to remove. Delete it then replace it with the last
306 // element.
307 delete[] g_troublesome_dlls[i];
308 g_troublesome_dlls[i] = g_troublesome_dlls[blacklist_size - 1];
309 g_troublesome_dlls[blacklist_size - 1] = NULL;
310 return true;
313 return false;
316 bool Initialize(bool force) {
317 // Check to see that we found the functions we need in ntdll.
318 if (!InitializeInterceptImports())
319 return false;
321 // Check to see if this is a non-browser process, abort if so.
322 if (IsNonBrowserProcess())
323 return false;
325 // Check to see if a beacon is present, abort if so.
326 if (!force && !LeaveSetupBeacon())
327 return false;
329 // Don't try blacklisting on unsupported OS versions.
330 OSInfo os_info;
331 if (os_info.version() <= VERSION_PRE_XP_SP2)
332 return false;
334 // Pseudo-handle, no need to close.
335 HANDLE current_process = ::GetCurrentProcess();
337 // Tells the resolver to patch already patched functions.
338 const bool kRelaxed = true;
340 // Record that we are starting the thunk setup code.
341 HKEY key = NULL;
342 DWORD disposition = 0;
343 LONG result = ::RegCreateKeyEx(HKEY_CURRENT_USER,
344 kRegistryBeaconPath,
346 NULL,
347 REG_OPTION_NON_VOLATILE,
348 KEY_QUERY_VALUE | KEY_SET_VALUE,
349 NULL,
350 &key,
351 &disposition);
352 if (result == ERROR_SUCCESS) {
353 DWORD blacklist_state = BLACKLIST_THUNK_SETUP;
354 ::RegSetValueEx(key,
355 kBeaconState,
357 REG_DWORD,
358 reinterpret_cast<LPBYTE>(&blacklist_state),
359 sizeof(blacklist_state));
360 } else {
361 key = NULL;
364 // Create a thunk via the appropriate ServiceResolver instance.
365 sandbox::ServiceResolverThunk* thunk = NULL;
366 #if defined(_WIN64)
367 // Because Windows 8 and 8.1 have different stubs in 64-bit,
368 // ServiceResolverThunk can handle all the formats in 64-bit (instead only
369 // handling 1 like it does in 32-bit versions).
370 thunk = new sandbox::ServiceResolverThunk(current_process, kRelaxed);
371 #else
372 if (GetWOW64StatusForCurrentProcess() == WOW64_ENABLED) {
373 if (os_info.version() >= VERSION_WIN8)
374 thunk = new sandbox::Wow64W8ResolverThunk(current_process, kRelaxed);
375 else
376 thunk = new sandbox::Wow64ResolverThunk(current_process, kRelaxed);
377 } else if (os_info.version() >= VERSION_WIN8) {
378 thunk = new sandbox::Win8ResolverThunk(current_process, kRelaxed);
379 } else {
380 thunk = new sandbox::ServiceResolverThunk(current_process, kRelaxed);
382 #endif
384 // Record that we have initialized the blacklist.
385 g_blacklist_initialized = true;
387 BYTE* thunk_storage = reinterpret_cast<BYTE*>(&g_thunk_storage);
389 // Mark the thunk storage as readable and writeable, since we
390 // ready to write to it.
391 DWORD old_protect = 0;
392 if (!VirtualProtect(&g_thunk_storage,
393 sizeof(g_thunk_storage),
394 PAGE_EXECUTE_READWRITE,
395 &old_protect)) {
396 RecordSuccessfulThunkSetup(&key);
397 return false;
400 thunk->AllowLocalPatches();
402 // We declare this early so it can be used in the 64-bit block below and
403 // still work on 32-bit build when referenced at the end of the function.
404 BOOL page_executable = false;
406 // Replace the default NtMapViewOfSection with our patched version.
407 #if defined(_WIN64)
408 NTSTATUS ret = thunk->Setup(::GetModuleHandle(sandbox::kNtdllName),
409 reinterpret_cast<void*>(&__ImageBase),
410 "NtMapViewOfSection",
411 NULL,
412 &blacklist::BlNtMapViewOfSection64,
413 thunk_storage,
414 sizeof(sandbox::ThunkData),
415 NULL);
417 // Keep a pointer to the original code, we don't have enough space to
418 // add it directly to the call.
419 g_nt_map_view_of_section_func = reinterpret_cast<NtMapViewOfSectionFunction>(
420 thunk_storage);
422 // Ensure that the pointer to the old function can't be changed.
423 page_executable = VirtualProtect(&g_nt_map_view_of_section_func,
424 sizeof(g_nt_map_view_of_section_func),
425 PAGE_EXECUTE_READ,
426 &old_protect);
427 #else
428 NTSTATUS ret = thunk->Setup(::GetModuleHandle(sandbox::kNtdllName),
429 reinterpret_cast<void*>(&__ImageBase),
430 "NtMapViewOfSection",
431 NULL,
432 &blacklist::BlNtMapViewOfSection,
433 thunk_storage,
434 sizeof(sandbox::ThunkData),
435 NULL);
436 #endif
437 delete thunk;
439 // Mark the thunk storage as executable and prevent any future writes to it.
440 page_executable = page_executable && VirtualProtect(&g_thunk_storage,
441 sizeof(g_thunk_storage),
442 PAGE_EXECUTE_READ,
443 &old_protect);
445 RecordSuccessfulThunkSetup(&key);
447 return NT_SUCCESS(ret) && page_executable;
450 } // namespace blacklist