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