1 // Copyright 2011 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/win/registry.h"
6
7 #include <windows.h>
8
9 #include <shlobj.h>
10 #include <stdint.h>
11
12 #include <cstring>
13 #include <iterator>
14 #include <string_view>
15 #include <utility>
16
17 #include "base/compiler_specific.h"
18 #include "base/functional/bind.h"
19 #include "base/functional/callback.h"
20 #include "base/location.h"
21 #include "base/memory/scoped_refptr.h"
22 #include "base/run_loop.h"
23 #include "base/strings/strcat.h"
24 #include "base/test/bind.h"
25 #include "base/test/gmock_expected_support.h"
26 #include "base/test/mock_callback.h"
27 #include "base/test/task_environment.h"
28 #include "base/test/test_mock_time_task_runner.h"
29 #include "base/test/test_reg_util_win.h"
30 #include "base/threading/simple_thread.h"
31 #include "base/win/win_util.h"
32 #include "base/win/windows_version.h"
33 #include "testing/gmock/include/gmock/gmock.h"
34 #include "testing/gtest/include/gtest/gtest.h"
35
36 namespace base::win {
37
38 namespace {
39
40 constexpr wchar_t kRootKey[] = L"Base_Registry_Unittest";
41
42 // A test harness for registry tests that operate in HKCU. Each test is given
43 // a valid key distinct from that used by other tests.
44 class RegistryTest : public testing::Test {
45 protected:
RegistryTest()46 RegistryTest() : root_key_(std::wstring(L"Software\\") + kRootKey) {}
47
SetUp()48 void SetUp() override {
49 ASSERT_NO_FATAL_FAILURE(registry_override_.OverrideRegistry(
50 HKEY_CURRENT_USER, &override_path_));
51
52 // Create the test's root key.
53 RegKey key(HKEY_CURRENT_USER, L"", KEY_CREATE_SUB_KEY);
54 ASSERT_NE(ERROR_SUCCESS,
55 key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_READ));
56 ASSERT_EQ(ERROR_SUCCESS,
57 key.Create(HKEY_CURRENT_USER, root_key().c_str(), KEY_READ));
58 }
59
60 // Returns the path to a key under HKCU that is made available for exclusive
61 // use by a test.
root_key() const62 const std::wstring& root_key() const { return root_key_; }
63
override_path() const64 const std::wstring& override_path() const { return override_path_; }
65
66 private:
67 registry_util::RegistryOverrideManager registry_override_;
68 const std::wstring root_key_;
69 std::wstring override_path_;
70 };
71
72 } // namespace
73
TEST_F(RegistryTest,ValueTest)74 TEST_F(RegistryTest, ValueTest) {
75 RegKey key;
76
77 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, root_key().c_str(),
78 KEY_READ | KEY_SET_VALUE));
79 ASSERT_TRUE(key.Valid());
80
81 const wchar_t kStringValueName[] = L"StringValue";
82 const wchar_t kDWORDValueName[] = L"DWORDValue";
83 const wchar_t kInt64ValueName[] = L"Int64Value";
84 const wchar_t kStringData[] = L"string data";
85 const DWORD kDWORDData = 0xdeadbabe;
86 const int64_t kInt64Data = 0xdeadbabedeadbabeLL;
87
88 // Test value creation
89 ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kStringValueName, kStringData));
90 ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kDWORDValueName, kDWORDData));
91 ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kInt64ValueName, &kInt64Data,
92 sizeof(kInt64Data), REG_QWORD));
93 EXPECT_THAT(key.GetValueCount(), base::test::ValueIs(3U));
94 EXPECT_TRUE(key.HasValue(kStringValueName));
95 EXPECT_TRUE(key.HasValue(kDWORDValueName));
96 EXPECT_TRUE(key.HasValue(kInt64ValueName));
97
98 // Test Read
99 std::wstring string_value;
100 DWORD dword_value = 0;
101 int64_t int64_value = 0;
102 ASSERT_EQ(ERROR_SUCCESS, key.ReadValue(kStringValueName, &string_value));
103 ASSERT_EQ(ERROR_SUCCESS, key.ReadValueDW(kDWORDValueName, &dword_value));
104 ASSERT_EQ(ERROR_SUCCESS, key.ReadInt64(kInt64ValueName, &int64_value));
105 EXPECT_EQ(kStringData, string_value);
106 EXPECT_EQ(kDWORDData, dword_value);
107 EXPECT_EQ(kInt64Data, int64_value);
108
109 // Make sure out args are not touched if ReadValue fails
110 const wchar_t* kNonExistent = L"NonExistent";
111 ASSERT_NE(ERROR_SUCCESS, key.ReadValue(kNonExistent, &string_value));
112 ASSERT_NE(ERROR_SUCCESS, key.ReadValueDW(kNonExistent, &dword_value));
113 ASSERT_NE(ERROR_SUCCESS, key.ReadInt64(kNonExistent, &int64_value));
114 EXPECT_EQ(kStringData, string_value);
115 EXPECT_EQ(kDWORDData, dword_value);
116 EXPECT_EQ(kInt64Data, int64_value);
117
118 // Test delete
119 ASSERT_EQ(ERROR_SUCCESS, key.DeleteValue(kStringValueName));
120 ASSERT_EQ(ERROR_SUCCESS, key.DeleteValue(kDWORDValueName));
121 ASSERT_EQ(ERROR_SUCCESS, key.DeleteValue(kInt64ValueName));
122 EXPECT_THAT(key.GetValueCount(), base::test::ValueIs(0U));
123 EXPECT_FALSE(key.HasValue(kStringValueName));
124 EXPECT_FALSE(key.HasValue(kDWORDValueName));
125 EXPECT_FALSE(key.HasValue(kInt64ValueName));
126 }
127
TEST_F(RegistryTest,BigValueIteratorTest)128 TEST_F(RegistryTest, BigValueIteratorTest) {
129 RegKey key;
130 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, root_key().c_str(),
131 KEY_READ | KEY_SET_VALUE));
132 ASSERT_TRUE(key.Valid());
133
134 // Create a test value that is larger than MAX_PATH.
135 std::wstring data(MAX_PATH * 2, 'a');
136
137 ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(data.c_str(), data.c_str()));
138
139 RegistryValueIterator iterator(HKEY_CURRENT_USER, root_key().c_str());
140 ASSERT_TRUE(iterator.Valid());
141 EXPECT_EQ(data, iterator.Name());
142 EXPECT_EQ(data, iterator.Value());
143 // ValueSize() is in bytes, including NUL.
144 EXPECT_EQ((MAX_PATH * 2 + 1) * sizeof(wchar_t), iterator.ValueSize());
145 ++iterator;
146 EXPECT_FALSE(iterator.Valid());
147 }
148
TEST_F(RegistryTest,TruncatedCharTest)149 TEST_F(RegistryTest, TruncatedCharTest) {
150 RegKey key;
151 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, root_key().c_str(),
152 KEY_READ | KEY_SET_VALUE));
153 ASSERT_TRUE(key.Valid());
154
155 const wchar_t kName[] = L"name";
156 // kData size is not a multiple of sizeof(wchar_t).
157 const uint8_t kData[] = {1, 2, 3, 4, 5};
158 EXPECT_EQ(5u, std::size(kData));
159 ASSERT_EQ(ERROR_SUCCESS,
160 key.WriteValue(kName, kData, std::size(kData), REG_BINARY));
161
162 RegistryValueIterator iterator(HKEY_CURRENT_USER, root_key().c_str());
163 ASSERT_TRUE(iterator.Valid());
164 // Avoid having to use EXPECT_STREQ here by leveraging std::string_view's
165 // operator== to perform a deep comparison.
166 EXPECT_EQ(std::wstring_view(kName), std::wstring_view(iterator.Name()));
167 // ValueSize() is in bytes.
168 ASSERT_EQ(std::size(kData), iterator.ValueSize());
169 // Value() is NUL terminated.
170 int end = (iterator.ValueSize() + sizeof(wchar_t) - 1) / sizeof(wchar_t);
171 EXPECT_NE('\0', iterator.Value()[end - 1]);
172 EXPECT_EQ('\0', iterator.Value()[end]);
173 EXPECT_EQ(0, std::memcmp(kData, iterator.Value(), std::size(kData)));
174 ++iterator;
175 EXPECT_FALSE(iterator.Valid());
176 }
177
178 // Tests that the value iterator is okay with an empty key.
TEST_F(RegistryTest,ValueIteratorEmptyKey)179 TEST_F(RegistryTest, ValueIteratorEmptyKey) {
180 RegistryValueIterator iterator(HKEY_CURRENT_USER, root_key().c_str());
181 EXPECT_EQ(iterator.ValueCount(), 0U);
182 EXPECT_FALSE(iterator.Valid());
183 }
184
185 // Tests that the default value is seen by a value iterator.
TEST_F(RegistryTest,ValueIteratorDefaultValue)186 TEST_F(RegistryTest, ValueIteratorDefaultValue) {
187 const std::wstring_view kTestString(L"i miss you");
188 ASSERT_EQ(RegKey(HKEY_CURRENT_USER, root_key().c_str(), KEY_SET_VALUE)
189 .WriteValue(nullptr, kTestString.data()),
190 ERROR_SUCCESS);
191 RegistryValueIterator iterator(HKEY_CURRENT_USER, root_key().c_str());
192 EXPECT_EQ(iterator.ValueCount(), 1U);
193 ASSERT_TRUE(iterator.Valid());
194 EXPECT_EQ(std::wstring_view(iterator.Name()), std::wstring_view());
195 EXPECT_EQ(iterator.ValueSize(), (kTestString.size() + 1) * sizeof(wchar_t));
196 EXPECT_EQ(iterator.Type(), REG_SZ);
197 EXPECT_EQ(std::wstring_view(iterator.Value()), kTestString);
198 ++iterator;
199 EXPECT_FALSE(iterator.Valid());
200 }
201
TEST_F(RegistryTest,NonRecursiveDelete)202 TEST_F(RegistryTest, NonRecursiveDelete) {
203 RegKey key;
204 // Create root_key()
205 // \->Bar (TestValue)
206 // \->Foo (TestValue)
207 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, root_key().c_str(),
208 KEY_CREATE_SUB_KEY));
209 ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE));
210 ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData"));
211 ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
212 ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData"));
213 key.Close();
214
215 const std::wstring bar_path = root_key() + L"\\Bar";
216 // Non-recursive delete of Bar from root_key() should fail.
217 ASSERT_EQ(ERROR_SUCCESS,
218 key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_QUERY_VALUE));
219 ASSERT_NE(ERROR_SUCCESS,
220 key.DeleteKey(L"Bar", RegKey::RecursiveDelete(false)));
221 key.Close();
222 ASSERT_TRUE(
223 RegKey(HKEY_CURRENT_USER, bar_path.c_str(), KEY_QUERY_VALUE).Valid());
224
225 // Non-recursive delete of Bar from itself should fail.
226 ASSERT_EQ(ERROR_SUCCESS,
227 key.Open(HKEY_CURRENT_USER, bar_path.c_str(), KEY_QUERY_VALUE));
228 ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(L"", RegKey::RecursiveDelete(false)));
229 key.Close();
230 ASSERT_TRUE(
231 RegKey(HKEY_CURRENT_USER, root_key().c_str(), KEY_QUERY_VALUE).Valid());
232
233 // Non-recursive delete of the subkey and then root_key() should succeed.
234 ASSERT_EQ(ERROR_SUCCESS,
235 key.Open(HKEY_CURRENT_USER, bar_path.c_str(), KEY_QUERY_VALUE));
236 ASSERT_EQ(ERROR_SUCCESS,
237 key.DeleteKey(L"Foo", RegKey::RecursiveDelete(false)));
238 ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"", RegKey::RecursiveDelete(false)));
239 key.Close();
240 ASSERT_FALSE(
241 RegKey(HKEY_CURRENT_USER, bar_path.c_str(), KEY_QUERY_VALUE).Valid());
242 }
243
TEST_F(RegistryTest,RecursiveDelete)244 TEST_F(RegistryTest, RecursiveDelete) {
245 RegKey key;
246 // Create root_key()
247 // \->Bar (TestValue)
248 // \->Foo (TestValue)
249 // \->Bar
250 // \->Foo
251 // \->Moo
252 // \->Foo
253 // and delete root_key()
254 std::wstring key_path = root_key();
255 ASSERT_EQ(ERROR_SUCCESS,
256 key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_CREATE_SUB_KEY));
257 ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE));
258 ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData"));
259 ASSERT_EQ(ERROR_SUCCESS,
260 key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_CREATE_SUB_KEY));
261 ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Moo", KEY_WRITE));
262 ASSERT_EQ(ERROR_SUCCESS,
263 key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_CREATE_SUB_KEY));
264 ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
265
266 key_path += L"\\Bar";
267 ASSERT_EQ(ERROR_SUCCESS,
268 key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_CREATE_SUB_KEY));
269 key_path += L"\\Foo";
270 ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
271 ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData"));
272 ASSERT_EQ(ERROR_SUCCESS,
273 key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_READ));
274
275 ASSERT_EQ(ERROR_SUCCESS,
276 key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_CREATE_SUB_KEY));
277 ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE));
278 ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
279 ASSERT_EQ(ERROR_SUCCESS,
280 key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_WRITE));
281 ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L""));
282 ASSERT_NE(ERROR_SUCCESS,
283 key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_READ));
284
285 ASSERT_EQ(ERROR_SUCCESS,
286 key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_WRITE));
287 ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"Bar"));
288 ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(L"Bar"));
289 ASSERT_NE(ERROR_SUCCESS,
290 key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_READ));
291 }
292
TEST_F(RegistryTest,OpenSubKey)293 TEST_F(RegistryTest, OpenSubKey) {
294 RegKey key;
295 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, root_key().c_str(),
296 KEY_READ | KEY_CREATE_SUB_KEY));
297
298 ASSERT_NE(ERROR_SUCCESS, key.OpenKey(L"foo", KEY_READ));
299 ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"foo", KEY_READ));
300 ASSERT_EQ(ERROR_SUCCESS,
301 key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_READ));
302 ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(L"foo", KEY_READ));
303
304 std::wstring foo_key = root_key() + L"\\Foo";
305 ASSERT_EQ(ERROR_SUCCESS,
306 key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ));
307
308 ASSERT_EQ(ERROR_SUCCESS,
309 key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_WRITE));
310 ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"foo"));
311 }
312
TEST_F(RegistryTest,InvalidRelativeKeyCreate)313 TEST_F(RegistryTest, InvalidRelativeKeyCreate) {
314 RegKey key(HKEY_CURRENT_USER,
315 base::StrCat({this->root_key(), L"_DoesNotExist"}).c_str(),
316 KEY_WOW64_32KEY | KEY_READ);
317 ASSERT_EQ(key.CreateKey(L"SomeSubKey", KEY_WOW64_32KEY | KEY_WRITE),
318 ERROR_INVALID_HANDLE);
319 }
320
TEST_F(RegistryTest,InvalidRelativeKeyOpen)321 TEST_F(RegistryTest, InvalidRelativeKeyOpen) {
322 RegKey key(HKEY_CURRENT_USER,
323 base::StrCat({this->root_key(), L"_DoesNotExist"}).c_str(),
324 KEY_WOW64_32KEY | KEY_READ);
325 ASSERT_EQ(key.OpenKey(L"SomeSubKey", KEY_WOW64_32KEY | KEY_READ),
326 ERROR_INVALID_HANDLE);
327 }
328
329 namespace {
330
331 class TestChangeDelegate {
332 public:
333 TestChangeDelegate() = default;
334 ~TestChangeDelegate() = default;
335
OnKeyChanged(base::OnceClosure quit_closure)336 void OnKeyChanged(base::OnceClosure quit_closure) {
337 std::move(quit_closure).Run();
338 called_ = true;
339 }
340
WasCalled()341 bool WasCalled() {
342 bool was_called = called_;
343 called_ = false;
344 return was_called;
345 }
346
347 private:
348 bool called_ = false;
349 };
350
351 } // namespace
352
TEST_F(RegistryTest,ChangeCallback)353 TEST_F(RegistryTest, ChangeCallback) {
354 RegKey key;
355 TestChangeDelegate delegate;
356 test::TaskEnvironment task_environment;
357 base::RunLoop loop1;
358 base::RunLoop loop2;
359 base::RunLoop loop3;
360
361 ASSERT_EQ(ERROR_SUCCESS,
362 key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_READ));
363
364 ASSERT_TRUE(key.StartWatching(BindOnce(&TestChangeDelegate::OnKeyChanged,
365 Unretained(&delegate),
366 loop1.QuitWhenIdleClosure())));
367 EXPECT_FALSE(delegate.WasCalled());
368
369 // Make some change.
370 RegKey key2;
371 ASSERT_EQ(ERROR_SUCCESS, key2.Open(HKEY_CURRENT_USER, root_key().c_str(),
372 KEY_READ | KEY_SET_VALUE));
373 ASSERT_TRUE(key2.Valid());
374 EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(L"name", L"data"));
375
376 // Allow delivery of the notification.
377 EXPECT_FALSE(delegate.WasCalled());
378 loop1.Run();
379
380 ASSERT_TRUE(delegate.WasCalled());
381 EXPECT_FALSE(delegate.WasCalled());
382
383 ASSERT_TRUE(key.StartWatching(BindOnce(&TestChangeDelegate::OnKeyChanged,
384 Unretained(&delegate),
385 loop2.QuitWhenIdleClosure())));
386
387 // Change something else.
388 EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(L"name2", L"data2"));
389 loop2.Run();
390 ASSERT_TRUE(delegate.WasCalled());
391
392 ASSERT_TRUE(key.StartWatching(BindOnce(&TestChangeDelegate::OnKeyChanged,
393 Unretained(&delegate),
394 loop3.QuitWhenIdleClosure())));
395 loop3.RunUntilIdle();
396 EXPECT_FALSE(delegate.WasCalled());
397 }
398
399 namespace {
400
401 // A thread that runs tasks from a TestMockTimeTaskRunner.
402 class RegistryWatcherThread : public SimpleThread {
403 public:
RegistryWatcherThread(scoped_refptr<base::TestMockTimeTaskRunner> task_runner)404 explicit RegistryWatcherThread(
405 scoped_refptr<base::TestMockTimeTaskRunner> task_runner)
406 : SimpleThread("RegistryWatcherThread"),
407 task_runner_(std::move(task_runner)) {}
408
409 private:
Run()410 void Run() override {
411 task_runner_->DetachFromThread();
412 task_runner_->RunUntilIdle();
413 }
414 const scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
415 };
416
417 } // namespace
418
TEST_F(RegistryTest,WatcherNotSignaledOnInitiatingThreadExit)419 TEST_F(RegistryTest, WatcherNotSignaledOnInitiatingThreadExit) {
420 RegKey key;
421
422 ASSERT_EQ(key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_READ),
423 ERROR_SUCCESS);
424
425 auto test_task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
426 base::TestMockTimeTaskRunner::Type::kBoundToThread);
427 ::testing::StrictMock<base::MockCallback<base::win::RegKey::ChangeCallback>>
428 change_cb;
429
430 test_task_runner->PostTask(FROM_HERE,
431 BindOnce(IgnoreResult(&RegKey::StartWatching),
432 Unretained(&key), change_cb.Get()));
433
434 {
435 // Start the watch on a thread that then goes away.
436 RegistryWatcherThread watcher_thread(test_task_runner);
437 watcher_thread.Start();
438 watcher_thread.Join();
439 }
440
441 // Termination of the thread should not cause a notification to get sent.
442 ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(&change_cb));
443 test_task_runner->DetachFromThread();
444 ASSERT_FALSE(test_task_runner->HasPendingTask());
445
446 // Expect that a notification is sent when a change is made. Exit the run loop
447 // when this happens.
448 base::RunLoop run_loop;
449 EXPECT_CALL(change_cb, Run).WillOnce([&run_loop]() { run_loop.Quit(); });
450
451 // Make some change.
452 RegKey key2;
453 ASSERT_EQ(key2.Open(HKEY_CURRENT_USER, root_key().c_str(),
454 KEY_READ | KEY_SET_VALUE),
455 ERROR_SUCCESS);
456 ASSERT_TRUE(key2.Valid());
457 ASSERT_EQ(key2.WriteValue(L"name", L"data"), ERROR_SUCCESS);
458
459 // Wait for the watcher to be signaled.
460 run_loop.Run();
461 }
462
TEST_F(RegistryTest,TestMoveConstruct)463 TEST_F(RegistryTest, TestMoveConstruct) {
464 RegKey key;
465
466 ASSERT_EQ(key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_SET_VALUE),
467 ERROR_SUCCESS);
468 RegKey key2(std::move(key));
469
470 // The old key should be meaningless now.
471 EXPECT_EQ(key.Handle(), nullptr);
472
473 // And the new one should work just fine.
474 EXPECT_NE(key2.Handle(), nullptr);
475 EXPECT_EQ(key2.WriteValue(L"foo", 1U), ERROR_SUCCESS);
476 }
477
TEST_F(RegistryTest,TestMoveAssign)478 TEST_F(RegistryTest, TestMoveAssign) {
479 RegKey key;
480 RegKey key2;
481 const wchar_t kFooValueName[] = L"foo";
482
483 ASSERT_EQ(key.Open(HKEY_CURRENT_USER, root_key().c_str(),
484 KEY_SET_VALUE | KEY_QUERY_VALUE),
485 ERROR_SUCCESS);
486 ASSERT_EQ(key.WriteValue(kFooValueName, 1U), ERROR_SUCCESS);
487 ASSERT_EQ(key2.Create(HKEY_CURRENT_USER, (root_key() + L"\\child").c_str(),
488 KEY_SET_VALUE),
489 ERROR_SUCCESS);
490 key2 = std::move(key);
491
492 // The old key should be meaningless now.
493 EXPECT_EQ(key.Handle(), nullptr);
494
495 // And the new one should hold what was the old one.
496 EXPECT_NE(key2.Handle(), nullptr);
497 DWORD foo = 0;
498 ASSERT_EQ(key2.ReadValueDW(kFooValueName, &foo), ERROR_SUCCESS);
499 EXPECT_EQ(foo, 1U);
500 }
501
502 // Verify that either the platform, or the API-integration, causes deletion
503 // attempts via an invalid handle to fail with the expected error code.
TEST_F(RegistryTest,DeleteWithInvalidRegKey)504 TEST_F(RegistryTest, DeleteWithInvalidRegKey) {
505 RegKey key;
506
507 static const wchar_t kFooName[] = L"foo";
508
509 EXPECT_EQ(key.DeleteKey(kFooName), ERROR_INVALID_HANDLE);
510 EXPECT_EQ(key.DeleteValue(kFooName), ERROR_INVALID_HANDLE);
511 }
512
513 // A test harness for tests of RegKey::DeleteKey; parameterized on whether to
514 // perform non-recursive or recursive deletes.
515 class DeleteKeyRegistryTest
516 : public RegistryTest,
517 public ::testing::WithParamInterface<RegKey::RecursiveDelete> {
518 protected:
519 DeleteKeyRegistryTest() = default;
520
521 private:
522 };
523
524 // Test that DeleteKey does not follow symbolic links.
TEST_P(DeleteKeyRegistryTest,DoesNotFollowLinks)525 TEST_P(DeleteKeyRegistryTest, DoesNotFollowLinks) {
526 // Create a subkey that should not be deleted.
527 std::wstring target_path = root_key() + L"\\LinkTarget";
528 {
529 RegKey target;
530 ASSERT_EQ(target.Create(HKEY_CURRENT_USER, target_path.c_str(), KEY_WRITE),
531 ERROR_SUCCESS);
532 ASSERT_EQ(target.WriteValue(L"IsTarget", 1U), ERROR_SUCCESS);
533 }
534
535 // Create a link to the above key.
536 std::wstring source_path = root_key() + L"\\LinkSource";
537 {
538 HKEY link_handle = {};
539 ASSERT_EQ(RegCreateKeyEx(HKEY_CURRENT_USER, source_path.c_str(), 0, nullptr,
540 REG_OPTION_CREATE_LINK | REG_OPTION_NON_VOLATILE,
541 KEY_WRITE, nullptr, &link_handle, nullptr),
542 ERROR_SUCCESS);
543 RegKey link(std::exchange(link_handle, HKEY{}));
544 ASSERT_TRUE(link.Valid());
545
546 std::wstring user_sid;
547 ASSERT_TRUE(GetUserSidString(&user_sid));
548
549 std::wstring value =
550 base::StrCat({L"\\Registry\\User\\", user_sid, L"\\", override_path(),
551 L"\\", root_key(), L"\\LinkTarget"});
552 ASSERT_EQ(link.WriteValue(L"SymbolicLinkValue", value.data(),
553 value.size() * sizeof(wchar_t), REG_LINK),
554 ERROR_SUCCESS);
555 }
556
557 // Verify that the link works.
558 {
559 RegKey link;
560 ASSERT_EQ(link.Open(HKEY_CURRENT_USER, source_path.c_str(), KEY_READ),
561 ERROR_SUCCESS);
562 DWORD value = 0;
563 ASSERT_EQ(link.ReadValueDW(L"IsTarget", &value), ERROR_SUCCESS);
564 ASSERT_EQ(value, 1U);
565 }
566
567 // Now delete the link and ensure that it was deleted, but not the target.
568 ASSERT_EQ(RegKey(HKEY_CURRENT_USER, root_key().c_str(), KEY_READ)
569 .DeleteKey(L"LinkSource", GetParam()),
570 ERROR_SUCCESS);
571 {
572 RegKey source;
573 ASSERT_NE(source.Open(HKEY_CURRENT_USER, source_path.c_str(), KEY_READ),
574 ERROR_SUCCESS);
575 }
576 {
577 RegKey target;
578 ASSERT_EQ(target.Open(HKEY_CURRENT_USER, target_path.c_str(), KEY_READ),
579 ERROR_SUCCESS);
580 }
581 }
582
583 INSTANTIATE_TEST_SUITE_P(NonRecursive,
584 DeleteKeyRegistryTest,
585 ::testing::Values(RegKey::RecursiveDelete(false)));
586 INSTANTIATE_TEST_SUITE_P(Recursive,
587 DeleteKeyRegistryTest,
588 ::testing::Values(RegKey::RecursiveDelete(true)));
589
590 // A test harness for tests that use HKLM to test WoW redirection and such.
591 // TODO(https://crbug.com/377917): The tests here that write to the registry are
592 // disabled because they need work to handle parallel runs of different tests.
593 class RegistryTestHKLM : public ::testing::Test {
594 protected:
595 enum : REGSAM {
596 #if defined(_WIN64)
597 kNativeViewMask = KEY_WOW64_64KEY,
598 kRedirectedViewMask = KEY_WOW64_32KEY,
599 #else
600 kNativeViewMask = KEY_WOW64_32KEY,
601 kRedirectedViewMask = KEY_WOW64_64KEY,
602 #endif // _WIN64
603 };
604
RegistryTestHKLM()605 RegistryTestHKLM()
606 : foo_software_key_(std::wstring(L"Software\\") + kRootKey + L"\\Foo") {}
607
IsRedirectorPresent()608 static bool IsRedirectorPresent() {
609 #if defined(_WIN64)
610 return true;
611 #else
612 return OSInfo::GetInstance()->IsWowX86OnAMD64();
613 #endif
614 }
615
616 const std::wstring foo_software_key_;
617 };
618
619 class RegistryTestHKLMAdmin : public RegistryTestHKLM {
620 protected:
SetUp()621 void SetUp() override {
622 if (!IsRedirectorPresent()) {
623 GTEST_SKIP();
624 }
625 if (!::IsUserAnAdmin()) {
626 GTEST_SKIP();
627 }
628 // Clean up any stale registry keys.
629 for (const REGSAM mask :
630 {this->kNativeViewMask, this->kRedirectedViewMask}) {
631 RegKey key;
632 if (key.Open(HKEY_LOCAL_MACHINE, L"Software", KEY_SET_VALUE | mask) ==
633 ERROR_SUCCESS) {
634 key.DeleteKey(kRootKey);
635 }
636 }
637 }
638 };
639
640 // This test requires running as an Administrator as it tests redirected
641 // registry writes to HKLM\Software
642 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384253.aspx
TEST_F(RegistryTestHKLMAdmin,Wow64RedirectedFromNative)643 TEST_F(RegistryTestHKLMAdmin, Wow64RedirectedFromNative) {
644 RegKey key;
645
646 // Test redirected key access from non-redirected.
647 ASSERT_EQ(ERROR_SUCCESS,
648 key.Create(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(),
649 KEY_WRITE | kRedirectedViewMask));
650 ASSERT_NE(ERROR_SUCCESS,
651 key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(), KEY_READ));
652 ASSERT_NE(ERROR_SUCCESS,
653 key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(),
654 KEY_READ | kNativeViewMask));
655
656 // Open the non-redirected view of the parent and try to delete the test key.
657 ASSERT_EQ(ERROR_SUCCESS,
658 key.Open(HKEY_LOCAL_MACHINE, L"Software", KEY_SET_VALUE));
659 ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
660 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
661 KEY_SET_VALUE | kNativeViewMask));
662 ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
663
664 // Open the redirected view and delete the key created above.
665 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
666 KEY_SET_VALUE | kRedirectedViewMask));
667 ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey));
668 }
669
670 // Test for the issue found in http://crbug.com/384587 where OpenKey would call
671 // Close() and reset wow64_access_ flag to 0 and cause a NOTREACHED to hit on a
672 // subsequent OpenKey call.
TEST_F(RegistryTestHKLM,SameWowFlags)673 TEST_F(RegistryTestHKLM, SameWowFlags) {
674 RegKey key;
675
676 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
677 KEY_READ | KEY_WOW64_64KEY));
678 ASSERT_EQ(ERROR_SUCCESS,
679 key.OpenKey(L"Microsoft", KEY_READ | KEY_WOW64_64KEY));
680 ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(L"Windows", KEY_READ | KEY_WOW64_64KEY));
681 }
682
TEST_F(RegistryTestHKLMAdmin,Wow64NativeFromRedirected)683 TEST_F(RegistryTestHKLMAdmin, Wow64NativeFromRedirected) {
684 RegKey key;
685
686 // Test non-redirected key access from redirected.
687 ASSERT_EQ(ERROR_SUCCESS,
688 key.Create(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(),
689 KEY_WRITE | kNativeViewMask));
690 ASSERT_EQ(ERROR_SUCCESS,
691 key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(), KEY_READ));
692 ASSERT_NE(ERROR_SUCCESS,
693 key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(),
694 KEY_READ | kRedirectedViewMask));
695
696 // Open the redirected view of the parent and try to delete the test key
697 // from the non-redirected view.
698 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
699 KEY_SET_VALUE | kRedirectedViewMask));
700 ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
701
702 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
703 KEY_SET_VALUE | kNativeViewMask));
704 ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey));
705 }
706
707 } // namespace base::win
708