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