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 StringPiece'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()336 void OnKeyChanged() {
337 RunLoop::QuitCurrentWhenIdleDeprecated();
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
358 ASSERT_EQ(ERROR_SUCCESS,
359 key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_READ));
360
361 ASSERT_TRUE(key.StartWatching(
362 BindOnce(&TestChangeDelegate::OnKeyChanged, Unretained(&delegate))));
363 EXPECT_FALSE(delegate.WasCalled());
364
365 // Make some change.
366 RegKey key2;
367 ASSERT_EQ(ERROR_SUCCESS, key2.Open(HKEY_CURRENT_USER, root_key().c_str(),
368 KEY_READ | KEY_SET_VALUE));
369 ASSERT_TRUE(key2.Valid());
370 EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(L"name", L"data"));
371
372 // Allow delivery of the notification.
373 EXPECT_FALSE(delegate.WasCalled());
374 RunLoop().Run();
375
376 ASSERT_TRUE(delegate.WasCalled());
377 EXPECT_FALSE(delegate.WasCalled());
378
379 ASSERT_TRUE(key.StartWatching(
380 BindOnce(&TestChangeDelegate::OnKeyChanged, Unretained(&delegate))));
381
382 // Change something else.
383 EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(L"name2", L"data2"));
384 RunLoop().Run();
385 ASSERT_TRUE(delegate.WasCalled());
386
387 ASSERT_TRUE(key.StartWatching(
388 BindOnce(&TestChangeDelegate::OnKeyChanged, Unretained(&delegate))));
389 RunLoop().RunUntilIdle();
390 EXPECT_FALSE(delegate.WasCalled());
391 }
392
393 namespace {
394
395 // A thread that runs tasks from a TestMockTimeTaskRunner.
396 class RegistryWatcherThread : public SimpleThread {
397 public:
RegistryWatcherThread(scoped_refptr<base::TestMockTimeTaskRunner> task_runner)398 explicit RegistryWatcherThread(
399 scoped_refptr<base::TestMockTimeTaskRunner> task_runner)
400 : SimpleThread("RegistryWatcherThread"),
401 task_runner_(std::move(task_runner)) {}
402
403 private:
Run()404 void Run() override {
405 task_runner_->DetachFromThread();
406 task_runner_->RunUntilIdle();
407 }
408 const scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
409 };
410
411 } // namespace
412
TEST_F(RegistryTest,WatcherNotSignaledOnInitiatingThreadExit)413 TEST_F(RegistryTest, WatcherNotSignaledOnInitiatingThreadExit) {
414 RegKey key;
415
416 ASSERT_EQ(key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_READ),
417 ERROR_SUCCESS);
418
419 auto test_task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
420 base::TestMockTimeTaskRunner::Type::kBoundToThread);
421 ::testing::StrictMock<base::MockCallback<base::win::RegKey::ChangeCallback>>
422 change_cb;
423
424 test_task_runner->PostTask(FROM_HERE,
425 BindOnce(IgnoreResult(&RegKey::StartWatching),
426 Unretained(&key), change_cb.Get()));
427
428 {
429 // Start the watch on a thread that then goes away.
430 RegistryWatcherThread watcher_thread(test_task_runner);
431 watcher_thread.Start();
432 watcher_thread.Join();
433 }
434
435 // Termination of the thread should not cause a notification to get sent.
436 ASSERT_TRUE(::testing::Mock::VerifyAndClearExpectations(&change_cb));
437 test_task_runner->DetachFromThread();
438 ASSERT_FALSE(test_task_runner->HasPendingTask());
439
440 // Expect that a notification is sent when a change is made. Exit the run loop
441 // when this happens.
442 base::RunLoop run_loop;
443 EXPECT_CALL(change_cb, Run).WillOnce([&run_loop]() { run_loop.Quit(); });
444
445 // Make some change.
446 RegKey key2;
447 ASSERT_EQ(key2.Open(HKEY_CURRENT_USER, root_key().c_str(),
448 KEY_READ | KEY_SET_VALUE),
449 ERROR_SUCCESS);
450 ASSERT_TRUE(key2.Valid());
451 ASSERT_EQ(key2.WriteValue(L"name", L"data"), ERROR_SUCCESS);
452
453 // Wait for the watcher to be signaled.
454 run_loop.Run();
455 }
456
TEST_F(RegistryTest,TestMoveConstruct)457 TEST_F(RegistryTest, TestMoveConstruct) {
458 RegKey key;
459
460 ASSERT_EQ(key.Open(HKEY_CURRENT_USER, root_key().c_str(), KEY_SET_VALUE),
461 ERROR_SUCCESS);
462 RegKey key2(std::move(key));
463
464 // The old key should be meaningless now.
465 EXPECT_EQ(key.Handle(), nullptr);
466
467 // And the new one should work just fine.
468 EXPECT_NE(key2.Handle(), nullptr);
469 EXPECT_EQ(key2.WriteValue(L"foo", 1U), ERROR_SUCCESS);
470 }
471
TEST_F(RegistryTest,TestMoveAssign)472 TEST_F(RegistryTest, TestMoveAssign) {
473 RegKey key;
474 RegKey key2;
475 const wchar_t kFooValueName[] = L"foo";
476
477 ASSERT_EQ(key.Open(HKEY_CURRENT_USER, root_key().c_str(),
478 KEY_SET_VALUE | KEY_QUERY_VALUE),
479 ERROR_SUCCESS);
480 ASSERT_EQ(key.WriteValue(kFooValueName, 1U), ERROR_SUCCESS);
481 ASSERT_EQ(key2.Create(HKEY_CURRENT_USER, (root_key() + L"\\child").c_str(),
482 KEY_SET_VALUE),
483 ERROR_SUCCESS);
484 key2 = std::move(key);
485
486 // The old key should be meaningless now.
487 EXPECT_EQ(key.Handle(), nullptr);
488
489 // And the new one should hold what was the old one.
490 EXPECT_NE(key2.Handle(), nullptr);
491 DWORD foo = 0;
492 ASSERT_EQ(key2.ReadValueDW(kFooValueName, &foo), ERROR_SUCCESS);
493 EXPECT_EQ(foo, 1U);
494 }
495
496 // Verify that either the platform, or the API-integration, causes deletion
497 // attempts via an invalid handle to fail with the expected error code.
TEST_F(RegistryTest,DeleteWithInvalidRegKey)498 TEST_F(RegistryTest, DeleteWithInvalidRegKey) {
499 RegKey key;
500
501 static const wchar_t kFooName[] = L"foo";
502
503 EXPECT_EQ(key.DeleteKey(kFooName), ERROR_INVALID_HANDLE);
504 EXPECT_EQ(key.DeleteValue(kFooName), ERROR_INVALID_HANDLE);
505 }
506
507 // A test harness for tests of RegKey::DeleteKey; parameterized on whether to
508 // perform non-recursive or recursive deletes.
509 class DeleteKeyRegistryTest
510 : public RegistryTest,
511 public ::testing::WithParamInterface<RegKey::RecursiveDelete> {
512 protected:
513 DeleteKeyRegistryTest() = default;
514
515 private:
516 };
517
518 // Test that DeleteKey does not follow symbolic links.
TEST_P(DeleteKeyRegistryTest,DoesNotFollowLinks)519 TEST_P(DeleteKeyRegistryTest, DoesNotFollowLinks) {
520 // Create a subkey that should not be deleted.
521 std::wstring target_path = root_key() + L"\\LinkTarget";
522 {
523 RegKey target;
524 ASSERT_EQ(target.Create(HKEY_CURRENT_USER, target_path.c_str(), KEY_WRITE),
525 ERROR_SUCCESS);
526 ASSERT_EQ(target.WriteValue(L"IsTarget", 1U), ERROR_SUCCESS);
527 }
528
529 // Create a link to the above key.
530 std::wstring source_path = root_key() + L"\\LinkSource";
531 {
532 HKEY link_handle = {};
533 ASSERT_EQ(RegCreateKeyEx(HKEY_CURRENT_USER, source_path.c_str(), 0, nullptr,
534 REG_OPTION_CREATE_LINK | REG_OPTION_NON_VOLATILE,
535 KEY_WRITE, nullptr, &link_handle, nullptr),
536 ERROR_SUCCESS);
537 RegKey link(std::exchange(link_handle, HKEY{}));
538 ASSERT_TRUE(link.Valid());
539
540 std::wstring user_sid;
541 ASSERT_TRUE(GetUserSidString(&user_sid));
542
543 std::wstring value =
544 base::StrCat({L"\\Registry\\User\\", user_sid, L"\\", override_path(),
545 L"\\", root_key(), L"\\LinkTarget"});
546 ASSERT_EQ(link.WriteValue(L"SymbolicLinkValue", value.data(),
547 value.size() * sizeof(wchar_t), REG_LINK),
548 ERROR_SUCCESS);
549 }
550
551 // Verify that the link works.
552 {
553 RegKey link;
554 ASSERT_EQ(link.Open(HKEY_CURRENT_USER, source_path.c_str(), KEY_READ),
555 ERROR_SUCCESS);
556 DWORD value = 0;
557 ASSERT_EQ(link.ReadValueDW(L"IsTarget", &value), ERROR_SUCCESS);
558 ASSERT_EQ(value, 1U);
559 }
560
561 // Now delete the link and ensure that it was deleted, but not the target.
562 ASSERT_EQ(RegKey(HKEY_CURRENT_USER, root_key().c_str(), KEY_READ)
563 .DeleteKey(L"LinkSource", GetParam()),
564 ERROR_SUCCESS);
565 {
566 RegKey source;
567 ASSERT_NE(source.Open(HKEY_CURRENT_USER, source_path.c_str(), KEY_READ),
568 ERROR_SUCCESS);
569 }
570 {
571 RegKey target;
572 ASSERT_EQ(target.Open(HKEY_CURRENT_USER, target_path.c_str(), KEY_READ),
573 ERROR_SUCCESS);
574 }
575 }
576
577 INSTANTIATE_TEST_SUITE_P(NonRecursive,
578 DeleteKeyRegistryTest,
579 ::testing::Values(RegKey::RecursiveDelete(false)));
580 INSTANTIATE_TEST_SUITE_P(Recursive,
581 DeleteKeyRegistryTest,
582 ::testing::Values(RegKey::RecursiveDelete(true)));
583
584 // A test harness for tests that use HKLM to test WoW redirection and such.
585 // TODO(https://crbug.com/377917): The tests here that write to the registry are
586 // disabled because they need work to handle parallel runs of different tests.
587 class RegistryTestHKLM : public ::testing::Test {
588 protected:
589 enum : REGSAM {
590 #if defined(_WIN64)
591 kNativeViewMask = KEY_WOW64_64KEY,
592 kRedirectedViewMask = KEY_WOW64_32KEY,
593 #else
594 kNativeViewMask = KEY_WOW64_32KEY,
595 kRedirectedViewMask = KEY_WOW64_64KEY,
596 #endif // _WIN64
597 };
598
RegistryTestHKLM()599 RegistryTestHKLM()
600 : foo_software_key_(std::wstring(L"Software\\") + kRootKey + L"\\Foo") {}
601
IsRedirectorPresent()602 static bool IsRedirectorPresent() {
603 #if defined(_WIN64)
604 return true;
605 #else
606 return OSInfo::GetInstance()->IsWowX86OnAMD64();
607 #endif
608 }
609
610 const std::wstring foo_software_key_;
611 };
612
613 class RegistryTestHKLMAdmin : public RegistryTestHKLM {
614 protected:
SetUp()615 void SetUp() override {
616 if (!IsRedirectorPresent()) {
617 GTEST_SKIP();
618 }
619 if (!::IsUserAnAdmin()) {
620 GTEST_SKIP();
621 }
622 // Clean up any stale registry keys.
623 for (const REGSAM mask :
624 {this->kNativeViewMask, this->kRedirectedViewMask}) {
625 RegKey key;
626 if (key.Open(HKEY_LOCAL_MACHINE, L"Software", KEY_SET_VALUE | mask) ==
627 ERROR_SUCCESS) {
628 key.DeleteKey(kRootKey);
629 }
630 }
631 }
632 };
633
634 // This test requires running as an Administrator as it tests redirected
635 // registry writes to HKLM\Software
636 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384253.aspx
TEST_F(RegistryTestHKLMAdmin,Wow64RedirectedFromNative)637 TEST_F(RegistryTestHKLMAdmin, Wow64RedirectedFromNative) {
638 RegKey key;
639
640 // Test redirected key access from non-redirected.
641 ASSERT_EQ(ERROR_SUCCESS,
642 key.Create(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(),
643 KEY_WRITE | kRedirectedViewMask));
644 ASSERT_NE(ERROR_SUCCESS,
645 key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(), KEY_READ));
646 ASSERT_NE(ERROR_SUCCESS,
647 key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(),
648 KEY_READ | kNativeViewMask));
649
650 // Open the non-redirected view of the parent and try to delete the test key.
651 ASSERT_EQ(ERROR_SUCCESS,
652 key.Open(HKEY_LOCAL_MACHINE, L"Software", KEY_SET_VALUE));
653 ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
654 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
655 KEY_SET_VALUE | kNativeViewMask));
656 ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
657
658 // Open the redirected view and delete the key created above.
659 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
660 KEY_SET_VALUE | kRedirectedViewMask));
661 ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey));
662 }
663
664 // Test for the issue found in http://crbug.com/384587 where OpenKey would call
665 // Close() and reset wow64_access_ flag to 0 and cause a NOTREACHED to hit on a
666 // subsequent OpenKey call.
TEST_F(RegistryTestHKLM,SameWowFlags)667 TEST_F(RegistryTestHKLM, SameWowFlags) {
668 RegKey key;
669
670 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
671 KEY_READ | KEY_WOW64_64KEY));
672 ASSERT_EQ(ERROR_SUCCESS,
673 key.OpenKey(L"Microsoft", KEY_READ | KEY_WOW64_64KEY));
674 ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(L"Windows", KEY_READ | KEY_WOW64_64KEY));
675 }
676
TEST_F(RegistryTestHKLMAdmin,Wow64NativeFromRedirected)677 TEST_F(RegistryTestHKLMAdmin, Wow64NativeFromRedirected) {
678 RegKey key;
679
680 // Test non-redirected key access from redirected.
681 ASSERT_EQ(ERROR_SUCCESS,
682 key.Create(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(),
683 KEY_WRITE | kNativeViewMask));
684 ASSERT_EQ(ERROR_SUCCESS,
685 key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(), KEY_READ));
686 ASSERT_NE(ERROR_SUCCESS,
687 key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(),
688 KEY_READ | kRedirectedViewMask));
689
690 // Open the redirected view of the parent and try to delete the test key
691 // from the non-redirected view.
692 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
693 KEY_SET_VALUE | kRedirectedViewMask));
694 ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
695
696 ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_LOCAL_MACHINE, L"Software",
697 KEY_SET_VALUE | kNativeViewMask));
698 ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey));
699 }
700
701 } // namespace base::win
702