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