1 //
2 // Copyright (C) 2012 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "update_engine/payload_consumer/filesystem_verifier_action.h"
18
19 #include <algorithm>
20 #include <cstring>
21 #include <memory>
22 #include <string>
23 #include <utility>
24
25 #include <base/bind.h>
26 #include <base/posix/eintr_wrapper.h>
27 #include <brillo/message_loops/fake_message_loop.h>
28 #include <brillo/message_loops/message_loop_utils.h>
29 #include <brillo/secure_blob.h>
30 #include <fec/ecc.h>
31 #include <gtest/gtest.h>
32 #include <libsnapshot/cow_writer.h>
33 #include <sys/stat.h>
34
35 #include "update_engine/common/dynamic_partition_control_stub.h"
36 #include "update_engine/common/hash_calculator.h"
37 #include "update_engine/common/mock_dynamic_partition_control.h"
38 #include "update_engine/common/test_utils.h"
39 #include "update_engine/common/utils.h"
40 #include "update_engine/payload_consumer/install_plan.h"
41 #include "update_engine/payload_consumer/verity_writer_android.h"
42
43 using brillo::MessageLoop;
44 using std::string;
45 using testing::_;
46 using testing::AtLeast;
47 using testing::DoAll;
48 using testing::NiceMock;
49 using testing::Return;
50 using testing::SetArgPointee;
51
52 namespace chromeos_update_engine {
53
54 class FilesystemVerifierActionTest : public ::testing::Test {
55 public:
56 static constexpr size_t BLOCK_SIZE = 4096;
57 // We use SHA256 for testing, so hash size is 256bits / 8
58 static constexpr size_t HASH_SIZE = 256 / 8;
59 static constexpr size_t PARTITION_SIZE = BLOCK_SIZE * 1024;
60 static constexpr size_t HASH_TREE_START_OFFSET = 800 * BLOCK_SIZE;
61 size_t hash_tree_size = 0;
62 size_t fec_start_offset = 0;
63 size_t fec_data_size = 0;
64 static constexpr size_t FEC_ROOTS = 2;
65 size_t fec_rounds = 0;
66 size_t fec_size = 0;
67
68 protected:
SetUp()69 void SetUp() override {
70 hash_tree_size = HashTreeBuilder::CalculateSize(
71 HASH_TREE_START_OFFSET, BLOCK_SIZE, HASH_SIZE);
72 fec_start_offset = HASH_TREE_START_OFFSET + hash_tree_size;
73 fec_data_size = fec_start_offset;
74 static constexpr size_t FEC_ROOTS = 2;
75 fec_rounds =
76 utils::DivRoundUp(fec_data_size / BLOCK_SIZE, FEC_RSM - FEC_ROOTS);
77 fec_size = fec_rounds * FEC_ROOTS * BLOCK_SIZE;
78
79 fec_data_.resize(fec_size);
80 hash_tree_data_.resize(hash_tree_size);
81 // Globally readable writable, as we want to write data
82 ASSERT_EQ(0, fchmod(source_part_.fd(), 0666))
83 << " Failed to set " << source_part_.path() << " as writable "
84 << strerror(errno);
85 ASSERT_EQ(0, fchmod(target_part_.fd(), 0666))
86 << " Failed to set " << target_part_.path() << " as writable "
87 << strerror(errno);
88 brillo::Blob part_data(PARTITION_SIZE);
89 test_utils::FillWithData(&part_data);
90 ASSERT_TRUE(utils::WriteFile(
91 source_part_.path().c_str(), part_data.data(), part_data.size()));
92 // FillWithData() will fill with different data next call. We want
93 // source/target partitions to contain different data for testing.
94 test_utils::FillWithData(&part_data);
95 ASSERT_TRUE(utils::WriteFile(
96 target_part_.path().c_str(), part_data.data(), part_data.size()));
97 loop_.SetAsCurrent();
98 }
99
TearDown()100 void TearDown() override {
101 EXPECT_EQ(0, brillo::MessageLoopRunMaxIterations(&loop_, 1));
102 }
103
104 void DoTestVABC(bool clear_target_hash, bool enable_verity);
105
106 // Returns true iff test has completed successfully.
107 bool DoTest(bool terminate_early, bool hash_fail);
108
109 void BuildActions(const InstallPlan& install_plan);
110 void BuildActions(const InstallPlan& install_plan,
111 DynamicPartitionControlInterface* dynamic_control);
112
AddFakePartition(InstallPlan * install_plan,std::string name="fake_part")113 InstallPlan::Partition* AddFakePartition(InstallPlan* install_plan,
114 std::string name = "fake_part") {
115 InstallPlan::Partition& part = install_plan->partitions.emplace_back();
116 part.name = name;
117 part.target_path = target_part_.path();
118 part.readonly_target_path = part.target_path;
119 part.target_size = PARTITION_SIZE;
120 part.block_size = BLOCK_SIZE;
121 part.source_path = source_part_.path();
122 part.source_size = PARTITION_SIZE;
123 EXPECT_TRUE(
124 HashCalculator::RawHashOfFile(source_part_.path(), &part.source_hash));
125 EXPECT_TRUE(
126 HashCalculator::RawHashOfFile(target_part_.path(), &part.target_hash));
127 return ∂
128 }
ZeroRange(FileDescriptorPtr fd,size_t start_block,size_t num_blocks)129 static void ZeroRange(FileDescriptorPtr fd,
130 size_t start_block,
131 size_t num_blocks) {
132 std::vector<unsigned char> buffer(BLOCK_SIZE);
133 ASSERT_EQ((ssize_t)(start_block * BLOCK_SIZE),
134 fd->Seek(start_block * BLOCK_SIZE, SEEK_SET));
135 for (size_t i = 0; i < num_blocks; i++) {
136 ASSERT_TRUE(utils::WriteAll(fd, buffer.data(), buffer.size()));
137 }
138 }
139
SetHashWithVerity(InstallPlan::Partition * partition)140 void SetHashWithVerity(InstallPlan::Partition* partition) {
141 partition->hash_tree_algorithm = "sha256";
142 partition->hash_tree_size = hash_tree_size;
143 partition->hash_tree_offset = HASH_TREE_START_OFFSET;
144 partition->hash_tree_data_offset = 0;
145 partition->hash_tree_data_size = HASH_TREE_START_OFFSET;
146 partition->fec_size = fec_size;
147 partition->fec_offset = fec_start_offset;
148 partition->fec_data_offset = 0;
149 partition->fec_data_size = fec_data_size;
150 partition->fec_roots = FEC_ROOTS;
151 VerityWriterAndroid verity_writer;
152 ASSERT_TRUE(verity_writer.Init(*partition));
153 LOG(INFO) << "Opening " << partition->readonly_target_path;
154 auto fd = std::make_shared<EintrSafeFileDescriptor>();
155 ASSERT_TRUE(fd->Open(partition->readonly_target_path.c_str(), O_RDWR))
156 << "Failed to open " << partition->target_path.c_str() << " "
157 << strerror(errno);
158 std::vector<unsigned char> buffer(BLOCK_SIZE);
159 // Only need to read up to hash tree
160 auto bytes_to_read = HASH_TREE_START_OFFSET;
161 auto offset = 0;
162 while (bytes_to_read > 0) {
163 const auto bytes_read = fd->Read(
164 buffer.data(), std::min<size_t>(buffer.size(), bytes_to_read));
165 ASSERT_GT(bytes_read, 0)
166 << "offset: " << offset << " bytes to read: " << bytes_to_read
167 << " error: " << strerror(errno);
168 ASSERT_TRUE(verity_writer.Update(offset, buffer.data(), bytes_read));
169 bytes_to_read -= bytes_read;
170 offset += bytes_read;
171 }
172 ASSERT_TRUE(verity_writer.Finalize(fd.get(), fd.get()));
173 ASSERT_TRUE(fd->IsOpen());
174 ASSERT_TRUE(HashCalculator::RawHashOfFile(target_part_.path(),
175 &partition->target_hash));
176
177 ASSERT_TRUE(fd->Seek(HASH_TREE_START_OFFSET, SEEK_SET));
178 ASSERT_EQ(fd->Read(hash_tree_data_.data(), hash_tree_data_.size()),
179 static_cast<ssize_t>(hash_tree_data_.size()))
180 << "Failed to read hashtree " << strerror(errno);
181 ASSERT_TRUE(fd->Seek(fec_start_offset, SEEK_SET));
182 ASSERT_EQ(fd->Read(fec_data_.data(), fec_data_.size()),
183 static_cast<ssize_t>(fec_data_.size()))
184 << "Failed to read FEC " << strerror(errno);
185 // Fs verification action is expected to write them, so clear verity data to
186 // ensure that they are re-created correctly.
187 ZeroRange(
188 fd, HASH_TREE_START_OFFSET / BLOCK_SIZE, hash_tree_size / BLOCK_SIZE);
189 ZeroRange(fd, fec_start_offset / BLOCK_SIZE, fec_size / BLOCK_SIZE);
190 }
191
192 brillo::FakeMessageLoop loop_{nullptr};
193 ActionProcessor processor_;
194 DynamicPartitionControlStub dynamic_control_stub_;
195 std::vector<unsigned char> fec_data_;
196 std::vector<unsigned char> hash_tree_data_;
197 static ScopedTempFile source_part_;
198 static ScopedTempFile target_part_;
199 InstallPlan install_plan_;
200 };
201
202 ScopedTempFile FilesystemVerifierActionTest::source_part_{
203 "source_part.XXXXXX", true, PARTITION_SIZE};
204 ScopedTempFile FilesystemVerifierActionTest::target_part_{
205 "target_part.XXXXXX", true, PARTITION_SIZE};
206
EnableVABC(MockDynamicPartitionControl * dynamic_control,const std::string & part_name)207 static void EnableVABC(MockDynamicPartitionControl* dynamic_control,
208 const std::string& part_name) {
209 ON_CALL(*dynamic_control, GetDynamicPartitionsFeatureFlag())
210 .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
211 ON_CALL(*dynamic_control, UpdateUsesSnapshotCompression())
212 .WillByDefault(Return(true));
213 ON_CALL(*dynamic_control, IsDynamicPartition(part_name, _))
214 .WillByDefault(Return(true));
215 }
216
217 class FilesystemVerifierActionTestDelegate : public ActionProcessorDelegate {
218 public:
FilesystemVerifierActionTestDelegate()219 FilesystemVerifierActionTestDelegate()
220 : ran_(false), code_(ErrorCode::kError) {}
221
ProcessingDone(const ActionProcessor * processor,ErrorCode code)222 void ProcessingDone(const ActionProcessor* processor, ErrorCode code) {
223 MessageLoop::current()->BreakLoop();
224 }
ProcessingStopped(const ActionProcessor * processor)225 void ProcessingStopped(const ActionProcessor* processor) {
226 MessageLoop::current()->BreakLoop();
227 }
ActionCompleted(ActionProcessor * processor,AbstractAction * action,ErrorCode code)228 void ActionCompleted(ActionProcessor* processor,
229 AbstractAction* action,
230 ErrorCode code) {
231 if (action->Type() == FilesystemVerifierAction::StaticType()) {
232 ran_ = true;
233 code_ = code;
234 EXPECT_FALSE(
235 static_cast<FilesystemVerifierAction*>(action)->partition_fd_);
236 } else if (action->Type() ==
237 ObjectCollectorAction<InstallPlan>::StaticType()) {
238 auto collector_action =
239 static_cast<ObjectCollectorAction<InstallPlan>*>(action);
240 install_plan_.reset(new InstallPlan(collector_action->object()));
241 }
242 }
ran() const243 bool ran() const { return ran_; }
code() const244 ErrorCode code() const { return code_; }
245
246 std::unique_ptr<InstallPlan> install_plan_;
247
248 private:
249 bool ran_;
250 ErrorCode code_;
251 };
252
DoTest(bool terminate_early,bool hash_fail)253 bool FilesystemVerifierActionTest::DoTest(bool terminate_early,
254 bool hash_fail) {
255 ScopedTempFile a_loop_file("a_loop_file.XXXXXX");
256
257 // Make random data for a.
258 const size_t kLoopFileSize = 10 * 1024 * 1024 + 512;
259 brillo::Blob a_loop_data(kLoopFileSize);
260 test_utils::FillWithData(&a_loop_data);
261
262 // Write data to disk
263 if (!(test_utils::WriteFileVector(a_loop_file.path(), a_loop_data))) {
264 ADD_FAILURE();
265 return false;
266 }
267
268 // Attach loop devices to the files
269 string a_dev;
270 test_utils::ScopedLoopbackDeviceBinder a_dev_releaser(
271 a_loop_file.path(), false, &a_dev);
272 if (!(a_dev_releaser.is_bound())) {
273 ADD_FAILURE();
274 return false;
275 }
276
277 LOG(INFO) << "verifying: " << a_loop_file.path() << " (" << a_dev << ")";
278
279 bool success = true;
280
281 // Set up the action objects
282 install_plan_.source_slot = 0;
283 install_plan_.target_slot = 1;
284 InstallPlan::Partition part;
285 part.name = "part";
286 part.target_size = kLoopFileSize - (hash_fail ? 1 : 0);
287 part.target_path = a_dev;
288 if (!HashCalculator::RawHashOfData(a_loop_data, &part.target_hash)) {
289 ADD_FAILURE();
290 success = false;
291 }
292 part.source_size = kLoopFileSize;
293 part.source_path = a_dev;
294 if (!HashCalculator::RawHashOfData(a_loop_data, &part.source_hash)) {
295 ADD_FAILURE();
296 success = false;
297 }
298 install_plan_.partitions = {part};
299
300 BuildActions(install_plan_);
301
302 FilesystemVerifierActionTestDelegate delegate;
303 processor_.set_delegate(&delegate);
304
305 loop_.PostTask(base::Bind(&ActionProcessor::StartProcessing,
306 base::Unretained(&processor_)));
307 if (terminate_early) {
308 loop_.PostTask(base::Bind(&ActionProcessor::StopProcessing,
309 base::Unretained(&processor_)));
310 }
311 loop_.Run();
312
313 if (!terminate_early) {
314 bool is_delegate_ran = delegate.ran();
315 EXPECT_TRUE(is_delegate_ran);
316 success = success && is_delegate_ran;
317 } else {
318 EXPECT_EQ(ErrorCode::kError, delegate.code());
319 return (ErrorCode::kError == delegate.code());
320 }
321 if (hash_fail) {
322 ErrorCode expected_exit_code = ErrorCode::kNewRootfsVerificationError;
323 EXPECT_EQ(expected_exit_code, delegate.code());
324 return (expected_exit_code == delegate.code());
325 }
326 EXPECT_EQ(ErrorCode::kSuccess, delegate.code());
327
328 // Make sure everything in the out_image is there
329 brillo::Blob a_out;
330 if (!utils::ReadFile(a_dev, &a_out)) {
331 ADD_FAILURE();
332 return false;
333 }
334 const bool is_a_file_reading_eq =
335 test_utils::ExpectVectorsEq(a_loop_data, a_out);
336 EXPECT_TRUE(is_a_file_reading_eq);
337 success = success && is_a_file_reading_eq;
338
339 bool is_install_plan_eq = (*delegate.install_plan_ == install_plan_);
340 EXPECT_TRUE(is_install_plan_eq);
341 success = success && is_install_plan_eq;
342 return success;
343 }
344
BuildActions(const InstallPlan & install_plan,DynamicPartitionControlInterface * dynamic_control)345 void FilesystemVerifierActionTest::BuildActions(
346 const InstallPlan& install_plan,
347 DynamicPartitionControlInterface* dynamic_control) {
348 auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
349 auto verifier_action =
350 std::make_unique<FilesystemVerifierAction>(dynamic_control);
351 auto collector_action =
352 std::make_unique<ObjectCollectorAction<InstallPlan>>();
353
354 feeder_action->set_obj(install_plan);
355
356 BondActions(feeder_action.get(), verifier_action.get());
357 BondActions(verifier_action.get(), collector_action.get());
358
359 processor_.EnqueueAction(std::move(feeder_action));
360 processor_.EnqueueAction(std::move(verifier_action));
361 processor_.EnqueueAction(std::move(collector_action));
362 }
363
BuildActions(const InstallPlan & install_plan)364 void FilesystemVerifierActionTest::BuildActions(
365 const InstallPlan& install_plan) {
366 BuildActions(install_plan, &dynamic_control_stub_);
367 }
368
369 class FilesystemVerifierActionTest2Delegate : public ActionProcessorDelegate {
370 public:
ActionCompleted(ActionProcessor * processor,AbstractAction * action,ErrorCode code)371 void ActionCompleted(ActionProcessor* processor,
372 AbstractAction* action,
373 ErrorCode code) {
374 if (action->Type() == FilesystemVerifierAction::StaticType()) {
375 ran_ = true;
376 code_ = code;
377 }
378 }
379 bool ran_;
380 ErrorCode code_;
381 };
382
TEST_F(FilesystemVerifierActionTest,MissingInputObjectTest)383 TEST_F(FilesystemVerifierActionTest, MissingInputObjectTest) {
384 auto copier_action =
385 std::make_unique<FilesystemVerifierAction>(&dynamic_control_stub_);
386 auto collector_action =
387 std::make_unique<ObjectCollectorAction<InstallPlan>>();
388
389 BondActions(copier_action.get(), collector_action.get());
390
391 processor_.EnqueueAction(std::move(copier_action));
392 processor_.EnqueueAction(std::move(collector_action));
393
394 FilesystemVerifierActionTest2Delegate delegate;
395 processor_.set_delegate(&delegate);
396
397 processor_.StartProcessing();
398 ASSERT_FALSE(processor_.IsRunning());
399 ASSERT_TRUE(delegate.ran_);
400 EXPECT_EQ(ErrorCode::kError, delegate.code_);
401 }
402
TEST_F(FilesystemVerifierActionTest,NonExistentDriveTest)403 TEST_F(FilesystemVerifierActionTest, NonExistentDriveTest) {
404 InstallPlan::Partition part;
405 part.name = "nope";
406 part.source_path = "/no/such/file";
407 part.target_path = "/no/such/file";
408 install_plan_.partitions = {part};
409
410 BuildActions(install_plan_);
411
412 FilesystemVerifierActionTest2Delegate delegate;
413 processor_.set_delegate(&delegate);
414
415 processor_.StartProcessing();
416 EXPECT_FALSE(processor_.IsRunning());
417 EXPECT_TRUE(delegate.ran_);
418 EXPECT_EQ(ErrorCode::kFilesystemVerifierError, delegate.code_);
419 }
420
TEST_F(FilesystemVerifierActionTest,RunAsRootVerifyHashTest)421 TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashTest) {
422 ASSERT_EQ(0U, getuid());
423 EXPECT_TRUE(DoTest(false, false));
424 }
425
TEST_F(FilesystemVerifierActionTest,RunAsRootVerifyHashFailTest)426 TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashFailTest) {
427 ASSERT_EQ(0U, getuid());
428 EXPECT_TRUE(DoTest(false, true));
429 }
430
TEST_F(FilesystemVerifierActionTest,RunAsRootTerminateEarlyTest)431 TEST_F(FilesystemVerifierActionTest, RunAsRootTerminateEarlyTest) {
432 ASSERT_EQ(0U, getuid());
433 EXPECT_TRUE(DoTest(true, false));
434 // TerminateEarlyTest may leak some null callbacks from the Stream class.
435 while (loop_.RunOnce(false)) {
436 }
437 }
438
439 #ifdef __ANDROID__
TEST_F(FilesystemVerifierActionTest,RunAsRootWriteVerityTest)440 TEST_F(FilesystemVerifierActionTest, RunAsRootWriteVerityTest) {
441 ScopedTempFile part_file("part_file.XXXXXX");
442 constexpr size_t filesystem_size = 200 * 4096;
443 constexpr size_t part_size = 256 * 4096;
444 brillo::Blob part_data(filesystem_size, 0x1);
445 part_data.resize(part_size);
446 ASSERT_TRUE(test_utils::WriteFileVector(part_file.path(), part_data));
447 string target_path;
448 test_utils::ScopedLoopbackDeviceBinder target_device(
449 part_file.path(), true, &target_path);
450
451 InstallPlan::Partition part;
452 part.name = "part";
453 part.target_path = target_path;
454 part.target_size = part_size;
455 part.block_size = 4096;
456 part.hash_tree_algorithm = "sha1";
457 part.hash_tree_data_offset = 0;
458 part.hash_tree_data_size = filesystem_size;
459 part.hash_tree_offset = filesystem_size;
460 part.hash_tree_size = 3 * 4096;
461 part.fec_data_offset = 0;
462 part.fec_data_size = filesystem_size + part.hash_tree_size;
463 part.fec_offset = part.fec_data_size;
464 part.fec_size = 2 * 4096;
465 part.fec_roots = 2;
466 // for i in {1..$((200 * 4096))}; do echo -n -e '\x1' >> part; done
467 // avbtool add_hashtree_footer --image part --partition_size $((256 * 4096))
468 // --partition_name part --do_not_append_vbmeta_image
469 // --output_vbmeta_image vbmeta
470 // truncate -s $((256 * 4096)) part
471 // sha256sum part | xxd -r -p | hexdump -v -e '/1 "0x%02x, "'
472 part.target_hash = {0x28, 0xd4, 0x96, 0x75, 0x4c, 0xf5, 0x8a, 0x3e,
473 0x31, 0x85, 0x08, 0x92, 0x85, 0x62, 0xf0, 0x37,
474 0xbc, 0x8d, 0x7e, 0xa4, 0xcb, 0x24, 0x18, 0x7b,
475 0xf3, 0xeb, 0xb5, 0x8d, 0x6f, 0xc8, 0xd8, 0x1a};
476 // avbtool info_image --image vbmeta | grep Salt | cut -d':' -f 2 |
477 // xxd -r -p | hexdump -v -e '/1 "0x%02x, "'
478 part.hash_tree_salt = {0x9e, 0xcb, 0xf8, 0xd5, 0x0b, 0xb4, 0x43,
479 0x0a, 0x7a, 0x10, 0xad, 0x96, 0xd7, 0x15,
480 0x70, 0xba, 0xed, 0x27, 0xe2, 0xae};
481 install_plan_.partitions = {part};
482
483 BuildActions(install_plan_);
484
485 FilesystemVerifierActionTestDelegate delegate;
486 processor_.set_delegate(&delegate);
487
488 loop_.PostTask(
489 FROM_HERE,
490 base::Bind(
491 [](ActionProcessor* processor) { processor->StartProcessing(); },
492 base::Unretained(&processor_)));
493 loop_.Run();
494
495 EXPECT_FALSE(processor_.IsRunning());
496 EXPECT_TRUE(delegate.ran());
497 EXPECT_EQ(ErrorCode::kSuccess, delegate.code());
498 }
499 #endif // __ANDROID__
500
TEST_F(FilesystemVerifierActionTest,RunAsRootSkipWriteVerityTest)501 TEST_F(FilesystemVerifierActionTest, RunAsRootSkipWriteVerityTest) {
502 ScopedTempFile part_file("part_file.XXXXXX");
503 constexpr size_t filesystem_size = 200 * 4096;
504 constexpr size_t part_size = 256 * 4096;
505 brillo::Blob part_data(part_size);
506 test_utils::FillWithData(&part_data);
507 ASSERT_TRUE(test_utils::WriteFileVector(part_file.path(), part_data));
508 string target_path;
509 test_utils::ScopedLoopbackDeviceBinder target_device(
510 part_file.path(), true, &target_path);
511
512 install_plan_.write_verity = false;
513 InstallPlan::Partition part;
514 part.name = "part";
515 part.target_path = target_path;
516 part.target_size = part_size;
517 part.block_size = 4096;
518 part.hash_tree_data_offset = 0;
519 part.hash_tree_data_size = filesystem_size;
520 part.hash_tree_offset = filesystem_size;
521 part.hash_tree_size = 3 * 4096;
522 part.fec_data_offset = 0;
523 part.fec_data_size = filesystem_size + part.hash_tree_size;
524 part.fec_offset = part.fec_data_size;
525 part.fec_size = 2 * 4096;
526 EXPECT_TRUE(HashCalculator::RawHashOfData(part_data, &part.target_hash));
527 install_plan_.partitions = {part};
528
529 BuildActions(install_plan_);
530
531 FilesystemVerifierActionTestDelegate delegate;
532 processor_.set_delegate(&delegate);
533
534 loop_.PostTask(
535 FROM_HERE,
536 base::Bind(
537 [](ActionProcessor* processor) { processor->StartProcessing(); },
538 base::Unretained(&processor_)));
539 loop_.Run();
540
541 ASSERT_FALSE(processor_.IsRunning());
542 ASSERT_TRUE(delegate.ran());
543 ASSERT_EQ(ErrorCode::kSuccess, delegate.code());
544 }
545
DoTestVABC(bool clear_target_hash,bool enable_verity)546 void FilesystemVerifierActionTest::DoTestVABC(bool clear_target_hash,
547 bool enable_verity) {
548 auto part_ptr = AddFakePartition(&install_plan_);
549 if (::testing::Test::HasFailure()) {
550 return;
551 }
552 ASSERT_NE(part_ptr, nullptr);
553 InstallPlan::Partition& part = *part_ptr;
554 part.target_path = "Shouldn't attempt to open this path";
555 if (enable_verity) {
556 install_plan_.write_verity = true;
557 ASSERT_NO_FATAL_FAILURE(SetHashWithVerity(&part));
558 }
559 if (clear_target_hash) {
560 part.target_hash.clear();
561 }
562
563 NiceMock<MockDynamicPartitionControl> dynamic_control;
564
565 EnableVABC(&dynamic_control, part.name);
566 auto open_cow = [part]() {
567 auto cow_fd = std::make_unique<EintrSafeFileDescriptor>();
568 EXPECT_TRUE(cow_fd->Open(part.readonly_target_path.c_str(), O_RDWR))
569 << "Failed to open part " << part.readonly_target_path
570 << strerror(errno);
571 return cow_fd;
572 };
573
574 EXPECT_CALL(dynamic_control, UpdateUsesSnapshotCompression())
575 .Times(AtLeast(1));
576 auto cow_fd = open_cow();
577 if (HasFailure()) {
578 return;
579 }
580
581 if (enable_verity) {
582 ON_CALL(dynamic_control, OpenCowFd(part.name, {part.source_path}, _))
583 .WillByDefault(open_cow);
584 EXPECT_CALL(dynamic_control, OpenCowFd(part.name, {part.source_path}, _))
585 .Times(AtLeast(1));
586
587 // fs verification isn't supposed to write to |readonly_target_path|. All
588 // writes should go through fd returned by |OpenCowFd|. Therefore we set
589 // target part as read-only to make sure.
590 ASSERT_EQ(0, chmod(part.readonly_target_path.c_str(), 0444))
591 << " Failed to set " << part.readonly_target_path << " as read-only "
592 << strerror(errno);
593 } else {
594 // Since we are not writing verity, we should not attempt to OpenCowFd()
595 // reads should go through regular file descriptors on mapped partitions.
596 EXPECT_CALL(dynamic_control, OpenCowFd(part.name, {part.source_path}, _))
597 .Times(0);
598 EXPECT_CALL(dynamic_control, MapAllPartitions()).Times(AtLeast(1));
599 }
600 EXPECT_CALL(dynamic_control, ListDynamicPartitionsForSlot(_, _, _))
601 .WillRepeatedly(
602 DoAll(SetArgPointee<2, std::vector<std::string>>({part.name}),
603 Return(true)));
604
605 BuildActions(install_plan_, &dynamic_control);
606
607 FilesystemVerifierActionTestDelegate delegate;
608 processor_.set_delegate(&delegate);
609
610 loop_.PostTask(FROM_HERE,
611 base::Bind(&ActionProcessor::StartProcessing,
612 base::Unretained(&processor_)));
613 loop_.Run();
614
615 ASSERT_FALSE(processor_.IsRunning());
616 ASSERT_TRUE(delegate.ran());
617 if (enable_verity) {
618 std::vector<unsigned char> actual_fec(fec_size);
619 ssize_t bytes_read = 0;
620 ASSERT_TRUE(utils::PReadAll(cow_fd.get(),
621 actual_fec.data(),
622 actual_fec.size(),
623 fec_start_offset,
624 &bytes_read));
625 ASSERT_EQ(actual_fec, fec_data_);
626 std::vector<unsigned char> actual_hash_tree(hash_tree_size);
627 ASSERT_TRUE(utils::PReadAll(cow_fd.get(),
628 actual_hash_tree.data(),
629 actual_hash_tree.size(),
630 HASH_TREE_START_OFFSET,
631 &bytes_read));
632 ASSERT_EQ(actual_hash_tree, hash_tree_data_);
633 }
634 if (clear_target_hash) {
635 ASSERT_EQ(ErrorCode::kNewRootfsVerificationError, delegate.code());
636 } else {
637 ASSERT_EQ(ErrorCode::kSuccess, delegate.code());
638 }
639 }
640
TEST_F(FilesystemVerifierActionTest,VABC_NoVerity_Success)641 TEST_F(FilesystemVerifierActionTest, VABC_NoVerity_Success) {
642 DoTestVABC(false, false);
643 }
644
TEST_F(FilesystemVerifierActionTest,VABC_NoVerity_Target_Mismatch)645 TEST_F(FilesystemVerifierActionTest, VABC_NoVerity_Target_Mismatch) {
646 DoTestVABC(true, false);
647 }
648
TEST_F(FilesystemVerifierActionTest,VABC_Verity_Success)649 TEST_F(FilesystemVerifierActionTest, VABC_Verity_Success) {
650 DoTestVABC(false, true);
651 }
652
TEST_F(FilesystemVerifierActionTest,VABC_Verity_ReadAfterWrite)653 TEST_F(FilesystemVerifierActionTest, VABC_Verity_ReadAfterWrite) {
654 ASSERT_NO_FATAL_FAILURE(DoTestVABC(false, true));
655 // Run FS verification again, w/o writing verity. We have seen a bug where
656 // attempting to run fs again will cause previously written verity data to be
657 // dropped, so cover this scenario.
658 ASSERT_GE(install_plan_.partitions.size(), 1UL);
659 auto& part = install_plan_.partitions[0];
660 install_plan_.write_verity = false;
661 part.readonly_target_path = target_part_.path();
662 NiceMock<MockDynamicPartitionControl> dynamic_control;
663 EnableVABC(&dynamic_control, part.name);
664
665 // b/186196758 is only visible if we repeatedely run FS verification w/o
666 // writing verity
667 for (int i = 0; i < 3; i++) {
668 BuildActions(install_plan_, &dynamic_control);
669
670 FilesystemVerifierActionTestDelegate delegate;
671 processor_.set_delegate(&delegate);
672 loop_.PostTask(
673 FROM_HERE,
674 base::Bind(
675 [](ActionProcessor* processor) { processor->StartProcessing(); },
676 base::Unretained(&processor_)));
677 loop_.Run();
678 ASSERT_FALSE(processor_.IsRunning());
679 ASSERT_TRUE(delegate.ran());
680 ASSERT_EQ(ErrorCode::kSuccess, delegate.code());
681 }
682 }
683
TEST_F(FilesystemVerifierActionTest,VABC_Verity_Target_Mismatch)684 TEST_F(FilesystemVerifierActionTest, VABC_Verity_Target_Mismatch) {
685 DoTestVABC(true, true);
686 }
687
688 } // namespace chromeos_update_engine
689