1 // Copyright 2018 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.
4
5 #include "chrome/chrome_cleaner/test/reboot_deletion_helper.h"
6
7 #include "base/files/scoped_temp_dir.h"
8 #include "base/logging.h"
9 #include "base/test/test_reg_util_win.h"
10 #include "base/win/registry.h"
11 #include "chrome/chrome_cleaner/os/disk_util.h"
12 #include "testing/gmock/include/gmock/gmock.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 namespace chrome_cleaner {
16
17 namespace {
18
19 // The moves-pending-reboot is a MULTISZ registry value in the HKLM part of the
20 // registry.
21 const wchar_t kSessionManagerKey[] =
22 L"SYSTEM\\CurrentControlSet\\Control\\Session Manager";
23 const wchar_t kPendingFileRenameOps[] = L"PendingFileRenameOperations";
24
25 const uint32_t kMaxRegistryValueLength = 1024;
26
27 const wchar_t kRemoveFile1[] = L"remove_one";
28 const wchar_t kRemoveFile2[] = L"remove_two";
29 const wchar_t kRemoveFile3[] = L"remove_three";
30 const wchar_t kRemoveFile4[] = L"remove_four";
31 const wchar_t kRemoveFile5[] = L"remove_five";
32 const wchar_t kDeleteFile[] = L"";
33 const BYTE kNoNullInvalidString[] = {12, 13};
34 const BYTE kMissingNullEntryString[] = {65, 0, 0, 0};
35 const BYTE kOddSizeEntryString[] = {0, 65, 0, 66, 0xFF, 0, 0, 0, 0};
36 const wchar_t kRemoveRawMultipleFiles[] =
37 L"remove_one\0\0remove_two\0remove_three\0\0\0";
38 const wchar_t kRemoveRawEmpty0[] = L"";
39 const wchar_t kRemoveRawEmpty1[] = L"\0";
40 const wchar_t kRemoveRawEmpty2[] = L"\0\0";
41 const wchar_t kRemoveRawCorrupt[] = L"file_with_no_ending_null";
42
TestPendingFileRenameOperations(const wchar_t * content,size_t content_size,PendingMoveVector * pending_moves)43 bool TestPendingFileRenameOperations(const wchar_t* content,
44 size_t content_size,
45 PendingMoveVector* pending_moves) {
46 DCHECK(content);
47 DCHECK(pending_moves);
48 pending_moves->clear();
49
50 base::win::RegKey session_manager_key(HKEY_LOCAL_MACHINE, kSessionManagerKey,
51 KEY_ALL_ACCESS);
52 if (!session_manager_key.Handle()) {
53 PLOG(ERROR) << "Can't open session manager.";
54 return false;
55 }
56
57 // Write raw content of the registry value.
58 if (session_manager_key.WriteValue(kPendingFileRenameOps, content,
59 content_size,
60 REG_MULTI_SZ) != ERROR_SUCCESS) {
61 PLOG(ERROR) << "Can't write to registry value '" << kPendingFileRenameOps
62 << "' value: '" << content << "'.";
63 return false;
64 }
65
66 // Read back the pending moves.
67 if (!GetPendingMoves(pending_moves)) {
68 PLOG(ERROR) << "Can't read back or parse the registry value '"
69 << kPendingFileRenameOps << "'.";
70 return false;
71 }
72
73 return true;
74 }
75
76 } // namespace
77
TEST(RebootDeletionHelper,GetPendingMovesRawValue)78 TEST(RebootDeletionHelper, GetPendingMovesRawValue) {
79 registry_util::RegistryOverrideManager registry_override;
80 registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
81
82 PendingMoveVector pending_moves;
83 EXPECT_TRUE(
84 TestPendingFileRenameOperations(kRemoveRawEmpty0, 0, &pending_moves));
85 EXPECT_TRUE(pending_moves.empty());
86
87 EXPECT_TRUE(TestPendingFileRenameOperations(
88 kRemoveRawEmpty0, sizeof(kRemoveRawEmpty0), &pending_moves));
89 EXPECT_TRUE(pending_moves.empty());
90
91 EXPECT_TRUE(TestPendingFileRenameOperations(
92 kRemoveRawEmpty1, sizeof(kRemoveRawEmpty1), &pending_moves));
93 EXPECT_TRUE(pending_moves.empty());
94
95 EXPECT_TRUE(TestPendingFileRenameOperations(
96 kRemoveRawEmpty2, sizeof(kRemoveRawEmpty2), &pending_moves));
97 EXPECT_TRUE(pending_moves.empty());
98
99 EXPECT_TRUE(TestPendingFileRenameOperations(kRemoveRawMultipleFiles,
100 sizeof(kRemoveRawMultipleFiles),
101 &pending_moves));
102 EXPECT_THAT(pending_moves,
103 testing::ElementsAre(std::make_pair(kRemoveFile1, kDeleteFile),
104 std::make_pair(kRemoveFile2, kRemoveFile3)));
105
106 const wchar_t kRemoveRawSingleFile[] = L"remove_one\0\0";
107 EXPECT_TRUE(TestPendingFileRenameOperations(
108 kRemoveRawSingleFile, sizeof(kRemoveRawSingleFile), &pending_moves));
109 EXPECT_THAT(pending_moves,
110 testing::ElementsAre(std::make_pair(kRemoveFile1, kDeleteFile)));
111
112 const wchar_t kRemoveRawRename0[] = L"remove_one\0remove_two";
113 EXPECT_TRUE(TestPendingFileRenameOperations(
114 kRemoveRawRename0, sizeof(kRemoveRawRename0), &pending_moves));
115 EXPECT_THAT(pending_moves,
116 testing::ElementsAre(std::make_pair(kRemoveFile1, kRemoveFile2)));
117
118 const wchar_t kRemoveRawRename1[] = L"remove_one\0remove_two\0";
119 EXPECT_TRUE(TestPendingFileRenameOperations(
120 kRemoveRawRename1, sizeof(kRemoveRawRename1), &pending_moves));
121 EXPECT_THAT(pending_moves,
122 testing::ElementsAre(std::make_pair(kRemoveFile1, kRemoveFile2)));
123
124 const wchar_t kRemoveRawRename2[] = L"remove_one\0remove_two\0\0";
125 EXPECT_TRUE(TestPendingFileRenameOperations(
126 kRemoveRawRename2, sizeof(kRemoveRawRename2), &pending_moves));
127 EXPECT_THAT(pending_moves,
128 testing::ElementsAre(std::make_pair(kRemoveFile1, kRemoveFile2)));
129
130 EXPECT_FALSE(TestPendingFileRenameOperations(
131 kRemoveRawCorrupt, sizeof(kRemoveRawCorrupt), &pending_moves));
132 }
133
TEST(RebootDeletionHelper,SetPendingMovesRawValue)134 TEST(RebootDeletionHelper, SetPendingMovesRawValue) {
135 registry_util::RegistryOverrideManager registry_override;
136 registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
137
138 base::win::RegKey session_manager_key(HKEY_LOCAL_MACHINE, kSessionManagerKey,
139 KEY_ALL_ACCESS);
140 EXPECT_TRUE(session_manager_key.Handle());
141
142 // Write some pending moves.
143 PendingMoveVector pending_moves;
144 pending_moves.push_back(std::make_pair(kRemoveFile1, kDeleteFile));
145 pending_moves.push_back(std::make_pair(kRemoveFile2, kRemoveFile3));
146
147 EXPECT_TRUE(SetPendingMoves(pending_moves));
148
149 wchar_t buffer[kMaxRegistryValueLength];
150 DWORD buffer_size = kMaxRegistryValueLength;
151 DWORD buffer_type = REG_NONE;
152
153 // Validate the raw content of the registry value.
154 EXPECT_EQ(ERROR_SUCCESS,
155 session_manager_key.ReadValue(kPendingFileRenameOps, &buffer[0],
156 &buffer_size, &buffer_type));
157
158 EXPECT_EQ(buffer_size, sizeof(kRemoveRawMultipleFiles));
159 EXPECT_EQ(0, ::memcmp(kRemoveRawMultipleFiles, buffer, buffer_size));
160 EXPECT_EQ(REG_MULTI_SZ, buffer_type);
161 }
162
TEST(RebootDeletionHelper,SetPendingMovesValueWithEmptyInput)163 TEST(RebootDeletionHelper, SetPendingMovesValueWithEmptyInput) {
164 registry_util::RegistryOverrideManager registry_override;
165 registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
166
167 base::win::RegKey session_manager_key(HKEY_LOCAL_MACHINE, kSessionManagerKey,
168 KEY_ALL_ACCESS);
169 EXPECT_TRUE(session_manager_key.Handle());
170
171 // Write raw content of the registry value.
172 EXPECT_EQ(ERROR_SUCCESS, session_manager_key.WriteValue(
173 kPendingFileRenameOps, kRemoveRawMultipleFiles,
174 sizeof(kRemoveRawMultipleFiles), REG_MULTI_SZ));
175
176 // Write an empty pending moves vector.
177 PendingMoveVector pending_moves;
178 SetPendingMoves(pending_moves);
179
180 // The registry value must be removed.
181 EXPECT_FALSE(session_manager_key.HasValue(kPendingFileRenameOps));
182 }
183
TEST(RebootDeletionHelper,GetAndSetPendingMoves)184 TEST(RebootDeletionHelper, GetAndSetPendingMoves) {
185 // Declare the scoped temp dir before the registry override manager because
186 // the recursive deletion of folders fail when running elevated while a
187 // registry override is active.
188 base::ScopedTempDir temp;
189 ASSERT_TRUE(temp.CreateUniqueTempDir());
190
191 registry_util::RegistryOverrideManager registry_override;
192 registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
193
194 // Expect the registry key to be empty.
195 PendingMoveVector pending_moves;
196 EXPECT_TRUE(GetPendingMoves(&pending_moves));
197 ASSERT_TRUE(pending_moves.empty());
198
199 // Write back some moves into the registry key.
200 base::FilePath file_path1 = temp.GetPath().Append(kRemoveFile1);
201 base::FilePath file_path2 = temp.GetPath().Append(kRemoveFile2);
202 base::FilePath file_path3 = temp.GetPath().Append(kRemoveFile3);
203
204 pending_moves.push_back(
205 std::make_pair(file_path1.value(), file_path2.value()));
206 pending_moves.push_back(std::make_pair(file_path3.value(), kDeleteFile));
207
208 EXPECT_TRUE(SetPendingMoves(pending_moves));
209
210 // Read back the written moves.
211 PendingMoveVector written_moves;
212 EXPECT_TRUE(GetPendingMoves(&written_moves));
213
214 // Validate that moves before and after serialisation are the same.
215 EXPECT_THAT(written_moves, testing::ElementsAreArray(pending_moves));
216
217 // Write back an empty set of moves.
218 pending_moves.clear();
219 EXPECT_TRUE(SetPendingMoves(pending_moves));
220 EXPECT_TRUE(GetPendingMoves(&written_moves));
221 EXPECT_TRUE(written_moves.empty());
222 }
223
TEST(RebootDeletionHelper,GetInvalidPendingMoves)224 TEST(RebootDeletionHelper, GetInvalidPendingMoves) {
225 // Open the pending registry key to write into invalid data.
226 registry_util::RegistryOverrideManager registry_override;
227 registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
228
229 base::win::RegKey session_manager_key(HKEY_LOCAL_MACHINE, kSessionManagerKey,
230 KEY_CREATE_SUB_KEY | KEY_SET_VALUE);
231 EXPECT_TRUE(session_manager_key.Handle());
232
233 // Write an invalid pending moves registry key with a wrong type.
234 EXPECT_EQ(ERROR_SUCCESS,
235 session_manager_key.WriteValue(kPendingFileRenameOps, 0xCCCCCCCC));
236 PendingMoveVector pending_moves;
237 EXPECT_FALSE(GetPendingMoves(&pending_moves));
238
239 // Write an invalid pending moves registry key with an invalid string format.
240 EXPECT_EQ(ERROR_SUCCESS, session_manager_key.WriteValue(
241 kPendingFileRenameOps, kNoNullInvalidString,
242 sizeof(kNoNullInvalidString), REG_MULTI_SZ));
243 EXPECT_FALSE(GetPendingMoves(&pending_moves));
244
245 // Write an invalid string without the ending empty entry.
246 EXPECT_EQ(ERROR_SUCCESS, session_manager_key.WriteValue(
247 kPendingFileRenameOps, kMissingNullEntryString,
248 sizeof(kMissingNullEntryString), REG_MULTI_SZ));
249 EXPECT_FALSE(GetPendingMoves(&pending_moves));
250
251 // Write an invalid string with an odd number of bytes.
252 EXPECT_EQ(ERROR_SUCCESS, session_manager_key.WriteValue(
253 kPendingFileRenameOps, kOddSizeEntryString,
254 sizeof(kOddSizeEntryString), REG_MULTI_SZ));
255 EXPECT_TRUE(GetPendingMoves(&pending_moves));
256 }
257
TEST(RebootDeletionHelper,UnregisterPostRebootRemovals)258 TEST(RebootDeletionHelper, UnregisterPostRebootRemovals) {
259 // Declare the scoped temp dir before the registry override manager because
260 // the recursive deletion of folders fail when running elevated while a
261 // registry override is active.
262 base::ScopedTempDir temp;
263 ASSERT_TRUE(temp.CreateUniqueTempDir());
264
265 registry_util::RegistryOverrideManager registry_override;
266 registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
267
268 // Write some moves and renamings into the registry key.
269 base::FilePath file_path1 = temp.GetPath().Append(kRemoveFile1);
270 base::FilePath file_path2 = temp.GetPath().Append(kRemoveFile2);
271 base::FilePath file_path3 = temp.GetPath().Append(kRemoveFile3);
272 base::FilePath file_path4 = temp.GetPath().Append(kRemoveFile4);
273 base::FilePath file_path5 = temp.GetPath().Append(kRemoveFile5);
274
275 PendingMoveVector pending_moves;
276 pending_moves.push_back(std::make_pair(file_path1.value(), kDeleteFile));
277 pending_moves.push_back(std::make_pair(file_path2.value(), kDeleteFile));
278 pending_moves.push_back(std::make_pair(file_path3.value(), kDeleteFile));
279 pending_moves.push_back(
280 std::make_pair(file_path4.value(), file_path5.value()));
281
282 EXPECT_TRUE(SetPendingMoves(pending_moves));
283
284 // Unregister paths from the pending moves.
285 FilePathSet paths;
286 paths.Insert(file_path2);
287 paths.Insert(file_path3);
288 // A file renaming action must not be removed, even if a unregister is
289 // requested.
290 paths.Insert(file_path4);
291 EXPECT_TRUE(UnregisterPostRebootRemovals(paths));
292
293 // Read back the pending moves.
294 PendingMoveVector written_moves;
295 EXPECT_TRUE(GetPendingMoves(&written_moves));
296 EXPECT_THAT(written_moves,
297 testing::ElementsAre(
298 std::make_pair(file_path1.value(), kDeleteFile),
299 std::make_pair(file_path4.value(), file_path5.value())));
300 }
301
TEST(RebootDeletionHelper,UnregisterPostRebootRemovalsOfQualifiedPath)302 TEST(RebootDeletionHelper, UnregisterPostRebootRemovalsOfQualifiedPath) {
303 registry_util::RegistryOverrideManager registry_override;
304 registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
305
306 // Add a pending deletion with a qualified path.
307 PendingMoveVector pending_moves;
308 pending_moves.push_back(
309 std::make_pair(L"\\??\\C:\\this_file_doesnt_exist", kDeleteFile));
310 EXPECT_TRUE(SetPendingMoves(pending_moves));
311
312 // Try to remove it with an unqualified path.
313 FilePathSet paths;
314 paths.Insert(base::FilePath(FILE_PATH_LITERAL("C:\\this_file_doesnt_exist")));
315 EXPECT_TRUE(UnregisterPostRebootRemovals(paths));
316
317 // Read back the pending moves.
318 PendingMoveVector written_moves;
319 EXPECT_TRUE(GetPendingMoves(&written_moves));
320 EXPECT_TRUE(written_moves.empty());
321 }
322
323 } // namespace chrome_cleaner
324