• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 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 <filesystem>
18 #include <iomanip>
19 #include <sstream>
20 #include <string>
21 #include <string_view>
22 #include <unordered_map>
23 #include <unordered_set>
24 #include <vector>
25 
26 #include <android-base/file.h>
27 #include <android-base/logging.h>
28 #include <android-base/properties.h>
29 #include <android-base/result.h>
30 #include <android-base/strings.h>
31 
32 #include <gtest/gtest.h>
33 #include <kver/kernel_release.h>
34 #include <libelf64/parse.h>
35 #include <openssl/sha.h>
36 #include <tinyxml2.h>
37 #include <vintf/VintfObject.h>
38 
39 #include "ramdisk_utils.h"
40 
41 namespace android {
42 namespace {
43 
44 constexpr std::string_view kOptionalKernelModulesConfigPath =
45     "/system/etc/kernel/kernel-modules.xml";
46 
47 class ModinfoTags {
48  public:
ParseData(const std::vector<char> & data)49   void ParseData(const std::vector<char>& data) {
50     size_t offset = 0;
51     while (offset < data.size()) {
52       std::string_view chunk(data.data() + offset);
53       offset += chunk.size() + 1;
54       // Probably just padding
55       if (chunk.empty()) continue;
56       const auto delimiter = chunk.find('=');
57       // Malformed chunk, just ignore it.
58       if (delimiter == std::string_view::npos) continue;
59       tags_[std::string(chunk.substr(0, delimiter))].emplace_back(
60           chunk.substr(delimiter + 1));
61     }
62   }
63 
64   // Returns all the values found for the given |tag| joined with a new line.
TagValue(const std::string & tag) const65   std::string TagValue(const std::string& tag) const {
66     const auto& tag_values = tags_.find(tag);
67     if (tag_values == tags_.end()) {
68       return "";
69     }
70     return android::base::Join(tag_values->second, "\n");
71   }
72 
73  private:
74   std::unordered_map<std::string, std::vector<std::string>> tags_;
75 };
76 
AddModulesFromPath(std::unordered_set<std::string> & modules,const std::string_view & config_path,bool optional)77 android::base::Result<void> AddModulesFromPath(
78     std::unordered_set<std::string>& modules,
79     const std::string_view& config_path, bool optional) {
80   if (!std::filesystem::exists(config_path)) {
81     if (optional) {
82       GTEST_LOG_(INFO) << "Config file " << config_path << " does not exist.";
83       return {};
84     }
85     return android::base::Error()
86            << "Config file " << config_path << " does not exist.";
87   }
88   std::string kernel_modules_content;
89   if (!android::base::ReadFileToString(std::string(config_path),
90                                        &kernel_modules_content)) {
91     return android::base::ErrnoError()
92            << "Failed to read file at " << config_path;
93   }
94   tinyxml2::XMLDocument kernel_modules_xml;
95   const auto& xml_error =
96       kernel_modules_xml.Parse(kernel_modules_content.c_str());
97   if (tinyxml2::XMLError::XML_SUCCESS != xml_error) {
98     return android::base::Error()
99            << "Failed to parse kernel modules config: "
100            << tinyxml2::XMLDocument::ErrorIDToName(xml_error);
101   }
102   const tinyxml2::XMLElement* const kernel_modules_element =
103       kernel_modules_xml.RootElement();
104   for (const tinyxml2::XMLElement* module_element =
105            kernel_modules_element->FirstChildElement("module");
106        module_element != nullptr;
107        module_element = module_element->NextSiblingElement("module")) {
108     modules.insert(std::string(module_element->Attribute("value")));
109   }
110   return {};
111 }
112 
GetAckModules()113 android::base::Result<std::unordered_set<std::string>> GetAckModules() {
114   std::unordered_set<std::string> modules;
115   // Load information from the test data.
116   const auto& exec_dir = android::base::GetExecutableDirectory();
117   const auto& kernel_modules_config = exec_dir + "/kernel-modules.xml";
118   if (!AddModulesFromPath(modules, kernel_modules_config,
119                           /* optional = */ false)
120            .ok()) {
121     return android::base::Error()
122            << "Failed to read test data from " << kernel_modules_config;
123   }
124   // Then check if there is additional information available from the device.
125   if (!AddModulesFromPath(modules, kOptionalKernelModulesConfigPath,
126                           /* optional = */ true)
127            .ok()) {
128     return android::base::Error() << "Failed to read test data from "
129                                   << kOptionalKernelModulesConfigPath;
130   }
131   return modules;
132 }
133 
sha256(const std::string & content)134 std::string sha256(const std::string& content) {
135   unsigned char hash[SHA256_DIGEST_LENGTH];
136   const unsigned char* data = (const unsigned char*)content.data();
137   SHA256(data, content.size(), hash);
138   std::ostringstream os;
139   os << std::hex << std::setfill('0');
140   for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i) {
141     os << std::setw(2) << static_cast<unsigned int>(hash[i]);
142   }
143   return os.str();
144 }
145 
146 class BuiltWithDdkTest : public testing::Test {
147  protected:
SetUp()148   void SetUp() override {
149     // Fetch device runtime information.
150     const auto& runtime_info = android::vintf::VintfObject::GetRuntimeInfo();
151     ASSERT_NE(nullptr, runtime_info);
152 
153     constexpr uint64_t kMinAndroidRelease = 16;
154     const auto& kernel_version = runtime_info->kernelVersion();
155     const auto& kernel_release = android::kver::KernelRelease::Parse(
156         runtime_info->osRelease(), /* allow_suffix = */ true);
157 
158     if (!kernel_release.has_value() ||
159         (kernel_release->android_release() < kMinAndroidRelease)) {
160       GTEST_SKIP() << "The test only applies to android" << kMinAndroidRelease
161                    << " or later kernels.";
162     }
163     if (runtime_info->kernelVersion().dropMinor() <
164         android::vintf::Version{6, 12}) {
165       GTEST_SKIP() << "Exempt from built with DDK test. Kernel: "
166                    << kernel_version.version << " " << kernel_version.majorRev;
167     }
168 
169     slot_suffix_ = android::base::GetProperty("ro.boot.slot_suffix", "");
170 
171     // Get the information of ACK modules from the test data and from the system
172     // if available.
173     ack_modules_ = GetAckModules();
174     ASSERT_RESULT_OK(ack_modules_) << "Unable to read list of ACK modules.";
175   }
176   android::base::Result<std::unordered_set<std::string>> ack_modules_;
177   std::string slot_suffix_;
178 };
179 
ModuleHash(const std::string & name,const std::string & author,const std::string & license)180 std::string ModuleHash(const std::string& name, const std::string& author,
181                        const std::string& license) {
182   return sha256(name + author + license);
183 }
184 
AddModulesFromPartition(std::vector<std::filesystem::path> & modules,const std::string & partition)185 android::base::Result<void> AddModulesFromPartition(
186     std::vector<std::filesystem::path>& modules, const std::string& partition) {
187   int modules_found = 0;
188   if (!std::filesystem::is_directory(partition)) {
189     return android::base::Error() << "Unable to analyze path " << partition;
190   }
191   for (const auto& path_entry :
192        std::filesystem::recursive_directory_iterator(partition)) {
193     if (path_entry.path().extension() == ".ko") {
194       modules.push_back(path_entry);
195       ++modules_found;
196     }
197   }
198   GTEST_LOG_(INFO) << modules_found << " modules found within " << partition;
199   return {};
200 }
201 
InspectModule(const std::unordered_set<std::string> & ack_modules,const std::filesystem::path & module_path)202 android::base::Result<void> InspectModule(
203     const std::unordered_set<std::string>& ack_modules,
204     const std::filesystem::path& module_path) {
205   android::elf64::Elf64Binary elf;
206   if (!android::elf64::Elf64Parser::ParseElfFile(module_path, elf)) {
207     GTEST_LOG_(WARNING) << "Unable to parse module at " << module_path;
208     return {};
209   }
210   ModinfoTags modinfo_tags;
211   for (int i = 0; i < elf.sections.size(); i++) {
212     android::elf64::Elf64_Sc& section = elf.sections[i];
213     // Skip irrelevant sections
214     if (section.name != ".modinfo") continue;
215     // Ensure the buffer is zero terminated.
216     if (section.data.back() != '\0') {
217       section.data.push_back('\0');
218     }
219     modinfo_tags.ParseData(section.data);
220     break;
221   }
222   // GKI Module
223   // TODO: b/374932907 -- Despite the fact that technically GKI modules are a
224   // subset of ACK modules add a dedicated check for them in V2.
225 
226   // ACK
227   const std::string module_hash =
228       ModuleHash(modinfo_tags.TagValue("name"), modinfo_tags.TagValue("author"),
229                  modinfo_tags.TagValue("license"));
230   if (ack_modules.contains(module_hash)) {
231     return {};
232   }
233   // DDK
234   if (modinfo_tags.TagValue("built_with") == "DDK") {
235     return {};
236   }
237   return android::base::Error()
238          << "Non compliant module found: " << module_path;
239 }
240 
241 // @VsrTest = 3.4.2
TEST_F(BuiltWithDdkTest,SystemModules)242 TEST_F(BuiltWithDdkTest, SystemModules) {
243   std::vector<std::filesystem::path> device_module_paths;
244   ASSERT_RESULT_OK(
245       AddModulesFromPartition(device_module_paths, "/vendor_dlkm/"));
246   ASSERT_RESULT_OK(
247       AddModulesFromPartition(device_module_paths, "/system_dlkm/"));
248 
249   // Run the inspection for each module found.
250   for (const auto& module_path : device_module_paths) {
251     EXPECT_RESULT_OK(InspectModule(ack_modules_.value(), module_path));
252   }
253 }
254 
InspectExtractedRamdisk(const std::filesystem::path & extracted_ramdisk_path,const std::unordered_set<std::string> & ack_modules)255 void InspectExtractedRamdisk(
256     const std::filesystem::path& extracted_ramdisk_path,
257     const std::unordered_set<std::string>& ack_modules) {
258   std::vector<std::filesystem::path> kernel_module_paths;
259 
260   for (auto& path_entry :
261        std::filesystem::recursive_directory_iterator(extracted_ramdisk_path)) {
262     if (path_entry.path().extension() == ".ko") {
263       kernel_module_paths.push_back(path_entry);
264     }
265   }
266 
267   // Run the inspection for each module found.
268   for (const auto& module_path : kernel_module_paths) {
269     EXPECT_RESULT_OK(InspectModule(ack_modules, module_path));
270   }
271 }
272 
273 // @VsrTest = 3.4.2
TEST_F(BuiltWithDdkTest,BootModules)274 TEST_F(BuiltWithDdkTest, BootModules) {
275   const std::string boot_path = "/dev/block/by-name/init_boot" + slot_suffix_;
276   if (!std::filesystem::exists(boot_path)) {
277     GTEST_SKIP() << "Boot path " << boot_path << " does not exist.";
278   }
279   const auto extracted_ramdisk = android::ExtractRamdiskToDirectory(boot_path);
280   ASSERT_TRUE(extracted_ramdisk.ok())
281       << "Failed to extract ramdisk: " << extracted_ramdisk.error();
282   InspectExtractedRamdisk((*extracted_ramdisk)->path, ack_modules_.value());
283 }
284 
285 // TODO: b/374932907 -- Verify if this is enough for vendor_kernel_boot as well.
286 // @VsrTest = 3.4.2
TEST_F(BuiltWithDdkTest,VendorBootModules)287 TEST_F(BuiltWithDdkTest, VendorBootModules) {
288   const std::string vendor_boot_path =
289       "/dev/block/by-name/vendor_boot" + slot_suffix_;
290 
291   if (!std::filesystem::exists(vendor_boot_path)) {
292     GTEST_SKIP() << "Boot path " << vendor_boot_path << " does not exist.";
293   }
294   const auto extracted_vendor_ramdisk =
295       android::ExtractVendorRamdiskToDirectory(vendor_boot_path);
296 
297   ASSERT_TRUE(extracted_vendor_ramdisk.ok())
298       << "Failed to extract vendor_ramdisk: "
299       << extracted_vendor_ramdisk.error();
300 
301   InspectExtractedRamdisk((*extracted_vendor_ramdisk)->path,
302                           ack_modules_.value());
303 }
304 
305 }  // namespace
306 }  // namespace android
307 
main(int argc,char * argv[])308 int main(int argc, char* argv[]) {
309   ::testing::InitGoogleTest(&argc, argv);
310   android::base::InitLogging(argv, android::base::StderrLogger);
311   return RUN_ALL_TESTS();
312 }
313