From 005c7caa611fa15f04a8d1968d3a8aa38f3f4730 Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Fri, 11 Mar 2016 14:45:19 -0800 Subject: [PATCH] Workaround for verifying large APKs. When an APK is verifier during installation, the recently added APK Signature Scheme v2 code uncondionally memory-maps the whole file. This fails for very large APKs, even those which are not signed with APK Signature Scheme, thus preventing installation of such APKs. This temporary workaround pretends that the APK is not signed with APK Signature Scheme v2 if the APK cannot be memory-mapped because there's insufficient memory. This workaround will be removed soon, once APK Signature Scheme v2 APK verification logic can handle very large APKs. Bug: 27613575 Change-Id: I27bad534855fe4bf3e09b1087398ffdd7f98f482 --- .../apk/ApkSignatureSchemeV2Verifier.java | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java index 60c727037379f..dcf987bc3c129 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java @@ -84,8 +84,19 @@ public class ApkSignatureSchemeV2Verifier { if (fileSize > Integer.MAX_VALUE) { return false; } - MappedByteBuffer apkContents = - apk.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize); + MappedByteBuffer apkContents; + try { + apkContents = apk.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize); + } catch (IOException e) { + if (e.getCause() instanceof OutOfMemoryError) { + // TODO: Remove this temporary workaround once verifying large APKs is + // supported. Very large APKs cannot be memory-mapped. This verification code + // needs to change to use a different approach for verifying such APKs. + return false; // Pretend that this APK does not have a v2 signature. + } else { + throw new IOException("Failed to memory-map APK", e); + } + } // ZipUtils and APK Signature Scheme v2 verifier expect little-endian byte order. apkContents.order(ByteOrder.LITTLE_ENDIAN); @@ -134,11 +145,26 @@ public class ApkSignatureSchemeV2Verifier { if (fileSize > Integer.MAX_VALUE) { throw new IOException("File too large: " + apk.length() + " bytes"); } - MappedByteBuffer apkContents = - apk.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize); - // Attempt to preload the contents into memory for faster overall verification (v2 and - // older) at the expense of somewhat increased latency for rejecting malformed APKs. - apkContents.load(); + MappedByteBuffer apkContents; + try { + apkContents = apk.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, fileSize); + // Attempt to preload the contents into memory for faster overall verification (v2 and + // older) at the expense of somewhat increased latency for rejecting malformed APKs. + apkContents.load(); + } catch (IOException e) { + if (e.getCause() instanceof OutOfMemoryError) { + // TODO: Remove this temporary workaround once verifying large APKs is supported. + // Very large APKs cannot be memory-mapped. This verification code needs to change + // to use a different approach for verifying such APKs. + // This workaround pretends that this APK does not have a v2 signature. This works + // fine provided the APK is not actually v2-signed. If the APK is v2 signed, v2 + // signature stripping protection inside v1 signature verification code will reject + // this APK. + throw new SignatureNotFoundException("Failed to memory-map APK", e); + } else { + throw new IOException("Failed to memory-map APK", e); + } + } return verify(apkContents); }