Merge "[hardening] Use read() instead of mmap() for incfs files" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e32f0d1c01
@@ -30,6 +30,7 @@ import android.util.SparseArray;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@@ -264,6 +265,13 @@ public final class IncrementalManager {
|
||||
return nativeIsIncrementalPath(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an fd corresponds to a file on a mounted Incremental File System.
|
||||
*/
|
||||
public static boolean isIncrementalFileFd(@NonNull FileDescriptor fd) {
|
||||
return nativeIsIncrementalFd(fd.getInt$());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns raw signature for file if it's on Incremental File System.
|
||||
* Unsafe, use only if you are sure what you are doing.
|
||||
@@ -437,5 +445,6 @@ public final class IncrementalManager {
|
||||
private static native boolean nativeIsEnabled();
|
||||
private static native boolean nativeIsV2Available();
|
||||
private static native boolean nativeIsIncrementalPath(@NonNull String path);
|
||||
private static native boolean nativeIsIncrementalFd(@NonNull int fd);
|
||||
private static native byte[] nativeUnsafeGetFileSignature(@NonNull String path);
|
||||
}
|
||||
|
||||
@@ -200,10 +200,9 @@ public final class ApkSigningBlockUtils {
|
||||
// physical memory.
|
||||
|
||||
DataSource beforeApkSigningBlock =
|
||||
new MemoryMappedFileDataSource(apkFileDescriptor, 0,
|
||||
signatureInfo.apkSigningBlockOffset);
|
||||
DataSource.create(apkFileDescriptor, 0, signatureInfo.apkSigningBlockOffset);
|
||||
DataSource centralDir =
|
||||
new MemoryMappedFileDataSource(
|
||||
DataSource.create(
|
||||
apkFileDescriptor, signatureInfo.centralDirOffset,
|
||||
signatureInfo.eocdOffset - signatureInfo.centralDirOffset);
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
package android.util.apk;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.os.incremental.IncrementalManager;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.security.DigestException;
|
||||
|
||||
@@ -35,4 +39,22 @@ interface DataSource {
|
||||
*/
|
||||
void feedIntoDataDigester(DataDigester md, long offset, int size)
|
||||
throws IOException, DigestException;
|
||||
|
||||
/**
|
||||
* Creates a DataSource that can handle the passed fd in the most efficient and safe manner.
|
||||
* @param fd file descriptor to read from
|
||||
* @param pos starting offset
|
||||
* @param size size of the region
|
||||
* @return created DataSource object
|
||||
*/
|
||||
static @NonNull DataSource create(@NonNull FileDescriptor fd, long pos, long size) {
|
||||
if (IncrementalManager.isIncrementalFileFd(fd)) {
|
||||
// IncFS-based files may have missing pages, and reading those via mmap() results
|
||||
// in a SIGBUS signal. Java doesn't have a good way of catching it, ending up killing
|
||||
// the process by default. Going back to read() is the safest option for these files.
|
||||
return new ReadFileDataSource(fd, pos, size);
|
||||
} else {
|
||||
return new MemoryMappedFileDataSource(fd, pos, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ class MemoryMappedFileDataSource implements DataSource {
|
||||
/**
|
||||
* Constructs a new {@code MemoryMappedFileDataSource} for the specified region of the file.
|
||||
*
|
||||
* @param fd file descriptor to read from.
|
||||
* @param position start position of the region in the file.
|
||||
* @param size size (in bytes) of the region.
|
||||
*/
|
||||
|
||||
73
core/java/android/util/apk/ReadFileDataSource.java
Normal file
73
core/java/android/util/apk/ReadFileDataSource.java
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.util.apk;
|
||||
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.DigestException;
|
||||
|
||||
/**
|
||||
* {@link DataSource} which provides data from a file descriptor by reading the sections
|
||||
* of the file via raw read() syscall. This is slower than memory-mapping but safer.
|
||||
*/
|
||||
class ReadFileDataSource implements DataSource {
|
||||
private final FileDescriptor mFd;
|
||||
private final long mFilePosition;
|
||||
private final long mSize;
|
||||
|
||||
private static final int CHUNK_SIZE = 1024 * 1024;
|
||||
|
||||
/**
|
||||
* Constructs a new {@code ReadFileDataSource} for the specified region of the file.
|
||||
*
|
||||
* @param fd file descriptor to read from.
|
||||
* @param position start position of the region in the file.
|
||||
* @param size size (in bytes) of the region.
|
||||
*/
|
||||
ReadFileDataSource(FileDescriptor fd, long position, long size) {
|
||||
mFd = fd;
|
||||
mFilePosition = position;
|
||||
mSize = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size() {
|
||||
return mSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void feedIntoDataDigester(DataDigester md, long offset, int size)
|
||||
throws IOException, DigestException {
|
||||
try {
|
||||
final byte[] buffer = new byte[Math.min(size, CHUNK_SIZE)];
|
||||
final long start = mFilePosition + offset;
|
||||
final long end = start + size;
|
||||
for (long pos = start, curSize = Math.min(size, CHUNK_SIZE);
|
||||
pos < end; curSize = Math.min(end - pos, CHUNK_SIZE)) {
|
||||
final int readSize = Os.pread(mFd, buffer, 0, (int) curSize, pos);
|
||||
md.consume(ByteBuffer.wrap(buffer, 0, readSize));
|
||||
pos += readSize;
|
||||
}
|
||||
} catch (ErrnoException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,16 @@
|
||||
{
|
||||
"presubmit": [
|
||||
{
|
||||
"name": "CtsContentTestCases",
|
||||
"options": [
|
||||
{
|
||||
"include-filter": "android.content.pm.cts.PackageManagerShellCommandIncrementalTest"
|
||||
},
|
||||
{
|
||||
"include-filter": "android.content.pm.cts.PackageManagerShellCommandTest"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "FrameworksCoreTests",
|
||||
"options": [
|
||||
|
||||
@@ -294,7 +294,7 @@ public abstract class VerityBuilder {
|
||||
|
||||
// 1. Digest the whole file by chunks.
|
||||
consumeByChunk(digester,
|
||||
new MemoryMappedFileDataSource(file.getFD(), 0, file.length()),
|
||||
DataSource.create(file.getFD(), 0, file.length()),
|
||||
MMAP_REGION_SIZE_BYTES);
|
||||
|
||||
// 2. Pad 0s up to the nearest 4096-byte block before hashing.
|
||||
@@ -315,7 +315,7 @@ public abstract class VerityBuilder {
|
||||
|
||||
// 1. Digest from the beginning of the file, until APK Signing Block is reached.
|
||||
consumeByChunk(digester,
|
||||
new MemoryMappedFileDataSource(apk.getFD(), 0, signatureInfo.apkSigningBlockOffset),
|
||||
DataSource.create(apk.getFD(), 0, signatureInfo.apkSigningBlockOffset),
|
||||
MMAP_REGION_SIZE_BYTES);
|
||||
|
||||
// 2. Skip APK Signing Block and continue digesting, until the Central Directory offset
|
||||
@@ -323,7 +323,7 @@ public abstract class VerityBuilder {
|
||||
long eocdCdOffsetFieldPosition =
|
||||
signatureInfo.eocdOffset + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET;
|
||||
consumeByChunk(digester,
|
||||
new MemoryMappedFileDataSource(apk.getFD(), signatureInfo.centralDirOffset,
|
||||
DataSource.create(apk.getFD(), signatureInfo.centralDirOffset,
|
||||
eocdCdOffsetFieldPosition - signatureInfo.centralDirOffset),
|
||||
MMAP_REGION_SIZE_BYTES);
|
||||
|
||||
@@ -338,7 +338,7 @@ public abstract class VerityBuilder {
|
||||
long offsetAfterEocdCdOffsetField =
|
||||
eocdCdOffsetFieldPosition + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE;
|
||||
consumeByChunk(digester,
|
||||
new MemoryMappedFileDataSource(apk.getFD(), offsetAfterEocdCdOffsetField,
|
||||
DataSource.create(apk.getFD(), offsetAfterEocdCdOffsetField,
|
||||
apk.length() - offsetAfterEocdCdOffsetField),
|
||||
MMAP_REGION_SIZE_BYTES);
|
||||
|
||||
|
||||
@@ -41,6 +41,10 @@ static jboolean nativeIsIncrementalPath(JNIEnv* env,
|
||||
return (jboolean)IncFs_IsIncFsPath(path.c_str());
|
||||
}
|
||||
|
||||
static jboolean nativeIsIncrementalFd(JNIEnv* env, jobject clazz, jint fd) {
|
||||
return (jboolean)IncFs_IsIncFsFd(fd);
|
||||
}
|
||||
|
||||
static jbyteArray nativeUnsafeGetFileSignature(JNIEnv* env, jobject clazz, jstring javaPath) {
|
||||
ScopedUtfChars path(env, javaPath);
|
||||
|
||||
@@ -61,6 +65,7 @@ static const JNINativeMethod method_table[] =
|
||||
{{"nativeIsEnabled", "()Z", (void*)nativeIsEnabled},
|
||||
{"nativeIsV2Available", "()Z", (void*)nativeIsV2Available},
|
||||
{"nativeIsIncrementalPath", "(Ljava/lang/String;)Z", (void*)nativeIsIncrementalPath},
|
||||
{"nativeIsIncrementalFd", "(I)Z", (void*)nativeIsIncrementalFd},
|
||||
{"nativeUnsafeGetFileSignature", "(Ljava/lang/String;)[B",
|
||||
(void*)nativeUnsafeGetFileSignature}};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user