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