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 <memory>
20 #include <string>
21 #include <utility>
22 
23 #include <base/bind.h>
24 #include <base/posix/eintr_wrapper.h>
25 #include <brillo/message_loops/fake_message_loop.h>
26 #include <brillo/message_loops/message_loop_utils.h>
27 #include <brillo/secure_blob.h>
28 #include <gtest/gtest.h>
29 
30 #include "update_engine/common/hash_calculator.h"
31 #include "update_engine/common/test_utils.h"
32 #include "update_engine/common/utils.h"
33 
34 using brillo::MessageLoop;
35 using std::string;
36 
37 namespace chromeos_update_engine {
38 
39 class FilesystemVerifierActionTest : public ::testing::Test {
40  protected:
41   void SetUp() override { loop_.SetAsCurrent(); }
42 
43   void TearDown() override {
44     EXPECT_EQ(0, brillo::MessageLoopRunMaxIterations(&loop_, 1));
45   }
46 
47   // Returns true iff test has completed successfully.
48   bool DoTest(bool terminate_early, bool hash_fail);
49 
50   void BuildActions(const InstallPlan& install_plan);
51 
52   brillo::FakeMessageLoop loop_{nullptr};
53   ActionProcessor processor_;
54 };
55 
56 class FilesystemVerifierActionTestDelegate : public ActionProcessorDelegate {
57  public:
58   FilesystemVerifierActionTestDelegate()
59       : ran_(false), code_(ErrorCode::kError) {}
60 
61   void ProcessingDone(const ActionProcessor* processor, ErrorCode code) {
62     MessageLoop::current()->BreakLoop();
63   }
64   void ProcessingStopped(const ActionProcessor* processor) {
65     MessageLoop::current()->BreakLoop();
66   }
67   void ActionCompleted(ActionProcessor* processor,
68                        AbstractAction* action,
69                        ErrorCode code) {
70     if (action->Type() == FilesystemVerifierAction::StaticType()) {
71       ran_ = true;
72       code_ = code;
73       EXPECT_FALSE(static_cast<FilesystemVerifierAction*>(action)->src_stream_);
74     } else if (action->Type() ==
75                ObjectCollectorAction<InstallPlan>::StaticType()) {
76       auto collector_action =
77           static_cast<ObjectCollectorAction<InstallPlan>*>(action);
78       install_plan_.reset(new InstallPlan(collector_action->object()));
79     }
80   }
81   bool ran() const { return ran_; }
82   ErrorCode code() const { return code_; }
83 
84   std::unique_ptr<InstallPlan> install_plan_;
85 
86  private:
87   bool ran_;
88   ErrorCode code_;
89 };
90 
91 bool FilesystemVerifierActionTest::DoTest(bool terminate_early,
92                                           bool hash_fail) {
93   test_utils::ScopedTempFile a_loop_file("a_loop_file.XXXXXX");
94 
95   // Make random data for a.
96   const size_t kLoopFileSize = 10 * 1024 * 1024 + 512;
97   brillo::Blob a_loop_data(kLoopFileSize);
98   test_utils::FillWithData(&a_loop_data);
99 
100   // Write data to disk
101   if (!(test_utils::WriteFileVector(a_loop_file.path(), a_loop_data))) {
102     ADD_FAILURE();
103     return false;
104   }
105 
106   // Attach loop devices to the files
107   string a_dev;
108   test_utils::ScopedLoopbackDeviceBinder a_dev_releaser(
109       a_loop_file.path(), false, &a_dev);
110   if (!(a_dev_releaser.is_bound())) {
111     ADD_FAILURE();
112     return false;
113   }
114 
115   LOG(INFO) << "verifying: " << a_loop_file.path() << " (" << a_dev << ")";
116 
117   bool success = true;
118 
119   // Set up the action objects
120   InstallPlan install_plan;
121   install_plan.source_slot = 0;
122   install_plan.target_slot = 1;
123   InstallPlan::Partition part;
124   part.name = "part";
125   part.target_size = kLoopFileSize - (hash_fail ? 1 : 0);
126   part.target_path = a_dev;
127   if (!HashCalculator::RawHashOfData(a_loop_data, &part.target_hash)) {
128     ADD_FAILURE();
129     success = false;
130   }
131   part.source_size = kLoopFileSize;
132   part.source_path = a_dev;
133   if (!HashCalculator::RawHashOfData(a_loop_data, &part.source_hash)) {
134     ADD_FAILURE();
135     success = false;
136   }
137   install_plan.partitions = {part};
138 
139   BuildActions(install_plan);
140 
141   FilesystemVerifierActionTestDelegate delegate;
142   processor_.set_delegate(&delegate);
143 
144   loop_.PostTask(FROM_HERE,
145                  base::Bind(
146                      [](ActionProcessor* processor, bool terminate_early) {
147                        processor->StartProcessing();
148                        if (terminate_early) {
149                          processor->StopProcessing();
150                        }
151                      },
152                      base::Unretained(&processor_),
153                      terminate_early));
154   loop_.Run();
155 
156   if (!terminate_early) {
157     bool is_delegate_ran = delegate.ran();
158     EXPECT_TRUE(is_delegate_ran);
159     success = success && is_delegate_ran;
160   } else {
161     EXPECT_EQ(ErrorCode::kError, delegate.code());
162     return (ErrorCode::kError == delegate.code());
163   }
164   if (hash_fail) {
165     ErrorCode expected_exit_code = ErrorCode::kNewRootfsVerificationError;
166     EXPECT_EQ(expected_exit_code, delegate.code());
167     return (expected_exit_code == delegate.code());
168   }
169   EXPECT_EQ(ErrorCode::kSuccess, delegate.code());
170 
171   // Make sure everything in the out_image is there
172   brillo::Blob a_out;
173   if (!utils::ReadFile(a_dev, &a_out)) {
174     ADD_FAILURE();
175     return false;
176   }
177   const bool is_a_file_reading_eq =
178       test_utils::ExpectVectorsEq(a_loop_data, a_out);
179   EXPECT_TRUE(is_a_file_reading_eq);
180   success = success && is_a_file_reading_eq;
181 
182   bool is_install_plan_eq = (*delegate.install_plan_ == install_plan);
183   EXPECT_TRUE(is_install_plan_eq);
184   success = success && is_install_plan_eq;
185   return success;
186 }
187 
188 void FilesystemVerifierActionTest::BuildActions(
189     const InstallPlan& install_plan) {
190   auto feeder_action = std::make_unique<ObjectFeederAction<InstallPlan>>();
191   auto verifier_action = std::make_unique<FilesystemVerifierAction>();
192   auto collector_action =
193       std::make_unique<ObjectCollectorAction<InstallPlan>>();
194 
195   feeder_action->set_obj(install_plan);
196 
197   BondActions(feeder_action.get(), verifier_action.get());
198   BondActions(verifier_action.get(), collector_action.get());
199 
200   processor_.EnqueueAction(std::move(feeder_action));
201   processor_.EnqueueAction(std::move(verifier_action));
202   processor_.EnqueueAction(std::move(collector_action));
203 }
204 
205 class FilesystemVerifierActionTest2Delegate : public ActionProcessorDelegate {
206  public:
207   void ActionCompleted(ActionProcessor* processor,
208                        AbstractAction* action,
209                        ErrorCode code) {
210     if (action->Type() == FilesystemVerifierAction::StaticType()) {
211       ran_ = true;
212       code_ = code;
213     }
214   }
215   bool ran_;
216   ErrorCode code_;
217 };
218 
219 TEST_F(FilesystemVerifierActionTest, MissingInputObjectTest) {
220   auto copier_action = std::make_unique<FilesystemVerifierAction>();
221   auto collector_action =
222       std::make_unique<ObjectCollectorAction<InstallPlan>>();
223 
224   BondActions(copier_action.get(), collector_action.get());
225 
226   processor_.EnqueueAction(std::move(copier_action));
227   processor_.EnqueueAction(std::move(collector_action));
228 
229   FilesystemVerifierActionTest2Delegate delegate;
230   processor_.set_delegate(&delegate);
231 
232   processor_.StartProcessing();
233   EXPECT_FALSE(processor_.IsRunning());
234   EXPECT_TRUE(delegate.ran_);
235   EXPECT_EQ(ErrorCode::kError, delegate.code_);
236 }
237 
238 TEST_F(FilesystemVerifierActionTest, NonExistentDriveTest) {
239   InstallPlan install_plan;
240   InstallPlan::Partition part;
241   part.name = "nope";
242   part.source_path = "/no/such/file";
243   part.target_path = "/no/such/file";
244   install_plan.partitions = {part};
245 
246   BuildActions(install_plan);
247 
248   FilesystemVerifierActionTest2Delegate delegate;
249   processor_.set_delegate(&delegate);
250 
251   processor_.StartProcessing();
252   EXPECT_FALSE(processor_.IsRunning());
253   EXPECT_TRUE(delegate.ran_);
254   EXPECT_EQ(ErrorCode::kFilesystemVerifierError, delegate.code_);
255 }
256 
257 TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashTest) {
258   ASSERT_EQ(0U, getuid());
259   EXPECT_TRUE(DoTest(false, false));
260 }
261 
262 TEST_F(FilesystemVerifierActionTest, RunAsRootVerifyHashFailTest) {
263   ASSERT_EQ(0U, getuid());
264   EXPECT_TRUE(DoTest(false, true));
265 }
266 
267 TEST_F(FilesystemVerifierActionTest, RunAsRootTerminateEarlyTest) {
268   ASSERT_EQ(0U, getuid());
269   EXPECT_TRUE(DoTest(true, false));
270   // TerminateEarlyTest may leak some null callbacks from the Stream class.
271   while (loop_.RunOnce(false)) {
272   }
273 }
274 
275 #ifdef __ANDROID__
276 TEST_F(FilesystemVerifierActionTest, RunAsRootWriteVerityTest) {
277   test_utils::ScopedTempFile part_file("part_file.XXXXXX");
278   constexpr size_t filesystem_size = 200 * 4096;
279   constexpr size_t part_size = 256 * 4096;
280   brillo::Blob part_data(filesystem_size, 0x1);
281   part_data.resize(part_size);
282   ASSERT_TRUE(test_utils::WriteFileVector(part_file.path(), part_data));
283   string target_path;
284   test_utils::ScopedLoopbackDeviceBinder target_device(
285       part_file.path(), true, &target_path);
286 
287   InstallPlan install_plan;
288   InstallPlan::Partition part;
289   part.name = "part";
290   part.target_path = target_path;
291   part.target_size = part_size;
292   part.block_size = 4096;
293   part.hash_tree_algorithm = "sha1";
294   part.hash_tree_data_offset = 0;
295   part.hash_tree_data_size = filesystem_size;
296   part.hash_tree_offset = filesystem_size;
297   part.hash_tree_size = 3 * 4096;
298   part.fec_data_offset = 0;
299   part.fec_data_size = filesystem_size + part.hash_tree_size;
300   part.fec_offset = part.fec_data_size;
301   part.fec_size = 2 * 4096;
302   part.fec_roots = 2;
303   // for i in {1..$((200 * 4096))}; do echo -n -e '\x1' >> part; done
304   // avbtool add_hashtree_footer --image part --partition_size $((256 * 4096))
305   //     --partition_name part --do_not_append_vbmeta_image
306   //     --output_vbmeta_image vbmeta
307   // truncate -s $((256 * 4096)) part
308   // sha256sum part | xxd -r -p | hexdump -v -e '/1 "0x%02x, "'
309   part.target_hash = {0x28, 0xd4, 0x96, 0x75, 0x4c, 0xf5, 0x8a, 0x3e,
310                       0x31, 0x85, 0x08, 0x92, 0x85, 0x62, 0xf0, 0x37,
311                       0xbc, 0x8d, 0x7e, 0xa4, 0xcb, 0x24, 0x18, 0x7b,
312                       0xf3, 0xeb, 0xb5, 0x8d, 0x6f, 0xc8, 0xd8, 0x1a};
313   // avbtool info_image --image vbmeta | grep Salt | cut -d':' -f 2 |
314   //     xxd -r -p | hexdump -v -e '/1 "0x%02x, "'
315   part.hash_tree_salt = {0x9e, 0xcb, 0xf8, 0xd5, 0x0b, 0xb4, 0x43,
316                          0x0a, 0x7a, 0x10, 0xad, 0x96, 0xd7, 0x15,
317                          0x70, 0xba, 0xed, 0x27, 0xe2, 0xae};
318   install_plan.partitions = {part};
319 
320   BuildActions(install_plan);
321 
322   FilesystemVerifierActionTestDelegate delegate;
323   processor_.set_delegate(&delegate);
324 
325   loop_.PostTask(
326       FROM_HERE,
327       base::Bind(
328           [](ActionProcessor* processor) { processor->StartProcessing(); },
329           base::Unretained(&processor_)));
330   loop_.Run();
331 
332   EXPECT_FALSE(processor_.IsRunning());
333   EXPECT_TRUE(delegate.ran());
334   EXPECT_EQ(ErrorCode::kSuccess, delegate.code());
335 }
336 #endif  // __ANDROID__
337 
338 TEST_F(FilesystemVerifierActionTest, RunAsRootSkipWriteVerityTest) {
339   test_utils::ScopedTempFile part_file("part_file.XXXXXX");
340   constexpr size_t filesystem_size = 200 * 4096;
341   constexpr size_t part_size = 256 * 4096;
342   brillo::Blob part_data(part_size);
343   test_utils::FillWithData(&part_data);
344   ASSERT_TRUE(test_utils::WriteFileVector(part_file.path(), part_data));
345   string target_path;
346   test_utils::ScopedLoopbackDeviceBinder target_device(
347       part_file.path(), true, &target_path);
348 
349   InstallPlan install_plan;
350   install_plan.write_verity = false;
351   InstallPlan::Partition part;
352   part.name = "part";
353   part.target_path = target_path;
354   part.target_size = part_size;
355   part.block_size = 4096;
356   part.hash_tree_data_offset = 0;
357   part.hash_tree_data_size = filesystem_size;
358   part.hash_tree_offset = filesystem_size;
359   part.hash_tree_size = 3 * 4096;
360   part.fec_data_offset = 0;
361   part.fec_data_size = filesystem_size + part.hash_tree_size;
362   part.fec_offset = part.fec_data_size;
363   part.fec_size = 2 * 4096;
364   EXPECT_TRUE(HashCalculator::RawHashOfData(part_data, &part.target_hash));
365   install_plan.partitions = {part};
366 
367   BuildActions(install_plan);
368 
369   FilesystemVerifierActionTestDelegate delegate;
370   processor_.set_delegate(&delegate);
371 
372   loop_.PostTask(
373       FROM_HERE,
374       base::Bind(
375           [](ActionProcessor* processor) { processor->StartProcessing(); },
376           base::Unretained(&processor_)));
377   loop_.Run();
378 
379   EXPECT_FALSE(processor_.IsRunning());
380   EXPECT_TRUE(delegate.ran());
381   EXPECT_EQ(ErrorCode::kSuccess, delegate.code());
382 }
383 }  // namespace chromeos_update_engine
384