From 4045ae78710b4cdf9a7c1e85cdbecd6a7a8ab7ae Mon Sep 17 00:00:00 2001 From: Tomasz Wasilczyk Date: Wed, 13 Mar 2024 10:21:03 -0700 Subject: [PATCH] Remove SignApk output limitation of 2GiB Bug: 302112430 Test: build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=aosp_x86_64 TARGET_RELEASE=trunk_staging CLANG_COVERAGE=true NATIVE_COVERAGE_PATHS="art external/aac external/boringssl external/conscrypt external/cpu_features external/dlmalloc external/fdlibm external/libaom external/libavc external/libcxx external/libfuse external/libgav1 external/libgsm external/libhevc external/libmpeg2 external/libopus external/libtextclassifier external/libvpx external/libxaac external/oj-libjdwp external/sonivox external/speex external/tremolo external/vixl frameworks/av frameworks/base/media libcore libnativehelper packages/modules/Bluetooth packages/modules/Connectivity packages/modules/DnsResolver packages/modules/ExtServices packages/modules/NetworkStack packages/modules/NeuralNetworks packages/modules/SdkExtensions packages/modules/StatsD packages/providers/MediaProvider system/core/base system/core/libcutils system/core/libstats system/media/audio_utils system/netd/resolv" com.android.art.testing apksigner verify --in $OUT/../generic_x86_64/system/apex/com.android.art.testing.apex --print-certs -v --min-sdk-version 23 Change-Id: Id31eae0f221dd7ef333bd12aeef591463e1c931a --- .../src/com/android/signapk/SignApk.java | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/tools/signapk/src/com/android/signapk/SignApk.java b/tools/signapk/src/com/android/signapk/SignApk.java index 6a9a5d35f0..6b2341bc80 100644 --- a/tools/signapk/src/com/android/signapk/SignApk.java +++ b/tools/signapk/src/com/android/signapk/SignApk.java @@ -987,6 +987,9 @@ class SignApk { private static class ZipSections { DataSource beforeCentralDir; + + // The following fields are still valid after closing the backing DataSource. + long beforeCentralDirSize; ByteBuffer centralDir; ByteBuffer eocd; } @@ -1006,7 +1009,9 @@ class SignApk { } ZipSections result = new ZipSections(); + result.beforeCentralDir = apk.slice(0, centralDirStartOffset); + result.beforeCentralDirSize = result.beforeCentralDir.size(); long centralDirSize = centralDirEndOffset - centralDirStartOffset; if (centralDirSize >= Integer.MAX_VALUE) throw new IndexOutOfBoundsException(); @@ -1270,11 +1275,8 @@ class SignApk { // signatures) apkSigner.inputApkSigningBlock(null); - // Build the output APK in memory, by copying input APK's ZIP entries across - // and then signing the output APK. - ByteArrayOutputStream v1SignedApkBuf = new ByteArrayOutputStream(); CountingOutputStream outputJarCounter = - new CountingOutputStream(v1SignedApkBuf); + new CountingOutputStream(outputFile); JarOutputStream outputJar = new JarOutputStream(outputJarCounter); // Use maximum compression for compressed entries because the APK lives forever // on the system partition. @@ -1287,10 +1289,13 @@ class SignApk { addV1Signature(apkSigner, addV1SignatureRequest, outputJar, timestamp); addV1SignatureRequest.done(); } + + // close output and switch to input mode outputJar.close(); - ByteBuffer v1SignedApk = ByteBuffer.wrap(v1SignedApkBuf.toByteArray()); - v1SignedApkBuf.reset(); - ByteBuffer[] outputChunks = new ByteBuffer[] {v1SignedApk}; + outputJar = null; + outputJarCounter = null; + outputFile = null; + RandomAccessFile v1SignedApk = new RandomAccessFile(outputFilename, "r"); ZipSections zipSections = findMainZipSections(DataSources.asDataSource( v1SignedApk)); @@ -1299,6 +1304,9 @@ class SignApk { eocd.put(zipSections.eocd); eocd.flip(); eocd.order(ByteOrder.LITTLE_ENDIAN); + + ByteBuffer[] outputChunks = new ByteBuffer[] {}; + // This loop is supposed to be iterated twice at most. // The second pass is to align the file size after amending EOCD comments // with assumption that re-generated signing block would be the same size. @@ -1325,13 +1333,8 @@ class SignApk { modifiedEocd, zipSections.beforeCentralDir.size() + padding + apkSigningBlock.length); - if (zipSections.beforeCentralDir.size() >= Integer.MAX_VALUE) { - throw new IndexOutOfBoundsException(); - } outputChunks = new ByteBuffer[] { - zipSections.beforeCentralDir.getByteBuffer(0, - (int)zipSections.beforeCentralDir.size()), ByteBuffer.allocate(padding), ByteBuffer.wrap(apkSigningBlock), zipSections.centralDir, @@ -1345,7 +1348,7 @@ class SignApk { // Calculate the file size eocd = modifiedEocd; - int fileSize = 0; + long fileSize = zipSections.beforeCentralDirSize; for (ByteBuffer buf : outputChunks) { fileSize += buf.remaining(); } @@ -1354,7 +1357,7 @@ class SignApk { break; } // Pad EOCD comment to align the file size. - int commentLen = alignment - fileSize % alignment; + int commentLen = alignment - (int)(fileSize % alignment); modifiedEocd = ByteBuffer.allocate(eocd.remaining() + commentLen); modifiedEocd.put(eocd); modifiedEocd.rewind(); @@ -1365,6 +1368,12 @@ class SignApk { eocd = modifiedEocd; } + // close input and switch back to output mode + v1SignedApk.close(); + v1SignedApk = null; + outputFile = new FileOutputStream(outputFilename, true); + outputFile.getChannel().truncate(zipSections.beforeCentralDirSize); + // This assumes outputChunks are array-backed. To avoid this assumption, the // code could be rewritten to use FileChannel. for (ByteBuffer outputChunk : outputChunks) {