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