Loading core/jni/com_android_internal_content_FileSystemUtils.cpp +114 −3 Original line number Diff line number Diff line Loading @@ -42,7 +42,11 @@ namespace android { bool punchHoles(const char *filePath, const uint64_t offset, const std::vector<Elf64_Phdr> &programHeaders) { struct stat64 beforePunch; lstat64(filePath, &beforePunch); if (int result = lstat64(filePath, &beforePunch); result != 0) { ALOGE("lstat64 failed for filePath %s, error:%d", filePath, errno); return false; } uint64_t blockSize = beforePunch.st_blksize; IF_ALOGD() { ALOGD("Total number of LOAD segments %zu", programHeaders.size()); Loading Loading @@ -152,7 +156,10 @@ bool punchHoles(const char *filePath, const uint64_t offset, IF_ALOGD() { struct stat64 afterPunch; lstat64(filePath, &afterPunch); if (int result = lstat64(filePath, &afterPunch); result != 0) { ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno); return false; } ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %ld, st_size: %" PRIu64 "", afterPunch.st_blocks, afterPunch.st_blksize, Loading @@ -177,7 +184,7 @@ bool punchHolesInElf64(const char *filePath, const uint64_t offset) { // only consider elf64 for punching holes if (ehdr.e_ident[EI_CLASS] != ELFCLASS64) { ALOGE("Provided file is not ELF64"); ALOGW("Provided file is not ELF64"); return false; } Loading Loading @@ -215,4 +222,108 @@ bool punchHolesInElf64(const char *filePath, const uint64_t offset) { return punchHoles(filePath, offset, programHeaders); } bool punchHolesInZip(const char *filePath, uint64_t offset, uint16_t extraFieldLen) { android::base::unique_fd fd(open(filePath, O_RDWR | O_CLOEXEC)); if (!fd.ok()) { ALOGE("Can't open file to punch %s", filePath); return false; } struct stat64 beforePunch; if (int result = lstat64(filePath, &beforePunch); result != 0) { ALOGE("lstat64 failed for filePath %s, error:%d", filePath, errno); return false; } uint64_t blockSize = beforePunch.st_blksize; IF_ALOGD() { ALOGD("Extra field length: %hu, Size before punching holes st_blocks: %" PRIu64 ", st_blksize: %ld, st_size: %" PRIu64 "", extraFieldLen, beforePunch.st_blocks, beforePunch.st_blksize, static_cast<uint64_t>(beforePunch.st_size)); } if (extraFieldLen < blockSize) { ALOGD("Skipping punching apk as extra field length is less than block size"); return false; } // content is preceded by extra field. Zip offset is offset of exact content. // move back by extraFieldLen so that scan can be started at start of extra field. uint64_t extraFieldStart; if (__builtin_sub_overflow(offset, extraFieldLen, &extraFieldStart)) { ALOGE("Overflow occurred when calculating start of extra field"); return false; } constexpr uint64_t kMaxSize = 64 * 1024; // Use malloc to gracefully handle any oom conditions std::unique_ptr<uint8_t, decltype(&free)> buffer(static_cast<uint8_t *>(malloc(kMaxSize)), &free); if (buffer == nullptr) { ALOGE("Failed to allocate read buffer"); return false; } // Read the entire extra fields at once and punch file according to zero stretches. if (!ReadFullyAtOffset(fd, buffer.get(), extraFieldLen, extraFieldStart)) { ALOGE("Failed to read extra field content"); return false; } IF_ALOGD() { ALOGD("Extra field length: %hu content near offset: %s", extraFieldLen, HexString(buffer.get(), extraFieldLen).c_str()); } uint64_t currentSize = 0; while (currentSize < extraFieldLen) { uint64_t end = currentSize; // find zero ranges while (end < extraFieldLen && *(buffer.get() + end) == 0) { ++end; } uint64_t punchLen; if (__builtin_sub_overflow(end, currentSize, &punchLen)) { ALOGW("Overflow occurred when calculating punching length"); return false; } // Don't punch for every stretch of zero which is found if (punchLen > blockSize) { uint64_t punchOffset; if (__builtin_add_overflow(extraFieldStart, currentSize, &punchOffset)) { ALOGW("Overflow occurred when calculating punch start offset"); return false; } ALOGD("Punching hole in apk start: %" PRIu64 " len:%" PRIu64 "", punchOffset, punchLen); // Punch hole for this entire stretch. int result = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, punchOffset, punchLen); if (result < 0) { ALOGE("fallocate failed to punch hole inside apk, error:%d", errno); return false; } } currentSize = end; ++currentSize; } IF_ALOGD() { struct stat64 afterPunch; if (int result = lstat64(filePath, &afterPunch); result != 0) { ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno); return false; } ALOGD("punchHolesInApk:: Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %ld, st_size: %" PRIu64 "", afterPunch.st_blocks, afterPunch.st_blksize, static_cast<uint64_t>(afterPunch.st_size)); } return true; } }; // namespace android core/jni/com_android_internal_content_FileSystemUtils.h +7 −0 Original line number Diff line number Diff line Loading @@ -28,4 +28,11 @@ namespace android { */ bool punchHolesInElf64(const char* filePath, uint64_t offset); /* * This function punches holes in zero segments of Apk file which are introduced during the * alignment. Alignment tools add padding inside of extra field in local file header. punch holes in * extra field for zero stretches till the actual file content. */ bool punchHolesInZip(const char* filePath, uint64_t offset, uint16_t extraFieldLen); } // namespace android No newline at end of file core/jni/com_android_internal_content_NativeLibraryHelper.cpp +29 −2 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/statfs.h> #include <sys/types.h> #include <time.h> #include <unistd.h> Loading Loading @@ -145,8 +146,9 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr uint16_t method; off64_t offset; if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc)) { uint16_t extraFieldLength; if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc, &extraFieldLength)) { ALOGE("Couldn't read zip entry info\n"); return INSTALL_FAILED_INVALID_APK; } Loading Loading @@ -177,6 +179,12 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr "%" PRIu64 "", fileName, zipFile->getZipFileName(), offset); } // if extra field for this zip file is present with some length, possibility is that it is // padding added for zip alignment. Punch holes there too. if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) { ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName()); } #endif // ENABLE_PUNCH_HOLES return INSTALL_SUCCEEDED; Loading Loading @@ -279,6 +287,25 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr return INSTALL_FAILED_CONTAINER_ERROR; } #ifdef ENABLE_PUNCH_HOLES // punch extracted elf files as well. This will fail where compression is on (like f2fs) but it // will be useful for ext4 based systems struct statfs64 fsInfo; int result = statfs64(localFileName, &fsInfo); if (result < 0) { ALOGW("Failed to stat file :%s", localFileName); } if (result == 0 && fsInfo.f_type == EXT4_SUPER_MAGIC) { ALOGD("Punching extracted elf file %s on fs:%" PRIu64 "", fileName, static_cast<uint64_t>(fsInfo.f_type)); if (!punchHolesInElf64(localFileName, 0)) { ALOGW("Failed to punch extracted elf file :%s from apk : %s", fileName, zipFile->getZipFileName()); } } #endif // ENABLE_PUNCH_HOLES ALOGV("Successfully moved %s to %s\n", localTmpFileName, localFileName); return INSTALL_SUCCEEDED; Loading libs/androidfw/ZipFileRO.cpp +18 −7 Original line number Diff line number Diff line Loading @@ -121,28 +121,39 @@ ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod, uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset, uint32_t* pModWhen, uint32_t* pCrc32) const { return getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, pModWhen, pCrc32, nullptr); } bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod, uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset, uint32_t* pModWhen, uint32_t* pCrc32, uint16_t* pExtraFieldSize) const { const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); const ZipEntry& ze = zipEntry->entry; if (pMethod != NULL) { if (pMethod != nullptr) { *pMethod = ze.method; } if (pUncompLen != NULL) { if (pUncompLen != nullptr) { *pUncompLen = ze.uncompressed_length; } if (pCompLen != NULL) { if (pCompLen != nullptr) { *pCompLen = ze.compressed_length; } if (pOffset != NULL) { if (pOffset != nullptr) { *pOffset = ze.offset; } if (pModWhen != NULL) { if (pModWhen != nullptr) { *pModWhen = ze.mod_time; } if (pCrc32 != NULL) { if (pCrc32 != nullptr) { *pCrc32 = ze.crc32; } if (pExtraFieldSize != nullptr) { *pExtraFieldSize = ze.extra_field_size; } return true; } Loading libs/androidfw/include/androidfw/ZipFileRO.h +4 −0 Original line number Diff line number Diff line Loading @@ -151,6 +151,10 @@ public: uint32_t* pCompLen, off64_t* pOffset, uint32_t* pModWhen, uint32_t* pCrc32) const; bool getEntryInfo(ZipEntryRO entry, uint16_t* pMethod, uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset, uint32_t* pModWhen, uint32_t* pCrc32, uint16_t* pExtraFieldSize) const; /* * Create a new FileMap object that maps a subset of the archive. For * an uncompressed entry this effectively provides a pointer to the Loading Loading
core/jni/com_android_internal_content_FileSystemUtils.cpp +114 −3 Original line number Diff line number Diff line Loading @@ -42,7 +42,11 @@ namespace android { bool punchHoles(const char *filePath, const uint64_t offset, const std::vector<Elf64_Phdr> &programHeaders) { struct stat64 beforePunch; lstat64(filePath, &beforePunch); if (int result = lstat64(filePath, &beforePunch); result != 0) { ALOGE("lstat64 failed for filePath %s, error:%d", filePath, errno); return false; } uint64_t blockSize = beforePunch.st_blksize; IF_ALOGD() { ALOGD("Total number of LOAD segments %zu", programHeaders.size()); Loading Loading @@ -152,7 +156,10 @@ bool punchHoles(const char *filePath, const uint64_t offset, IF_ALOGD() { struct stat64 afterPunch; lstat64(filePath, &afterPunch); if (int result = lstat64(filePath, &afterPunch); result != 0) { ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno); return false; } ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %ld, st_size: %" PRIu64 "", afterPunch.st_blocks, afterPunch.st_blksize, Loading @@ -177,7 +184,7 @@ bool punchHolesInElf64(const char *filePath, const uint64_t offset) { // only consider elf64 for punching holes if (ehdr.e_ident[EI_CLASS] != ELFCLASS64) { ALOGE("Provided file is not ELF64"); ALOGW("Provided file is not ELF64"); return false; } Loading Loading @@ -215,4 +222,108 @@ bool punchHolesInElf64(const char *filePath, const uint64_t offset) { return punchHoles(filePath, offset, programHeaders); } bool punchHolesInZip(const char *filePath, uint64_t offset, uint16_t extraFieldLen) { android::base::unique_fd fd(open(filePath, O_RDWR | O_CLOEXEC)); if (!fd.ok()) { ALOGE("Can't open file to punch %s", filePath); return false; } struct stat64 beforePunch; if (int result = lstat64(filePath, &beforePunch); result != 0) { ALOGE("lstat64 failed for filePath %s, error:%d", filePath, errno); return false; } uint64_t blockSize = beforePunch.st_blksize; IF_ALOGD() { ALOGD("Extra field length: %hu, Size before punching holes st_blocks: %" PRIu64 ", st_blksize: %ld, st_size: %" PRIu64 "", extraFieldLen, beforePunch.st_blocks, beforePunch.st_blksize, static_cast<uint64_t>(beforePunch.st_size)); } if (extraFieldLen < blockSize) { ALOGD("Skipping punching apk as extra field length is less than block size"); return false; } // content is preceded by extra field. Zip offset is offset of exact content. // move back by extraFieldLen so that scan can be started at start of extra field. uint64_t extraFieldStart; if (__builtin_sub_overflow(offset, extraFieldLen, &extraFieldStart)) { ALOGE("Overflow occurred when calculating start of extra field"); return false; } constexpr uint64_t kMaxSize = 64 * 1024; // Use malloc to gracefully handle any oom conditions std::unique_ptr<uint8_t, decltype(&free)> buffer(static_cast<uint8_t *>(malloc(kMaxSize)), &free); if (buffer == nullptr) { ALOGE("Failed to allocate read buffer"); return false; } // Read the entire extra fields at once and punch file according to zero stretches. if (!ReadFullyAtOffset(fd, buffer.get(), extraFieldLen, extraFieldStart)) { ALOGE("Failed to read extra field content"); return false; } IF_ALOGD() { ALOGD("Extra field length: %hu content near offset: %s", extraFieldLen, HexString(buffer.get(), extraFieldLen).c_str()); } uint64_t currentSize = 0; while (currentSize < extraFieldLen) { uint64_t end = currentSize; // find zero ranges while (end < extraFieldLen && *(buffer.get() + end) == 0) { ++end; } uint64_t punchLen; if (__builtin_sub_overflow(end, currentSize, &punchLen)) { ALOGW("Overflow occurred when calculating punching length"); return false; } // Don't punch for every stretch of zero which is found if (punchLen > blockSize) { uint64_t punchOffset; if (__builtin_add_overflow(extraFieldStart, currentSize, &punchOffset)) { ALOGW("Overflow occurred when calculating punch start offset"); return false; } ALOGD("Punching hole in apk start: %" PRIu64 " len:%" PRIu64 "", punchOffset, punchLen); // Punch hole for this entire stretch. int result = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, punchOffset, punchLen); if (result < 0) { ALOGE("fallocate failed to punch hole inside apk, error:%d", errno); return false; } } currentSize = end; ++currentSize; } IF_ALOGD() { struct stat64 afterPunch; if (int result = lstat64(filePath, &afterPunch); result != 0) { ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno); return false; } ALOGD("punchHolesInApk:: Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %ld, st_size: %" PRIu64 "", afterPunch.st_blocks, afterPunch.st_blksize, static_cast<uint64_t>(afterPunch.st_size)); } return true; } }; // namespace android
core/jni/com_android_internal_content_FileSystemUtils.h +7 −0 Original line number Diff line number Diff line Loading @@ -28,4 +28,11 @@ namespace android { */ bool punchHolesInElf64(const char* filePath, uint64_t offset); /* * This function punches holes in zero segments of Apk file which are introduced during the * alignment. Alignment tools add padding inside of extra field in local file header. punch holes in * extra field for zero stretches till the actual file content. */ bool punchHolesInZip(const char* filePath, uint64_t offset, uint16_t extraFieldLen); } // namespace android No newline at end of file
core/jni/com_android_internal_content_NativeLibraryHelper.cpp +29 −2 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/statfs.h> #include <sys/types.h> #include <time.h> #include <unistd.h> Loading Loading @@ -145,8 +146,9 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr uint16_t method; off64_t offset; if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc)) { uint16_t extraFieldLength; if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc, &extraFieldLength)) { ALOGE("Couldn't read zip entry info\n"); return INSTALL_FAILED_INVALID_APK; } Loading Loading @@ -177,6 +179,12 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr "%" PRIu64 "", fileName, zipFile->getZipFileName(), offset); } // if extra field for this zip file is present with some length, possibility is that it is // padding added for zip alignment. Punch holes there too. if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) { ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName()); } #endif // ENABLE_PUNCH_HOLES return INSTALL_SUCCEEDED; Loading Loading @@ -279,6 +287,25 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr return INSTALL_FAILED_CONTAINER_ERROR; } #ifdef ENABLE_PUNCH_HOLES // punch extracted elf files as well. This will fail where compression is on (like f2fs) but it // will be useful for ext4 based systems struct statfs64 fsInfo; int result = statfs64(localFileName, &fsInfo); if (result < 0) { ALOGW("Failed to stat file :%s", localFileName); } if (result == 0 && fsInfo.f_type == EXT4_SUPER_MAGIC) { ALOGD("Punching extracted elf file %s on fs:%" PRIu64 "", fileName, static_cast<uint64_t>(fsInfo.f_type)); if (!punchHolesInElf64(localFileName, 0)) { ALOGW("Failed to punch extracted elf file :%s from apk : %s", fileName, zipFile->getZipFileName()); } } #endif // ENABLE_PUNCH_HOLES ALOGV("Successfully moved %s to %s\n", localTmpFileName, localFileName); return INSTALL_SUCCEEDED; Loading
libs/androidfw/ZipFileRO.cpp +18 −7 Original line number Diff line number Diff line Loading @@ -121,28 +121,39 @@ ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod, uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset, uint32_t* pModWhen, uint32_t* pCrc32) const { return getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, pModWhen, pCrc32, nullptr); } bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod, uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset, uint32_t* pModWhen, uint32_t* pCrc32, uint16_t* pExtraFieldSize) const { const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); const ZipEntry& ze = zipEntry->entry; if (pMethod != NULL) { if (pMethod != nullptr) { *pMethod = ze.method; } if (pUncompLen != NULL) { if (pUncompLen != nullptr) { *pUncompLen = ze.uncompressed_length; } if (pCompLen != NULL) { if (pCompLen != nullptr) { *pCompLen = ze.compressed_length; } if (pOffset != NULL) { if (pOffset != nullptr) { *pOffset = ze.offset; } if (pModWhen != NULL) { if (pModWhen != nullptr) { *pModWhen = ze.mod_time; } if (pCrc32 != NULL) { if (pCrc32 != nullptr) { *pCrc32 = ze.crc32; } if (pExtraFieldSize != nullptr) { *pExtraFieldSize = ze.extra_field_size; } return true; } Loading
libs/androidfw/include/androidfw/ZipFileRO.h +4 −0 Original line number Diff line number Diff line Loading @@ -151,6 +151,10 @@ public: uint32_t* pCompLen, off64_t* pOffset, uint32_t* pModWhen, uint32_t* pCrc32) const; bool getEntryInfo(ZipEntryRO entry, uint16_t* pMethod, uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset, uint32_t* pModWhen, uint32_t* pCrc32, uint16_t* pExtraFieldSize) const; /* * Create a new FileMap object that maps a subset of the archive. For * an uncompressed entry this effectively provides a pointer to the Loading