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 &part;
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