• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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                                                   /*low_4gb=*/false,
78                                                   &error_msg));
79     // Check the vdex doesn't have dex.
80     if (vdex->HasDexSection()) {
81       return Errorf("The vdex {} should not contain dex code", vdex_location);
82     }
83 
84     // Verify the deps.
85     VdexFile::VdexFileHeader vdex_header = vdex->GetVdexFileHeader();
86     if (!vdex_header.IsValid()) {
87       return Errorf("Invalid vdex header in {}", vdex_location);
88     }
89 
90     std::vector<const DexFile*> dex_files;
91     dex_files.push_back(dex_file);
92     std::unique_ptr<VerifierDeps> deps(new VerifierDeps(dex_files, /*output_only=*/false));
93 
94     if (!deps->ParseStoredData(dex_files, vdex->GetVerifierDepsData())) {
95       return Errorf("{}", error_msg);
96     }
97 
98     opened_vdex_files_.push_back(std::move(vdex));
99     return deps;
100   }
101 
GetClassDefIndex(const std::string & cls,const DexFile & dex_file)102   uint16_t GetClassDefIndex(const std::string& cls, const DexFile& dex_file) {
103     const dex::TypeId* type_id = dex_file.FindTypeId(cls.c_str());
104     DCHECK(type_id != nullptr);
105     dex::TypeIndex type_idx = dex_file.GetIndexForTypeId(*type_id);
106     const dex::ClassDef* class_def = dex_file.FindClassDef(type_idx);
107     DCHECK(class_def != nullptr);
108     return dex_file.GetIndexForClassDef(*class_def);
109   }
110 
HasVerifiedClass(const std::unique_ptr<VerifierDeps> & deps,const std::string & cls,const DexFile & dex_file)111   bool HasVerifiedClass(const std::unique_ptr<VerifierDeps>& deps,
112                         const std::string& cls,
113                         const DexFile& dex_file) {
114     uint16_t class_def_idx = GetClassDefIndex(cls, dex_file);
115     return deps->GetVerifiedClasses(dex_file)[class_def_idx];
116   }
117 
GetFilename(const std::unique_ptr<const DexFile> & dex_file)118   std::string GetFilename(const std::unique_ptr<const DexFile>& dex_file) {
119     const std::string& str = dex_file->GetLocation();
120     size_t idx = str.rfind('/');
121     if (idx == std::string::npos) {
122       return str;
123     }
124     return str.substr(idx + 1);
125   }
126 
GetOdex(const std::unique_ptr<const DexFile> & dex_file,const std::string & suffix="")127   std::string GetOdex(const std::unique_ptr<const DexFile>& dex_file,
128                       const std::string& suffix = "") {
129     return GetScratchDir() + "/" + GetFilename(dex_file) + suffix + ".odex";
130   }
131 
GetVdex(const std::unique_ptr<const DexFile> & dex_file,const std::string & suffix="")132   std::string GetVdex(const std::unique_ptr<const DexFile>& dex_file,
133                       const std::string& suffix = "") {
134     return GetScratchDir() + "/" + GetFilename(dex_file) + suffix + ".vdex";
135   }
136 
137   std::string output_;
138   std::vector<std::unique_ptr<VdexFile>> opened_vdex_files_;
139 };
140 
141 // Validates verification against public API stubs:
142 // - create a vdex file contraints by a predefined list of public API (passed as separate dex)
143 // - compile with the above vdex file as input to validate the compilation flow
TEST_F(Dex2oatVdexTest,VerifyPublicSdkStubs)144 TEST_F(Dex2oatVdexTest, VerifyPublicSdkStubs) {
145   // Dex2oatVdexTestDex is the subject app using normal APIs found in the boot classpath.
146   std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Dex2oatVdexTestDex"));
147   // Dex2oatVdexPublicSdkDex serves as the public API-stubs, restricting what can be verified.
148   const std::string api_dex_location = GetTestDexFileName("Dex2oatVdexPublicSdkDex");
149 
150   // Compile the subject app using the predefined API-stubs
151   ASSERT_THAT(RunDex2oat(dex_file->GetLocation(), GetOdex(dex_file), &api_dex_location),
152               HasValue(true));
153 
154   std::unique_ptr<VerifierDeps> deps =
155       OR_ASSERT_FAIL(GetVerifierDeps(GetVdex(dex_file), dex_file.get()));
156 
157   // Verify public API usage. The classes should be verified.
158   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessPublicCtor;", *dex_file));
159   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessPublicMethod;", *dex_file));
160   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessPublicMethodFromParent;", *dex_file));
161   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessPublicStaticMethod;", *dex_file));
162   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessPublicStaticField;", *dex_file));
163 
164   // Verify NON public API usage. The classes should be verified, but will run
165   // with access checks.
166   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessNonPublicCtor;", *dex_file));
167   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessNonPublicMethod;", *dex_file));
168   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessNonPublicMethodFromParent;", *dex_file));
169   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessNonPublicStaticMethod;", *dex_file));
170   ASSERT_TRUE(HasVerifiedClass(deps, "LAccessNonPublicStaticField;", *dex_file));
171 
172   // Compile again without public API stubs but with the previously generated vdex.
173   // This simulates a normal install where the apk has its code pre-verified.
174   // The results should be the same.
175 
176   std::string dm_file = GetScratchDir() + "/base.dm";
177   CreateDexMetadata(GetVdex(dex_file), dm_file);
178   std::vector<std::string> extra_args;
179   extra_args.push_back("--dm-file=" + dm_file);
180   output_ = "";
181   ASSERT_THAT(RunDex2oat(dex_file->GetLocation(), GetOdex(dex_file), nullptr, false, extra_args),
182               HasValue(true));
183 
184   std::unique_ptr<VerifierDeps> deps2 =
185       OR_ASSERT_FAIL(GetVerifierDeps(GetVdex(dex_file), dex_file.get()));
186 
187   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessPublicCtor;", *dex_file));
188   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessPublicMethod;", *dex_file));
189   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessPublicMethodFromParent;", *dex_file));
190   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessPublicStaticMethod;", *dex_file));
191   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessPublicStaticField;", *dex_file));
192 
193   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessNonPublicCtor;", *dex_file)) << output_;
194   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessNonPublicMethod;", *dex_file));
195   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessNonPublicMethodFromParent;", *dex_file));
196   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessNonPublicStaticMethod;", *dex_file));
197   ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessNonPublicStaticField;", *dex_file));
198 }
199 
200 // Check that if the input dm does contain dex files then the compilation fails
TEST_F(Dex2oatVdexTest,VerifyPublicSdkStubsWithDexFiles)201 TEST_F(Dex2oatVdexTest, VerifyPublicSdkStubsWithDexFiles) {
202   // Dex2oatVdexTestDex is the subject app using normal APIs found in the boot classpath.
203   std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Dex2oatVdexTestDex"));
204 
205   // Compile the subject app using the predefined API-stubs
206   ASSERT_THAT(RunDex2oat(dex_file->GetLocation(),
207                          GetOdex(dex_file),
208                          /*public_sdk=*/nullptr,
209                          /*copy_dex_files=*/true),
210               HasValue(true));
211 
212   // Create the .dm file with the output.
213   std::string dm_file = GetScratchDir() + "/base.dm";
214   CreateDexMetadata(GetVdex(dex_file), dm_file);
215   std::vector<std::string> extra_args;
216   extra_args.push_back("--dm-file=" + dm_file);
217 
218   // Recompile again with the .dm file which contains a vdex with code.
219   // The compilation will pass, but dex2oat will not use the vdex file.
220   ASSERT_THAT(RunDex2oat(dex_file->GetLocation(),
221                          GetOdex(dex_file, "v2"),
222                          /*public_sdk=*/nullptr,
223                          /*copy_dex_files=*/true,
224                          extra_args),
225               HasValue(true));
226 }
227 
228 // Check that corrupt vdex files from .dm archives are ignored.
TEST_F(Dex2oatVdexTest,VerifyCorruptVdexFile)229 TEST_F(Dex2oatVdexTest, VerifyCorruptVdexFile) {
230   // Dex2oatVdexTestDex is the subject app using normal APIs found in the boot classpath.
231   std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Dex2oatVdexTestDex"));
232 
233   // Create the .dm file with the output.
234   // Instead passing the vdex files, pass the actual dex file. This will simulate a vdex corruption.
235   // The compiler should ignore it.
236   std::string dm_file = GetScratchDir() + "/base.dm";
237   CreateDexMetadata(dex_file->GetLocation(), dm_file);
238   std::vector<std::string> extra_args;
239   extra_args.push_back("--dm-file=" + dm_file);
240 
241   // Compile the dex file. Despite having a corrupt input .vdex, we should not crash.
242   ASSERT_THAT(RunDex2oat(dex_file->GetLocation(),
243                          GetOdex(dex_file),
244                          /*public_sdk=*/nullptr,
245                          /*copy_dex_files=*/true,
246                          extra_args),
247               HasValue(true))
248       << output_;
249 }
250 
251 // Check that if the input dm a vdex with mismatching checksums the compilation fails
TEST_F(Dex2oatVdexTest,VerifyInputDmWithMismatchedChecksums)252 TEST_F(Dex2oatVdexTest, VerifyInputDmWithMismatchedChecksums) {
253   // Generate a vdex file for Dex2oatVdexTestDex.
254   std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Dex2oatVdexTestDex"));
255 
256   ASSERT_THAT(RunDex2oat(dex_file->GetLocation(),
257                          GetOdex(dex_file),
258                          /*public_sdk=*/nullptr,
259                          /*copy_dex_files=*/false),
260               HasValue(true));
261 
262   // Create the .dm file with the output.
263   std::string dm_file = GetScratchDir() + "/base.dm";
264   CreateDexMetadata(GetVdex(dex_file), dm_file);
265   std::vector<std::string> extra_args;
266   extra_args.push_back("--dm-file=" + dm_file);
267 
268   // Try to compile Main using an input dm which contains the vdex for
269   // Dex2oatVdexTestDex. It should fail.
270   std::unique_ptr<const DexFile> dex_file2(OpenTestDexFile("Main"));
271   ASSERT_THAT(RunDex2oat(dex_file2->GetLocation(),
272                          GetOdex(dex_file2, "v2"),
273                          /*public_sdk=*/nullptr,
274                          /*copy_dex_files=*/false,
275                          extra_args),
276               HasValue(false))
277       << output_;
278 }
279 
280 }  // namespace art
281