• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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