Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e015edab authored by Kohsuke Yatoh's avatar Kohsuke Yatoh Committed by Automerger Merge Worker
Browse files

Merge "Add test for font crash protection." into sc-dev am: f26c5a51

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13486913

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I8a0cc109fd12a43f5f3e14168aa91a3e0878e4b5
parents 13f4660e f26c5a51
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -16,7 +16,10 @@ java_test_host {
    name: "ApkVerityTest",
    srcs: ["src/**/*.java"],
    libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
    static_libs: ["frameworks-base-hostutils"],
    static_libs: [
        "block_device_writer_jar",
        "frameworks-base-hostutils",
    ],
    test_suites: ["general-tests", "vts"],
    target_required: [
        "block_device_writer_module",
+6 −0
Original line number Diff line number Diff line
@@ -51,3 +51,9 @@ cc_test {
    test_suites: ["general-tests", "pts", "vts"],
    gtest: false,
}

java_library_host {
    name: "block_device_writer_jar",
    srcs: ["src/**/*.java"],
    libs: ["tradefed", "junit"],
}
+100 −0
Original line number Diff line number Diff line
/*
 * 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 com.android.blockdevicewriter;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;

import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;

import java.util.ArrayList;

/**
 * Wrapper for block_device_writer command.
 *
 * <p>To use this class, please push block_device_writer binary to /data/local/tmp.
 * 1. In Android.bp, add:
 * <pre>
 *     target_required: ["block_device_writer_module"],
 * </pre>
 * 2. In AndroidText.xml, add:
 * <pre>
 *     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
 *         <option name="push" value="block_device_writer->/data/local/tmp/block_device_writer" />
 *     </target_preparer>
 * </pre>
 */
public final class BlockDeviceWriter {
    private static final String EXECUTABLE = "/data/local/tmp/block_device_writer";

    /**
     * Modifies a byte of the file directly against the backing block storage.
     *
     * The effect can only be observed when the page cache is read from disk again. See
     * {@link #dropCaches} for details.
     */
    public static void damageFileAgainstBlockDevice(ITestDevice device, String path,
            long offsetOfTargetingByte)
            throws DeviceNotAvailableException {
        assertThat(path).startsWith("/data/");
        ITestDevice.MountPointInfo mountPoint = device.getMountPointInfo("/data");
        ArrayList<String> args = new ArrayList<>();
        args.add(EXECUTABLE);
        if ("f2fs".equals(mountPoint.type)) {
            args.add("--use-f2fs-pinning");
        }
        args.add(mountPoint.filesystem);
        args.add(path);
        args.add(Long.toString(offsetOfTargetingByte));
        CommandResult result = device.executeShellV2Command(String.join(" ", args));
        assertWithMessage(
                String.format("stdout=%s\nstderr=%s", result.getStdout(), result.getStderr()))
                .that(result.getStatus()).isEqualTo(CommandStatus.SUCCESS);
    }

    /**
     * Drops file caches so that the result of {@link #damageFileAgainstBlockDevice} can be
     * observed. If a process has an open FD or memory map of the damaged file, cache eviction won't
     * happen and the damage cannot be observed.
     */
    public static void dropCaches(ITestDevice device) throws DeviceNotAvailableException {
        CommandResult result = device.executeShellV2Command(
                "sync && echo 1 > /proc/sys/vm/drop_caches");
        assertThat(result.getStatus()).isEqualTo(CommandStatus.SUCCESS);
    }

    public static void assertFileNotOpen(ITestDevice device, String path)
            throws DeviceNotAvailableException {
        CommandResult result = device.executeShellV2Command("lsof " + path);
        assertThat(result.getStatus()).isEqualTo(CommandStatus.SUCCESS);
        assertThat(result.getStdout()).isEmpty();
    }

    /**
     * Checks if the give offset of a file can be read.
     * This method will return false if the file has fs-verity enabled and is damaged at the offset.
     */
    public static boolean canReadByte(ITestDevice device, String filePath, long offset)
            throws DeviceNotAvailableException {
        CommandResult result = device.executeShellV2Command(
                "dd if=" + filePath + " bs=1 count=1 skip=" + Long.toString(offset));
        return result.getStatus() == CommandStatus.SUCCESS;
    }
}
+17 −24
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static org.junit.Assert.fail;

import android.platform.test.annotations.RootPermissionTest;

import com.android.blockdevicewriter.BlockDeviceWriter;
import com.android.fsverity.AddFsVerityCertRule;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
@@ -334,22 +335,23 @@ public class ApkVerityTest extends BaseHostJUnit4Test {
        long offsetFirstByte = 0;

        // The first two pages should be both readable at first.
        assertTrue(canReadByte(apkPath, offsetFirstByte));
        assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetFirstByte));
        if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) {
            assertTrue(canReadByte(apkPath, offsetFirstByte + FSVERITY_PAGE_SIZE));
            assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath,
                    offsetFirstByte + FSVERITY_PAGE_SIZE));
        }

        // Damage the file directly against the block device.
        damageFileAgainstBlockDevice(apkPath, offsetFirstByte);

        // Expect actual read from disk to fail but only at damaged page.
        dropCaches();
        assertFalse(canReadByte(apkPath, offsetFirstByte));
        BlockDeviceWriter.dropCaches(mDevice);
        assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetFirstByte));
        if (apkSize > offsetFirstByte + FSVERITY_PAGE_SIZE) {
            long lastByteOfTheSamePage =
                    offsetFirstByte % FSVERITY_PAGE_SIZE + FSVERITY_PAGE_SIZE - 1;
            assertFalse(canReadByte(apkPath, lastByteOfTheSamePage));
            assertTrue(canReadByte(apkPath, lastByteOfTheSamePage + 1));
            assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, lastByteOfTheSamePage));
            assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, lastByteOfTheSamePage + 1));
        }
    }

@@ -362,21 +364,22 @@ public class ApkVerityTest extends BaseHostJUnit4Test {
        long offsetOfLastByte = apkSize - 1;

        // The first two pages should be both readable at first.
        assertTrue(canReadByte(apkPath, offsetOfLastByte));
        assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetOfLastByte));
        if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) {
            assertTrue(canReadByte(apkPath, offsetOfLastByte - FSVERITY_PAGE_SIZE));
            assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath,
                    offsetOfLastByte - FSVERITY_PAGE_SIZE));
        }

        // Damage the file directly against the block device.
        damageFileAgainstBlockDevice(apkPath, offsetOfLastByte);

        // Expect actual read from disk to fail but only at damaged page.
        dropCaches();
        assertFalse(canReadByte(apkPath, offsetOfLastByte));
        BlockDeviceWriter.dropCaches(mDevice);
        assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, offsetOfLastByte));
        if (offsetOfLastByte - FSVERITY_PAGE_SIZE > 0) {
            long firstByteOfTheSamePage = offsetOfLastByte - offsetOfLastByte % FSVERITY_PAGE_SIZE;
            assertFalse(canReadByte(apkPath, firstByteOfTheSamePage));
            assertTrue(canReadByte(apkPath, firstByteOfTheSamePage - 1));
            assertFalse(BlockDeviceWriter.canReadByte(mDevice, apkPath, firstByteOfTheSamePage));
            assertTrue(BlockDeviceWriter.canReadByte(mDevice, apkPath, firstByteOfTheSamePage - 1));
        }
    }

@@ -395,8 +398,8 @@ public class ApkVerityTest extends BaseHostJUnit4Test {
                // from filesystem cache. Forcing GC workarounds the problem.
                int retry = 5;
                for (; retry > 0; retry--) {
                    dropCaches();
                    if (!canReadByte(path, kTargetOffset)) {
                    BlockDeviceWriter.dropCaches(mDevice);
                    if (!BlockDeviceWriter.canReadByte(mDevice, path, kTargetOffset)) {
                        break;
                    }
                    try {
@@ -451,16 +454,6 @@ public class ApkVerityTest extends BaseHostJUnit4Test {
        return Long.parseLong(expectRemoteCommandToSucceed("stat -c '%s' " + packageName).trim());
    }

    private void dropCaches() throws DeviceNotAvailableException {
        expectRemoteCommandToSucceed("sync && echo 1 > /proc/sys/vm/drop_caches");
    }

    private boolean canReadByte(String filePath, long offset) throws DeviceNotAvailableException {
        CommandResult result = mDevice.executeShellV2Command(
                "dd if=" + filePath + " bs=1 count=1 skip=" + Long.toString(offset));
        return result.getStatus() == CommandStatus.SUCCESS;
    }

    private String expectRemoteCommandToSucceed(String cmd) throws DeviceNotAvailableException {
        CommandResult result = mDevice.executeShellV2Command(cmd);
        assertEquals("`" + cmd + "` failed: " + result.getStderr(), CommandStatus.SUCCESS,
+7 −1
Original line number Diff line number Diff line
@@ -16,8 +16,14 @@ java_test_host {
    name: "UpdatableSystemFontTest",
    srcs: ["src/**/*.java"],
    libs: ["tradefed", "compatibility-tradefed", "compatibility-host-util"],
    static_libs: ["frameworks-base-hostutils"],
    static_libs: [
        "block_device_writer_jar",
        "frameworks-base-hostutils",
    ],
    test_suites: ["general-tests", "vts"],
    target_required: [
        "block_device_writer_module",
    ],
    data: [
        ":NotoColorEmojiTtf",
        ":UpdatableSystemFontTestCertDer",
Loading