• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2022 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 <action.h>
18 #include <action_manager.h>
19 #include <action_parser.h>
20 #include <android-base/file.h>
21 #include <android-base/logging.h>
22 #include <android-base/parseint.h>
23 #include <android-base/result.h>
24 #include <android-base/strings.h>
25 #include <apex_file.h>
26 #include <builtins.h>
27 #include <getopt.h>
28 #include <parser.h>
29 #include <pwd.h>
30 #include <service_list.h>
31 #include <service_parser.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 
35 #include <iostream>
36 #include <string_view>
37 
38 using ::apex::proto::ApexManifest;
39 
40 // Fake getpwnam for host execution, used by the init::ServiceParser.
getpwnam(const char *)41 passwd* getpwnam(const char*) {
42   static char fake_buf[] = "fake";
43   static passwd fake_passwd = {
44       .pw_name = fake_buf,
45       .pw_dir = fake_buf,
46       .pw_shell = fake_buf,
47       .pw_uid = 123,
48       .pw_gid = 123,
49   };
50   return &fake_passwd;
51 }
52 
53 namespace android {
54 namespace apex {
55 namespace {
56 
57 static const std::vector<std::string> partitions = {"system", "system_ext",
58                                                     "product", "vendor", "odm"};
59 
PrintUsage()60 void PrintUsage() {
61   printf(R"(usage: host_apex_verifier [options]
62 
63 Tests APEX file(s) for correctness.
64 
65 Options:
66   --deapexer=PATH             Use the deapexer binary at this path when extracting APEXes.
67   --debugfs=PATH              Use the debugfs binary at this path when extracting APEXes.
68   --sdk_version=INT           The active system SDK version used when filtering versioned
69                               init.rc files.
70   --out_system=DIR            Path to the factory APEX directory for the system partition.
71   --out_system_ext=DIR        Path to the factory APEX directory for the system_ext partition.
72   --out_product=DIR           Path to the factory APEX directory for the product partition.
73   --out_vendor=DIR            Path to the factory APEX directory for the vendor partition.
74   --out_odm=DIR               Path to the factory APEX directory for the odm partition.
75 )");
76 }
77 
ApexInitRcSupportedActionMap()78 const android::init::BuiltinFunctionMap& ApexInitRcSupportedActionMap() {
79   static const android::init::BuiltinFunctionMap functions = {
80       // Add any init actions supported inside APEXes here.
81       // See system/core/init/builtins.cpp for expected syntax.
82   };
83   return functions;
84 }
85 
86 // Validate any init rc files inside the APEX.
CheckInitRc(const std::string & apex_dir,const ApexManifest & manifest,int sdk_version)87 void CheckInitRc(const std::string& apex_dir, const ApexManifest& manifest,
88                  int sdk_version) {
89   init::Parser parser;
90   init::ServiceList service_list = init::ServiceList();
91   parser.AddSectionParser("service", std::make_unique<init::ServiceParser>(
92                                          &service_list, nullptr, std::nullopt));
93   const init::BuiltinFunctionMap& function_map = ApexInitRcSupportedActionMap();
94   init::Action::set_function_map(&function_map);
95   init::ActionManager action_manager = init::ActionManager();
96   parser.AddSectionParser(
97       "on", std::make_unique<init::ActionParser>(&action_manager, nullptr));
98   std::string init_dir_path = apex_dir + "/etc";
99   std::vector<std::string> init_configs;
100   std::unique_ptr<DIR, decltype(&closedir)> init_dir(
101       opendir(init_dir_path.c_str()), closedir);
102   if (init_dir) {
103     dirent* entry;
104     while ((entry = readdir(init_dir.get()))) {
105       if (base::EndsWith(entry->d_name, "rc")) {
106         init_configs.push_back(init_dir_path + "/" + entry->d_name);
107       }
108     }
109   }
110   // TODO(b/225380016): Extend this tool to check all init.rc files
111   // in the APEX, possibly including different requirements depending
112   // on the SDK version.
113   for (const auto& c :
114        init::FilterVersionedConfigs(init_configs, sdk_version)) {
115     parser.ParseConfigFile(c);
116   }
117 
118   for (const auto& service : service_list) {
119     // Ensure the service path points inside this APEX.
120     auto service_path = service->args()[0];
121     if (!base::StartsWith(service_path, "/apex/" + manifest.name())) {
122       LOG(FATAL) << "Service " << service->name()
123                  << " has path outside of the APEX: " << service_path;
124     }
125     LOG(INFO) << service->name() << ": " << service_path;
126   }
127 
128   // The parser will fail if there are any unsupported actions.
129   if (parser.parse_error_count() > 0) {
130     LOG(FATAL) << "Failed to parse APEX init rc file(s)";
131   }
132 }
133 
134 // Extract and validate a single APEX.
ScanApex(const std::string & deapexer,const std::string & debugfs,int sdk_version,const std::string & apex_path)135 void ScanApex(const std::string& deapexer, const std::string& debugfs,
136               int sdk_version, const std::string& apex_path) {
137   LOG(INFO) << "Checking APEX " << apex_path;
138 
139   auto apex = OR_FATAL(ApexFile::Open(apex_path));
140   ApexManifest manifest = apex.GetManifest();
141 
142   auto extracted_apex = TemporaryDir();
143   std::string extracted_apex_dir = extracted_apex.path;
144   std::string deapexer_command = deapexer + " --debugfs_path " + debugfs +
145                                  " extract " + apex_path + " " +
146                                  extracted_apex_dir;
147   auto code = system(deapexer_command.c_str());
148   if (code != 0) {
149     LOG(FATAL) << "Error running deapexer command \"" << deapexer_command
150                << "\": " << code;
151   }
152 
153   CheckInitRc(extracted_apex_dir, manifest, sdk_version);
154 }
155 
156 // Scan the factory APEX files in the partition apex dir.
157 // Scans APEX files directly, rather than flattened ${PRODUCT_OUT}/apex/
158 // directories. This allows us to check:
159 //   - Prebuilt APEXes which do not flatten to that path.
160 //   - Multi-installed APEXes, where only the default
161 //     APEX may flatten to that path.
162 //   - Extracted target_files archives which may not contain
163 //     flattened <PARTITON>/apex/ directories.
ScanPartitionApexes(const std::string & deapexer,const std::string & debugfs,int sdk_version,const std::string & partition_dir)164 void ScanPartitionApexes(const std::string& deapexer,
165                          const std::string& debugfs, int sdk_version,
166                          const std::string& partition_dir) {
167   LOG(INFO) << "Scanning partition factory APEX dir " << partition_dir;
168 
169   std::unique_ptr<DIR, decltype(&closedir)> apex_dir(
170       opendir(partition_dir.c_str()), closedir);
171   if (!apex_dir) {
172     LOG(WARNING) << "Unable to open dir " << partition_dir;
173     return;
174   }
175 
176   dirent* entry;
177   while ((entry = readdir(apex_dir.get()))) {
178     if (base::EndsWith(entry->d_name, ".apex") ||
179         base::EndsWith(entry->d_name, ".capex")) {
180       ScanApex(deapexer, debugfs, sdk_version,
181                partition_dir + "/" + entry->d_name);
182     }
183   }
184 }
185 
186 }  // namespace
187 
main(int argc,char ** argv)188 int main(int argc, char** argv) {
189   android::base::InitLogging(argv, &android::base::StdioLogger);
190 
191   std::string deapexer, debugfs;
192   int sdk_version = INT_MAX;
193   std::map<std::string, std::string> partition_map;
194 
195   while (true) {
196     static const struct option long_options[] = {
197         {"help", no_argument, nullptr, 'h'},
198         {"deapexer", required_argument, nullptr, 0},
199         {"debugfs", required_argument, nullptr, 0},
200         {"sdk_version", required_argument, nullptr, 0},
201         {"out_system", required_argument, nullptr, 0},
202         {"out_system_ext", required_argument, nullptr, 0},
203         {"out_product", required_argument, nullptr, 0},
204         {"out_vendor", required_argument, nullptr, 0},
205         {"out_odm", required_argument, nullptr, 0},
206         {nullptr, 0, nullptr, 0},
207     };
208 
209     int option_index;
210     int arg = getopt_long(argc, argv, "h", long_options, &option_index);
211 
212     if (arg == -1) {
213       break;
214     }
215 
216     switch (arg) {
217       case 0: {
218         std::string_view name(long_options[option_index].name);
219         if (name == "deapexer") {
220           deapexer = optarg;
221         }
222         if (name == "debugfs") {
223           debugfs = optarg;
224         }
225         if (name == "sdk_version") {
226           if (!base::ParseInt(optarg, &sdk_version)) {
227             PrintUsage();
228             return EXIT_FAILURE;
229           }
230         }
231         for (const auto& p : partitions) {
232           if (name == "out_" + p) {
233             partition_map[p] = optarg;
234           }
235         }
236         break;
237       }
238       case 'h':
239         PrintUsage();
240         return EXIT_SUCCESS;
241       default:
242         LOG(ERROR) << "getopt returned invalid result: " << arg;
243         return EXIT_FAILURE;
244     }
245   }
246 
247   argc -= optind;
248   argv += optind;
249 
250   if (argc != 0 || deapexer.empty() || debugfs.empty()) {
251     PrintUsage();
252     return EXIT_FAILURE;
253   }
254 
255   for (const auto& p : partition_map) {
256     ScanPartitionApexes(deapexer, debugfs, sdk_version, p.second);
257   }
258 
259   return EXIT_SUCCESS;
260 }
261 
262 }  // namespace apex
263 }  // namespace android
264 
main(int argc,char ** argv)265 int main(int argc, char** argv) { return android::apex::main(argc, argv); }
266