Merge "[hardening] Use read() instead of mmap() for incfs files" into sc-dev

This commit is contained in:
Yurii Zubrytskyi
2021-03-12 08:07:38 +00:00
committed by Android (Google) Code Review
8 changed files with 127 additions and 7 deletions

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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.
*/

View 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);
}
}
}

View File

@@ -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": [

View File

@@ -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);

View File

@@ -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}};