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