xref: /aosp_15_r20/art/dex2oat/dex2oat_vdex_test.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1 /*
2  * Copyright (C) 2020 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 <string>
18 #include <vector>
19 
20 #include "android-base/result-gmock.h"
21 #include "android-base/result.h"
22 #include "common_runtime_test.h"
23 #include "dex2oat_environment_test.h"
24 #include "vdex_file.h"
25 #include "verifier/verifier_deps.h"
26 
27 namespace art {
28 
29 using ::android::base::Result;
30 using ::android::base::testing::HasValue;
31 using verifier::VerifierDeps;
32 
33 class Dex2oatVdexTest : public Dex2oatEnvironmentTest {
34  public:
TearDown()35   void TearDown() override {
36     Dex2oatEnvironmentTest::TearDown();
37 
38     output_ = "";
39     opened_vdex_files_.clear();
40   }
41 
42  protected:
RunDex2oat(const std::string & dex_location,const std::string & odex_location,const std::string * public_sdk,bool copy_dex_files=false,const std::vector<std::string> & extra_args={})43   Result<bool> RunDex2oat(const std::string& dex_location,
44                           const std::string& odex_location,
45                           const std::string* public_sdk,
46                           bool copy_dex_files = false,
47                           const std::vector<std::string>& extra_args = {}) {
48     std::vector<std::string> args;
49     args.push_back("--dex-file=" + dex_location);
50     args.push_back("--oat-file=" + odex_location);
51     if (public_sdk != nullptr) {
52       args.push_back("--public-sdk=" + *public_sdk);
53     }
54     args.push_back("--compiler-filter=" +
55                    CompilerFilter::NameOfFilter(CompilerFilter::Filter::kVerify));
56     args.push_back("--runtime-arg");
57     args.push_back("-Xnorelocate");
58     if (!copy_dex_files) {
59       args.push_back("--copy-dex-files=false");
60     }
61     args.push_back("--runtime-arg");
62     args.push_back("-verbose:verifier,compiler");
63     // Use a single thread to facilitate debugging. We only compile tiny dex files.
64     args.push_back("-j1");
65 
66     args.insert(args.end(), extra_args.begin(), extra_args.end());
67 
68     int status = OR_RETURN(Dex2Oat(args, &output_));
69     return status == 0;
70   }
71 
GetVerifierDeps(const std::string & vdex_location,const DexFile * dex_file)72   Result<std::unique_ptr<VerifierDeps>> GetVerifierDeps(const std::string& vdex_location,
73                                                         const DexFile* dex_file) {
74     // Verify the vdex file content: only the classes using public APIs should be verified.
75     std::string error_msg;
76     std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_location,
77                                                   /*writable=*/false,
78                                                   /*low_4gb=*/false,
79                                                   &error_msg));
80     // Check the vdex doesn't have dex.
81     if (vdex->HasDexSection()) {
82       return Errorf("The vdex {} should not contain dex code", vdex_location);
83     }
84 
85     // Verify the deps.
86     VdexFile::VdexFileHeader vdex_header = vdex->GetVdexFileHeader();
87     if (!vdex_header.IsValid()) {
88       return Errorf("Invalid vdex header in {}", vdex_location);
89     }
90 
91     std::vector<const DexFile*> dex_files;
92     dex_files.push_back(dex_file);
93     std::unique_ptr<VerifierDeps> deps(new VerifierDeps(dex_files, /*output_only=*/false));
94 
95     if (!deps->ParseStoredData(dex_files, vdex->GetVerifierDepsData())) {
96       return Errorf("{}", error_msg);
97     }
98 
99     opened_vdex_files_.push_back(std::move(vdex));
100     return deps;
101   }
102 
GetClassDefIndex(const std::string & cls,const DexFile & dex_file)103   uint16_t GetClassDefIndex(const std::string& cls, const DexFile& dex_file) {
104     const dex::TypeId* type_id = dex_file.FindTypeId(cls.c_str());
105     DCHECK(type_id != nullptr);
106     dex::TypeIndex type_idx = dex_file.GetIndexForTypeId(*type_id);
107     const dex::ClassDef* class_def = dex_file.FindClassDef(type_idx);
108     DCHECK(class_def != nullptr);
109     return dex_file.GetIndexForClassDef(*class_def);
110   }
111 
HasVerifiedClass(const std::unique_ptr<VerifierDeps> & deps,const std::string & cls,const DexFile & dex_file)112   bool HasVerifiedClass(const std::unique_ptr<VerifierDeps>& deps,
113                         const std::string& cls,
114                         const DexFile& dex_file) {
115     uint16_t class_def_idx = GetClassDefIndex(cls, dex_file);
116     return deps->GetVerifiedClasses(dex_file)[class_def_idx];
117   }
118 
GetFilename(const std::unique_ptr<const DexFile> & dex_file)119   std::string GetFilename(const std::unique_ptr<const DexFile>& dex_file) {
120     const std::string& str = dex_file->GetLocation();
121     size_t idx = str.rfind('/');
122     if (idx == std::string::npos) {
123       return str;
124     }
125     return str.substr(idx + 1);
126   }
127 
GetOdex(const std::unique_ptr<const DexFile> & dex_file,const std::string & suffix="")128   std::string GetOdex(const std::unique_ptr<const DexFile>& dex_file,
129                       const std::string& suffix = "") {
130     return GetScratchDir() + "/" + GetFilename(dex_file) + suffix + ".odex";
131   }
132 
GetVdex(const std::unique_ptr<const DexFile> & dex_file,const std::string & suffix="")133   std::string GetVdex(const std::unique_ptr<const DexFile>& dex_file,
134                       const std::string& suffix = "") {
135     return GetScratchDir() + "/" + GetFilename(dex_file) + suffix + ".vdex";
136   }
137 
138   std::string output_;
139   std::vector<std::unique_ptr<VdexFile>> opened_vdex_files_;
140 };
141 
142 // Validates verification against public API stubs:
143 // - create a vdex file contraints by a predefined list of public API (passed as separate dex)
144 // - compile with the above vdex file as input to validate the compilation flow
TEST_F(Dex2oatVdexTest,VerifyPublicSdkStubs)145 TEST_F(Dex2oatVdexTest, VerifyPublicSdkStubs) {
146   // Dex2oatVdexTestDex is the subject app using normal APIs found in the boot classpath.
147   std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Dex2oatVdexTestDex"));
148   // Dex2oatVdexPublicSdkDex serves as the public API-stubs, restricting what can be verified.
149   const std::string api_dex_location = GetTestDexFileName("Dex2oatVdexPublicSdkDex");
150 
151   // Compile the subject app using the predefined API-stubs
152   ASSERT_THAT(RunDex2oat(dex_file->GetLocation(), GetOdex(dex_file), &api_dex_location),
153               HasValue(true));
154 
155   std::unique_ptr<VerifierDeps> deps =
156       OR_ASSERT_FAIL(GetVerifierDeps(GetVdex(dex_file), dex_file.get()));
157 
158   // Verify public API usage. The classes should be verified.
159   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessPublicCtor;", *dex_file));
160   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessPublicMethod;", *dex_file));
161   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessPublicMethodFromParent;", *dex_file));
162   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessPublicStaticMethod;", *dex_file));
163   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessPublicStaticField;", *dex_file));
164 
165   // Verify NON public API usage. The classes should be verified, but will run
166   // with access checks.
167   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessNonPublicCtor;", *dex_file));
168   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessNonPublicMethod;", *dex_file));
169   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessNonPublicMethodFromParent;", *dex_file));
170   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessNonPublicStaticMethod;", *dex_file));
171   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessNonPublicStaticField;", *dex_file));
172 
173   // Compile again without public API stubs but with the previously generated vdex.
174   // This simulates a normal install where the apk has its code pre-verified.
175   // The results should be the same.
176 
177   std::string dm_file = GetScratchDir() + "/base.dm";
178   CreateDexMetadata(GetVdex(dex_file), dm_file);
179   std::vector<std::string> extra_args;
180   extra_args.push_back("--dm-file=" + dm_file);
181   output_ = "";
182   ASSERT_THAT(RunDex2oat(dex_file->GetLocation(), GetOdex(dex_file), nullptr, false, extra_args),
183               HasValue(true));
184 
185   std::unique_ptr<VerifierDeps> deps2 =
186       OR_ASSERT_FAIL(GetVerifierDeps(GetVdex(dex_file), dex_file.get()));
187 
188   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessPublicCtor;", *dex_file));
189   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessPublicMethod;", *dex_file));
190   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessPublicMethodFromParent;", *dex_file));
191   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessPublicStaticMethod;", *dex_file));
192   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessPublicStaticField;", *dex_file));
193 
194   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessNonPublicCtor;", *dex_file)) << output_;
195   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessNonPublicMethod;", *dex_file));
196   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessNonPublicMethodFromParent;", *dex_file));
197   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessNonPublicStaticMethod;", *dex_file));
198   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessNonPublicStaticField;", *dex_file));
199 }
200 
201 // Check that if the input dm does contain dex files then the compilation fails
TEST_F(Dex2oatVdexTest,VerifyPublicSdkStubsWithDexFiles)202 TEST_F(Dex2oatVdexTest, VerifyPublicSdkStubsWithDexFiles) {
203   // Dex2oatVdexTestDex is the subject app using normal APIs found in the boot classpath.
204   std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Dex2oatVdexTestDex"));
205 
206   // Compile the subject app using the predefined API-stubs
207   ASSERT_THAT(RunDex2oat(dex_file->GetLocation(),
208                          GetOdex(dex_file),
209                          /*public_sdk=*/nullptr,
210                          /*copy_dex_files=*/true),
211               HasValue(true));
212 
213   // Create the .dm file with the output.
214   std::string dm_file = GetScratchDir() + "/base.dm";
215   CreateDexMetadata(GetVdex(dex_file), dm_file);
216   std::vector<std::string> extra_args;
217   extra_args.push_back("--dm-file=" + dm_file);
218 
219   // Recompile again with the .dm file which contains a vdex with code.
220   // The compilation will pass, but dex2oat will not use the vdex file.
221   ASSERT_THAT(RunDex2oat(dex_file->GetLocation(),
222                          GetOdex(dex_file, "v2"),
223                          /*public_sdk=*/nullptr,
224                          /*copy_dex_files=*/true,
225                          extra_args),
226               HasValue(true));
227 }
228 
229 // Check that corrupt vdex files from .dm archives are ignored.
TEST_F(Dex2oatVdexTest,VerifyCorruptVdexFile)230 TEST_F(Dex2oatVdexTest, VerifyCorruptVdexFile) {
231   // Dex2oatVdexTestDex is the subject app using normal APIs found in the boot classpath.
232   std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Dex2oatVdexTestDex"));
233 
234   // Create the .dm file with the output.
235   // Instead passing the vdex files, pass the actual dex file. This will simulate a vdex corruption.
236   // The compiler should ignore it.
237   std::string dm_file = GetScratchDir() + "/base.dm";
238   CreateDexMetadata(dex_file->GetLocation(), dm_file);
239   std::vector<std::string> extra_args;
240   extra_args.push_back("--dm-file=" + dm_file);
241 
242   // Compile the dex file. Despite having a corrupt input .vdex, we should not crash.
243   ASSERT_THAT(RunDex2oat(dex_file->GetLocation(),
244                          GetOdex(dex_file),
245                          /*public_sdk=*/nullptr,
246                          /*copy_dex_files=*/true,
247                          extra_args),
248               HasValue(true))
249       << output_;
250 }
251 
252 // Check that if the input dm a vdex with mismatching checksums the compilation fails
TEST_F(Dex2oatVdexTest,VerifyInputDmWithMismatchedChecksums)253 TEST_F(Dex2oatVdexTest, VerifyInputDmWithMismatchedChecksums) {
254   // Generate a vdex file for Dex2oatVdexTestDex.
255   std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Dex2oatVdexTestDex"));
256 
257   ASSERT_THAT(RunDex2oat(dex_file->GetLocation(),
258                          GetOdex(dex_file),
259                          /*public_sdk=*/nullptr,
260                          /*copy_dex_files=*/false),
261               HasValue(true));
262 
263   // Create the .dm file with the output.
264   std::string dm_file = GetScratchDir() + "/base.dm";
265   CreateDexMetadata(GetVdex(dex_file), dm_file);
266   std::vector<std::string> extra_args;
267   extra_args.push_back("--dm-file=" + dm_file);
268 
269   // Try to compile Main using an input dm which contains the vdex for
270   // Dex2oatVdexTestDex. It should fail.
271   std::unique_ptr<const DexFile> dex_file2(OpenTestDexFile("Main"));
272   ASSERT_THAT(RunDex2oat(dex_file2->GetLocation(),
273                          GetOdex(dex_file2, "v2"),
274                          /*public_sdk=*/nullptr,
275                          /*copy_dex_files=*/false,
276                          extra_args),
277               HasValue(false))
278       << output_;
279 }
280 
281 }  // namespace art
282