Merge "Add permissive MTE mode." am: 514c41c6e2

Original change: https://android-review.googlesource.com/c/platform/system/core/+/2101441

Change-Id: I65fb04fe5ad6eba01e8a7533baf18c45ff606da7
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Florian Mayer 2022-06-15 17:17:38 +00:00 committed by Automerger Merge Worker
commit 226258d02f
5 changed files with 222 additions and 2 deletions

View file

@ -38,6 +38,8 @@
#include <unistd.h>
#include <android-base/macros.h>
#include <android-base/parsebool.h>
#include <android-base/properties.h>
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
#include <bionic/reserved_signals.h>
@ -49,7 +51,10 @@
#include "handler/fallback.h"
using android::base::Pipe;
using ::android::base::GetBoolProperty;
using ::android::base::ParseBool;
using ::android::base::ParseBoolResult;
using ::android::base::Pipe;
// We muck with our fds in a 'thread' that doesn't share the same fd table.
// Close fds in that thread with a raw close syscall instead of going through libc.
@ -82,6 +87,13 @@ static pid_t __gettid() {
return syscall(__NR_gettid);
}
static bool is_permissive_mte() {
// Environment variable for testing or local use from shell.
char* permissive_env = getenv("MTE_PERMISSIVE");
return GetBoolProperty("persist.sys.mte.permissive", false) ||
(permissive_env && ParseBool(permissive_env) == ParseBoolResult::kTrue);
}
static inline void futex_wait(volatile void* ftx, int value) {
syscall(__NR_futex, ftx, FUTEX_WAIT, value, nullptr, nullptr, 0);
}
@ -592,7 +604,28 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c
// If the signal is fatal, don't unlock the mutex to prevent other crashing threads from
// starting to dump right before our death.
pthread_mutex_unlock(&crash_mutex);
} else {
}
#ifdef __aarch64__
else if (info->si_signo == SIGSEGV &&
(info->si_code == SEGV_MTESERR || info->si_code == SEGV_MTEAERR) &&
is_permissive_mte()) {
// If we are in permissive MTE mode, we do not crash, but instead disable MTE on this thread,
// and then let the failing instruction be retried. The second time should work (except
// if there is another non-MTE fault).
int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
if (tagged_addr_ctrl < 0) {
fatal_errno("failed to PR_GET_TAGGED_ADDR_CTRL");
}
tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_NONE;
if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) {
fatal_errno("failed to PR_SET_TAGGED_ADDR_CTRL");
}
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING.");
pthread_mutex_unlock(&crash_mutex);
}
#endif
else {
// Resend the signal, so that either the debugger or the parent's waitpid sees it.
resend_signal(info);
}

View file

@ -0,0 +1,34 @@
// Copyright (C) 2022 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.
cc_binary {
name: "mte_crash",
srcs: ["mte_crash.cpp"],
sanitize: {
memtag_heap: true,
diag: {
memtag_heap: true,
},
},
}
java_test_host {
name: "permissive_mte_test",
libs: ["tradefed"],
static_libs: ["frameworks-base-hostutils", "cts-install-lib-host"],
srcs: ["src/**/PermissiveMteTest.java", ":libtombstone_proto-src"],
data: [":mte_crash"],
test_config: "AndroidTest.xml",
test_suites: ["general-tests"],
}

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2022 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.
-->
<configuration description="Runs the permissive MTE tests">
<option name="test-suite-tag" value="init_test_upgrade_mte" />
<option name="test-suite-tag" value="apct" />
<!-- For tombstone inspection. -->
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
<option name="cleanup" value="true" />
<option name="push" value="mte_crash->/data/local/tmp/mte_crash" />
</target_preparer>
<test class="com.android.tradefed.testtype.HostTest" >
<option name="jar" value="permissive_mte_test.jar" />
</test>
</configuration>

View file

@ -0,0 +1,24 @@
/*
* Copyright (C) 2022 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.
*/
#include <stdio.h>
#include <stdlib.h>
int main(int, char**) {
volatile char* f = (char*)malloc(1);
printf("%c\n", f[17]);
return 0;
}

View file

@ -0,0 +1,100 @@
/*
* Copyright (C) 2022 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.tests.init;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assume.assumeTrue;
import com.android.server.os.TombstoneProtos.Tombstone;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.CommandResult;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Arrays;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(DeviceJUnit4ClassRunner.class)
public class PermissiveMteTest extends BaseHostJUnit4Test {
String mUUID;
@Before
public void setUp() throws Exception {
mUUID = java.util.UUID.randomUUID().toString();
CommandResult result =
getDevice().executeShellV2Command("/data/local/tmp/mte_crash setUp " + mUUID);
assumeTrue("mte_crash needs to segfault", result.getExitCode() == 139);
}
Tombstone parseTombstone(String tombstonePath) throws Exception {
File tombstoneFile = getDevice().pullFile(tombstonePath);
InputStream istr = new FileInputStream(tombstoneFile);
Tombstone tombstoneProto;
try {
tombstoneProto = Tombstone.parseFrom(istr);
} finally {
istr.close();
}
return tombstoneProto;
}
@After
public void tearDown() throws Exception {
String[] tombstones = getDevice().getChildren("/data/tombstones");
for (String tombstone : tombstones) {
if (!tombstone.endsWith(".pb")) {
continue;
}
String tombstonePath = "/data/tombstones/" + tombstone;
Tombstone tombstoneProto = parseTombstone(tombstonePath);
if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(mUUID))) {
continue;
}
getDevice().deleteFile(tombstonePath);
// remove the non .pb file as well.
getDevice().deleteFile(tombstonePath.substring(0, tombstonePath.length() - 3));
}
}
@Test
public void testCrash() throws Exception {
CommandResult result = getDevice().executeShellV2Command(
"MTE_PERMISSIVE=1 /data/local/tmp/mte_crash testCrash " + mUUID);
assertThat(result.getExitCode()).isEqualTo(0);
int numberTombstones = 0;
String[] tombstones = getDevice().getChildren("/data/tombstones");
for (String tombstone : tombstones) {
if (!tombstone.endsWith(".pb")) {
continue;
}
String tombstonePath = "/data/tombstones/" + tombstone;
Tombstone tombstoneProto = parseTombstone(tombstonePath);
if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(mUUID))) {
continue;
}
if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains("testCrash"))) {
continue;
}
numberTombstones++;
}
assertThat(numberTombstones).isEqualTo(1);
}
}