blob: d9a555a3ea27df19b4b7ec3588f08a8c2af15de6 [file] [log] [blame]
Calin Juravle33787682019-07-26 14:27:18 -07001/*
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
27namespace art {
28
29using verifier::VerifierDeps;
30
31class Dex2oatVdexTest : public Dex2oatEnvironmentTest {
32 public:
33 void TearDown() override {
34 Dex2oatEnvironmentTest::TearDown();
35
36 output_ = "";
37 error_msg_ = "";
38 opened_vdex_files_.clear();
39 }
40
41 protected:
42 bool RunDex2oat(const std::string& dex_location,
43 const std::string& odex_location,
44 const std::string* public_sdk,
Calin Juravle046cecf2020-12-07 18:31:28 -080045 bool copy_dex_files = false,
Calin Juravle33787682019-07-26 14:27:18 -070046 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");
Calin Juravle046cecf2020-12-07 18:31:28 -080057 if (!copy_dex_files) {
58 args.push_back("--copy-dex-files=false");
59 }
Calin Juravle33787682019-07-26 14:27:18 -070060 args.push_back("--runtime-arg");
Calin Juravle046cecf2020-12-07 18:31:28 -080061 args.push_back("-verbose:verifier,compiler");
Calin Juravle33787682019-07-26 14:27:18 -070062 // 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
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.
Nicolas Geoffraya129d8a2021-03-18 22:23:04 +000084 VdexFile::VdexFileHeader vdex_header = vdex->GetVdexFileHeader();
Calin Juravle33787682019-07-26 14:27:18 -070085 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
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
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
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
Calin Juravle046cecf2020-12-07 18:31:28 -0800134 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
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
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
Calin Juravle33787682019-07-26 14:27:18 -0700153 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
161TEST_F(Dex2oatVdexTest, VerifyPublicSdkStubs) {
162 std::string error_msg;
Calin Juravle33787682019-07-26 14:27:18 -0700163
164 // Dex2oatVdexTestDex is the subject app using normal APIs found in the boot classpath.
165 std::unique_ptr<const DexFile> dex_file(OpenTestDexFile("Dex2oatVdexTestDex"));
Calin Juravle33787682019-07-26 14:27:18 -0700166 // 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
Calin Juravle046cecf2020-12-07 18:31:28 -0800170 ASSERT_TRUE(RunDex2oat(dex_file->GetLocation(), GetOdex(dex_file), &api_dex_location));
Calin Juravle33787682019-07-26 14:27:18 -0700171
Calin Juravle046cecf2020-12-07 18:31:28 -0800172 std::unique_ptr<VerifierDeps> deps = GetVerifierDeps(GetVdex(dex_file), dex_file.get());
Calin Juravle33787682019-07-26 14:27:18 -0700173
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
Nicolas Geoffrayc3c44172021-01-07 10:03:39 +0000181 // 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));
Calin Juravle33787682019-07-26 14:27:18 -0700187 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
Calin Juravle046cecf2020-12-07 18:31:28 -0800193 std::string dm_file = GetScratchDir() + "/base.dm";
194 CreateDexMetadata(GetVdex(dex_file), dm_file);
Calin Juravle33787682019-07-26 14:27:18 -0700195 std::vector<std::string> extra_args;
196 extra_args.push_back("--dm-file=" + dm_file);
Calin Juravle33787682019-07-26 14:27:18 -0700197 output_ = "";
Calin Juravle046cecf2020-12-07 18:31:28 -0800198 ASSERT_TRUE(RunDex2oat(dex_file->GetLocation(), GetOdex(dex_file), nullptr, false, extra_args));
Calin Juravle33787682019-07-26 14:27:18 -0700199
Calin Juravle046cecf2020-12-07 18:31:28 -0800200 std::unique_ptr<VerifierDeps> deps2 = GetVerifierDeps(GetVdex(dex_file), dex_file.get());
Calin Juravle33787682019-07-26 14:27:18 -0700201
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
Nicolas Geoffrayc3c44172021-01-07 10:03:39 +0000208 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));
Calin Juravle33787682019-07-26 14:27:18 -0700212 ASSERT_TRUE(HasVerifiedClass(deps2, "LAccessNonPublicStaticField;", *dex_file));
213}
214
Calin Juravle046cecf2020-12-07 18:31:28 -0800215// Check that if the input dm does contain dex files then the compilation fails
216TEST_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
Calin Juravle32bf6d32021-02-02 19:29:29 -0800245// Check that corrupt vdex files from .dm archives are ignored.
246TEST_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
Calin Juravle33787682019-07-26 14:27:18 -0700269} // namespace art