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