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

Commit f26c5a51 authored by Kohsuke Yatoh's avatar Kohsuke Yatoh Committed by Android (Google) Code Review
Browse files

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

parents a2409ede 45c62319
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