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

Commit cfc472b8 authored by Florian Mayer's avatar Florian Mayer Committed by Automerger Merge Worker
Browse files

Merge "Add permissive MTE mode." am: 514c41c6 am: 226258d0 am: fa068b7f

parents f1f198d9 fa068b7f
Loading
Loading
Loading
Loading
+35 −2
Original line number Original line Diff line number Diff line
@@ -38,6 +38,8 @@
#include <unistd.h>
#include <unistd.h>


#include <android-base/macros.h>
#include <android-base/macros.h>
#include <android-base/parsebool.h>
#include <android-base/properties.h>
#include <android-base/unique_fd.h>
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
#include <async_safe/log.h>
#include <bionic/reserved_signals.h>
#include <bionic/reserved_signals.h>
@@ -49,7 +51,10 @@


#include "handler/fallback.h"
#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.
// 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.
// 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);
  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) {
static inline void futex_wait(volatile void* ftx, int value) {
  syscall(__NR_futex, ftx, FUTEX_WAIT, value, nullptr, nullptr, 0);
  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
    // If the signal is fatal, don't unlock the mutex to prevent other crashing threads from
    // starting to dump right before our death.
    // starting to dump right before our death.
    pthread_mutex_unlock(&crash_mutex);
    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 the signal, so that either the debugger or the parent's waitpid sees it.
    resend_signal(info);
    resend_signal(info);
  }
  }
+34 −0
Original line number Original line Diff line number Diff line
// 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"],
}
+29 −0
Original line number Original line Diff line number Diff line
<?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>
 No newline at end of file
+24 −0
Original line number Original line Diff line number Diff line
/*
 * 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;
}
+100 −0
Original line number Original line Diff line number Diff line
/*
 * 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);
  }
}