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