• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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