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 #define TRACE_TAG INCREMENTAL
18
19 #include "incremental_utils.h"
20
21 #include <android-base/endian.h>
22 #include <android-base/mapped_file.h>
23 #include <android-base/strings.h>
24 #include <ziparchive/zip_archive.h>
25 #include <ziparchive/zip_writer.h>
26
27 #include <algorithm>
28 #include <array>
29 #include <cinttypes>
30 #include <numeric>
31 #include <unordered_set>
32
33 #include "adb_io.h"
34 #include "adb_trace.h"
35 #include "sysdeps.h"
36
37 using namespace std::literals;
38
39 namespace incremental {
40
offsetToBlockIndex(int64_t offset)41 static constexpr inline int32_t offsetToBlockIndex(int64_t offset) {
42 return (offset & ~(kBlockSize - 1)) >> 12;
43 }
44
verity_tree_blocks_for_file(Size fileSize)45 Size verity_tree_blocks_for_file(Size fileSize) {
46 if (fileSize == 0) {
47 return 0;
48 }
49
50 constexpr int hash_per_block = kBlockSize / kDigestSize;
51
52 Size total_tree_block_count = 0;
53
54 const Size block_count = 1 + (fileSize - 1) / kBlockSize;
55 Size hash_block_count = block_count;
56 while (hash_block_count > 1) {
57 hash_block_count = (hash_block_count + hash_per_block - 1) / hash_per_block;
58 total_tree_block_count += hash_block_count;
59 }
60 return total_tree_block_count;
61 }
62
verity_tree_size_for_file(Size fileSize)63 Size verity_tree_size_for_file(Size fileSize) {
64 return verity_tree_blocks_for_file(fileSize) * kBlockSize;
65 }
66
read_int32(borrowed_fd fd)67 static inline int32_t read_int32(borrowed_fd fd) {
68 int32_t result;
69 return ReadFdExactly(fd, &result, sizeof(result)) ? result : -1;
70 }
71
skip_int(borrowed_fd fd)72 static inline int32_t skip_int(borrowed_fd fd) {
73 return adb_lseek(fd, 4, SEEK_CUR);
74 }
75
append_int(borrowed_fd fd,std::vector<char> * bytes)76 static inline void append_int(borrowed_fd fd, std::vector<char>* bytes) {
77 int32_t le_val = read_int32(fd);
78 auto old_size = bytes->size();
79 bytes->resize(old_size + sizeof(le_val));
80 memcpy(bytes->data() + old_size, &le_val, sizeof(le_val));
81 }
82
append_bytes_with_size(borrowed_fd fd,std::vector<char> * bytes,int * bytes_left)83 static inline bool append_bytes_with_size(borrowed_fd fd, std::vector<char>* bytes,
84 int* bytes_left) {
85 int32_t le_size = read_int32(fd);
86 if (le_size < 0) {
87 return false;
88 }
89 int32_t size = int32_t(le32toh(le_size));
90 if (size < 0 || size > *bytes_left) {
91 return false;
92 }
93 if (size == 0) {
94 return true;
95 }
96 *bytes_left -= size;
97 auto old_size = bytes->size();
98 bytes->resize(old_size + sizeof(le_size) + size);
99 memcpy(bytes->data() + old_size, &le_size, sizeof(le_size));
100 ReadFdExactly(fd, bytes->data() + old_size + sizeof(le_size), size);
101 return true;
102 }
103
skip_bytes_with_size(borrowed_fd fd)104 static inline int32_t skip_bytes_with_size(borrowed_fd fd) {
105 int32_t le_size = read_int32(fd);
106 if (le_size < 0) {
107 return -1;
108 }
109 int32_t size = int32_t(le32toh(le_size));
110 return (int32_t)adb_lseek(fd, size, SEEK_CUR);
111 }
112
read_id_sig_headers(borrowed_fd fd)113 std::pair<std::vector<char>, int32_t> read_id_sig_headers(borrowed_fd fd) {
114 std::vector<char> signature;
115 append_int(fd, &signature); // version
116 int max_size = kMaxSignatureSize - sizeof(int32_t);
117 // hashingInfo and signingInfo
118 if (!append_bytes_with_size(fd, &signature, &max_size) ||
119 !append_bytes_with_size(fd, &signature, &max_size)) {
120 return {};
121 }
122 auto le_tree_size = read_int32(fd);
123 auto tree_size = int32_t(le32toh(le_tree_size)); // size of the verity tree
124 return {std::move(signature), tree_size};
125 }
126
skip_id_sig_headers(borrowed_fd fd)127 std::pair<off64_t, ssize_t> skip_id_sig_headers(borrowed_fd fd) {
128 skip_int(fd); // version
129 skip_bytes_with_size(fd); // hashingInfo
130 auto offset = skip_bytes_with_size(fd); // signingInfo
131 auto le_tree_size = read_int32(fd);
132 auto tree_size = int32_t(le32toh(le_tree_size)); // size of the verity tree
133 return {offset + sizeof(le_tree_size), tree_size};
134 }
135
136 template <class T>
valueAt(borrowed_fd fd,off64_t offset)137 static T valueAt(borrowed_fd fd, off64_t offset) {
138 T t;
139 memset(&t, 0, sizeof(T));
140 if (adb_pread(fd, &t, sizeof(T), offset) != sizeof(T)) {
141 memset(&t, -1, sizeof(T));
142 }
143
144 return t;
145 }
146
appendBlocks(int32_t start,int count,std::vector<int32_t> * blocks)147 static void appendBlocks(int32_t start, int count, std::vector<int32_t>* blocks) {
148 if (count == 1) {
149 blocks->push_back(start);
150 } else {
151 auto oldSize = blocks->size();
152 blocks->resize(oldSize + count);
153 std::iota(blocks->begin() + oldSize, blocks->end(), start);
154 }
155 }
156
157 template <class T>
unduplicate(std::vector<T> & v)158 static void unduplicate(std::vector<T>& v) {
159 std::unordered_set<T> uniques(v.size());
160 v.erase(std::remove_if(v.begin(), v.end(),
161 [&uniques](T t) { return !uniques.insert(t).second; }),
162 v.end());
163 }
164
CentralDirOffset(borrowed_fd fd,Size fileSize)165 static off64_t CentralDirOffset(borrowed_fd fd, Size fileSize) {
166 static constexpr int kZipEocdRecMinSize = 22;
167 static constexpr int32_t kZipEocdRecSig = 0x06054b50;
168 static constexpr int kZipEocdCentralDirSizeFieldOffset = 12;
169 static constexpr int kZipEocdCommentLengthFieldOffset = 20;
170
171 int32_t sigBuf = 0;
172 off64_t eocdOffset = -1;
173 off64_t maxEocdOffset = fileSize - kZipEocdRecMinSize;
174 int16_t commentLenBuf = 0;
175
176 // Search from the end of zip, backward to find beginning of EOCD
177 for (int16_t commentLen = 0; commentLen < fileSize; ++commentLen) {
178 sigBuf = valueAt<int32_t>(fd, maxEocdOffset - commentLen);
179 if (sigBuf == kZipEocdRecSig) {
180 commentLenBuf = valueAt<int16_t>(
181 fd, maxEocdOffset - commentLen + kZipEocdCommentLengthFieldOffset);
182 if (commentLenBuf == commentLen) {
183 eocdOffset = maxEocdOffset - commentLen;
184 break;
185 }
186 }
187 }
188
189 if (eocdOffset < 0) {
190 return -1;
191 }
192
193 off64_t cdLen = static_cast<int64_t>(
194 valueAt<int32_t>(fd, eocdOffset + kZipEocdCentralDirSizeFieldOffset));
195
196 return eocdOffset - cdLen;
197 }
198
199 // Does not support APKs larger than 4GB
SignerBlockOffset(borrowed_fd fd,Size fileSize)200 static off64_t SignerBlockOffset(borrowed_fd fd, Size fileSize) {
201 static constexpr int kApkSigBlockMinSize = 32;
202 static constexpr int kApkSigBlockFooterSize = 24;
203 static constexpr int64_t APK_SIG_BLOCK_MAGIC_HI = 0x3234206b636f6c42l;
204 static constexpr int64_t APK_SIG_BLOCK_MAGIC_LO = 0x20676953204b5041l;
205
206 off64_t cdOffset = CentralDirOffset(fd, fileSize);
207 if (cdOffset < 0) {
208 return -1;
209 }
210 // CD offset is where original signer block ends. Search backwards for magic and footer.
211 if (cdOffset < kApkSigBlockMinSize ||
212 valueAt<int64_t>(fd, cdOffset - 2 * sizeof(int64_t)) != APK_SIG_BLOCK_MAGIC_LO ||
213 valueAt<int64_t>(fd, cdOffset - sizeof(int64_t)) != APK_SIG_BLOCK_MAGIC_HI) {
214 return -1;
215 }
216 int32_t signerSizeInFooter = valueAt<int32_t>(fd, cdOffset - kApkSigBlockFooterSize);
217 off64_t signerBlockOffset = cdOffset - signerSizeInFooter - sizeof(int64_t);
218 if (signerBlockOffset < 0) {
219 return -1;
220 }
221 int32_t signerSizeInHeader = valueAt<int32_t>(fd, signerBlockOffset);
222 if (signerSizeInFooter != signerSizeInHeader) {
223 return -1;
224 }
225
226 return signerBlockOffset;
227 }
228
ZipPriorityBlocks(off64_t signerBlockOffset,Size fileSize)229 static std::vector<int32_t> ZipPriorityBlocks(off64_t signerBlockOffset, Size fileSize) {
230 int32_t signerBlockIndex = offsetToBlockIndex(signerBlockOffset);
231 int32_t lastBlockIndex = offsetToBlockIndex(fileSize);
232 const auto numPriorityBlocks = lastBlockIndex - signerBlockIndex + 1;
233
234 std::vector<int32_t> zipPriorityBlocks;
235
236 // Some magic here: most of zip libraries perform a scan for EOCD record starting at the offset
237 // of a maximum comment size from the end of the file. This means the last 65-ish KBs will be
238 // accessed first, followed by the rest of the central directory blocks. Make sure we
239 // send the data in the proper order, as central directory can be quite big by itself.
240 static constexpr auto kMaxZipCommentSize = 64 * 1024;
241 static constexpr auto kNumBlocksInEocdSearch = kMaxZipCommentSize / kBlockSize + 1;
242 if (numPriorityBlocks > kNumBlocksInEocdSearch) {
243 appendBlocks(lastBlockIndex - kNumBlocksInEocdSearch + 1, kNumBlocksInEocdSearch,
244 &zipPriorityBlocks);
245 appendBlocks(signerBlockIndex, numPriorityBlocks - kNumBlocksInEocdSearch,
246 &zipPriorityBlocks);
247 } else {
248 appendBlocks(signerBlockIndex, numPriorityBlocks, &zipPriorityBlocks);
249 }
250
251 // Somehow someone keeps accessing the start of the archive, even if there's nothing really
252 // interesting there...
253 appendBlocks(0, 1, &zipPriorityBlocks);
254 return zipPriorityBlocks;
255 }
256
openZipArchiveFd(borrowed_fd fd)257 [[maybe_unused]] static ZipArchiveHandle openZipArchiveFd(borrowed_fd fd) {
258 bool transferFdOwnership = false;
259 #ifdef _WIN32
260 //
261 // Need to create a special CRT FD here as the current one is not compatible with
262 // normal read()/write() calls that libziparchive uses.
263 // To make this work we have to create a copy of the file handle, as CRT doesn't care
264 // and closes it together with the new descriptor.
265 //
266 // Note: don't move this into a helper function, it's better to be hard to reuse because
267 // the code is ugly and won't work unless it's a last resort.
268 //
269 auto handle = adb_get_os_handle(fd);
270 HANDLE dupedHandle;
271 if (!::DuplicateHandle(::GetCurrentProcess(), handle, ::GetCurrentProcess(), &dupedHandle, 0,
272 false, DUPLICATE_SAME_ACCESS)) {
273 D("%s failed at DuplicateHandle: %d", __func__, (int)::GetLastError());
274 return {};
275 }
276 int osfd = _open_osfhandle((intptr_t)dupedHandle, _O_RDONLY | _O_BINARY);
277 if (osfd < 0) {
278 D("%s failed at _open_osfhandle: %d", __func__, errno);
279 ::CloseHandle(handle);
280 return {};
281 }
282 transferFdOwnership = true;
283 #else
284 int osfd = fd.get();
285 #endif
286 ZipArchiveHandle zip;
287 if (OpenArchiveFd(osfd, "apk_fd", &zip, transferFdOwnership) != 0) {
288 D("%s failed at OpenArchiveFd: %d", __func__, errno);
289 #ifdef _WIN32
290 // "_close()" is a secret WinCRT name for the regular close() function.
291 _close(osfd);
292 #endif
293 return {};
294 }
295 return zip;
296 }
297
openZipArchive(borrowed_fd fd,Size fileSize)298 static std::pair<ZipArchiveHandle, std::unique_ptr<android::base::MappedFile>> openZipArchive(
299 borrowed_fd fd, Size fileSize) {
300 #ifndef __LP64__
301 if (fileSize >= INT_MAX) {
302 return {openZipArchiveFd(fd), nullptr};
303 }
304 #endif
305 auto mapping =
306 android::base::MappedFile::FromOsHandle(adb_get_os_handle(fd), 0, fileSize, PROT_READ);
307 if (!mapping) {
308 D("%s failed at FromOsHandle: %d", __func__, errno);
309 return {};
310 }
311 ZipArchiveHandle zip;
312 if (OpenArchiveFromMemory(mapping->data(), mapping->size(), "apk_mapping", &zip) != 0) {
313 D("%s failed at OpenArchiveFromMemory: %d", __func__, errno);
314 return {};
315 }
316 return {zip, std::move(mapping)};
317 }
318
InstallationPriorityBlocks(borrowed_fd fd,Size fileSize)319 static std::vector<int32_t> InstallationPriorityBlocks(borrowed_fd fd, Size fileSize) {
320 static constexpr std::array<std::string_view, 3> additional_matches = {
321 "resources.arsc"sv, "AndroidManifest.xml"sv, "classes.dex"sv};
322 auto [zip, _] = openZipArchive(fd, fileSize);
323 if (!zip) {
324 return {};
325 }
326
327 auto matcher = [](std::string_view entry_name) {
328 if (entry_name.starts_with("lib/"sv) && entry_name.ends_with(".so"sv)) {
329 return true;
330 }
331 return std::any_of(additional_matches.begin(), additional_matches.end(),
332 [entry_name](std::string_view i) { return i == entry_name; });
333 };
334
335 void* cookie = nullptr;
336 if (StartIteration(zip, &cookie, std::move(matcher)) != 0) {
337 D("%s failed at StartIteration: %d", __func__, errno);
338 return {};
339 }
340
341 std::vector<int32_t> installationPriorityBlocks;
342 ZipEntry64 entry;
343 std::string_view entryName;
344 while (Next(cookie, &entry, &entryName) == 0) {
345 if (entryName == "classes.dex"sv) {
346 // Only the head is needed for installation
347 int32_t startBlockIndex = offsetToBlockIndex(entry.offset);
348 appendBlocks(startBlockIndex, 2, &installationPriorityBlocks);
349 D("\tadding to priority blocks: '%.*s' (%d)", (int)entryName.size(), entryName.data(),
350 2);
351 } else {
352 // Full entries are needed for installation
353 off64_t entryStartOffset = entry.offset;
354 off64_t entryEndOffset =
355 entryStartOffset +
356 (entry.method == kCompressStored ? entry.uncompressed_length
357 : entry.compressed_length) +
358 (entry.has_data_descriptor ? 16 /* sizeof(DataDescriptor) */ : 0);
359 int32_t startBlockIndex = offsetToBlockIndex(entryStartOffset);
360 int32_t endBlockIndex = offsetToBlockIndex(entryEndOffset);
361 int32_t numNewBlocks = endBlockIndex - startBlockIndex + 1;
362 appendBlocks(startBlockIndex, numNewBlocks, &installationPriorityBlocks);
363 D("\tadding to priority blocks: '%.*s' (%d)", (int)entryName.size(), entryName.data(),
364 numNewBlocks);
365 }
366 }
367
368 EndIteration(cookie);
369 CloseArchive(zip);
370 return installationPriorityBlocks;
371 }
372
PriorityBlocksForFile(const std::string & filepath,borrowed_fd fd,Size fileSize)373 std::vector<int32_t> PriorityBlocksForFile(const std::string& filepath, borrowed_fd fd,
374 Size fileSize) {
375 if (!android::base::EndsWithIgnoreCase(filepath, ".apk"sv)) {
376 return {};
377 }
378 off64_t signerOffset = SignerBlockOffset(fd, fileSize);
379 if (signerOffset < 0) {
380 // No signer block? not a valid APK
381 return {};
382 }
383 std::vector<int32_t> priorityBlocks = ZipPriorityBlocks(signerOffset, fileSize);
384 std::vector<int32_t> installationPriorityBlocks = InstallationPriorityBlocks(fd, fileSize);
385
386 priorityBlocks.insert(priorityBlocks.end(), installationPriorityBlocks.begin(),
387 installationPriorityBlocks.end());
388 unduplicate(priorityBlocks);
389 return priorityBlocks;
390 }
391
392 } // namespace incremental
393