Commit e29c70f6 authored by Richard Townsend's avatar Richard Townsend Committed by Commit Bot

feat: detect support for MTE and BTI

MTE and BTI are Armv8.5-A features which help memory safety and
control-flow integrity. This CL adds support for detecting them
using the AT_HWCAP2 Linux API.

MTE contains new instructions which will crash older cores. base::CPU
can now be used to tell whether these instructions are safe to
execute and adds a test to verify this. This does not test whether
MTE is configured correctly to give any security properties, a
subsequent CL will add tests for this.

Bug: 1145581, 1137393
Change-Id: I9abd66af1c1bdbf49eebd17f225a59b0a6cdfd33
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2526738
Commit-Queue: Richard Townsend <richard.townsend@arm.com>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#831180}
parent 2c47a322
...@@ -3471,6 +3471,15 @@ test("base_unittests") { ...@@ -3471,6 +3471,15 @@ test("base_unittests") {
"//build/config/compiler:no_size_t_to_int_warning", "//build/config/compiler:no_size_t_to_int_warning",
"//build/config/compiler:noshadowing", "//build/config/compiler:noshadowing",
] ]
if (current_cpu == "arm64" &&
(is_linux || is_chromeos || is_android || is_fuchsia)) {
# base/ has access to the MTE intrinsics because it needs to use them,
# but they're not backwards compatible. Use base::CPU::has_mte()
# beforehand to confirm or use indirect functions (ifuncs) to select
# an MTE-specific implementation via at dynamic link-time.
cflags = [ "-march=armv8-a+memtag" ]
}
} }
action("build_date") { action("build_date") {
......
...@@ -32,7 +32,12 @@ ...@@ -32,7 +32,12 @@
#if defined(ARCH_CPU_ARM_FAMILY) && \ #if defined(ARCH_CPU_ARM_FAMILY) && \
(defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS)) (defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS))
#include <asm/hwcap.h>
#include <sys/auxv.h>
#include "base/files/file_util.h" #include "base/files/file_util.h"
// Temporary definitions until a new hwcap.h is pulled in.
#define HWCAP2_MTE (1 << 18)
#define HWCAP2_BTI (1 << 17)
#endif #endif
#if defined(ARCH_CPU_X86_FAMILY) #if defined(ARCH_CPU_X86_FAMILY)
...@@ -82,28 +87,7 @@ std::tuple<int, int, int, int> ComputeX86FamilyAndModel( ...@@ -82,28 +87,7 @@ std::tuple<int, int, int, int> ComputeX86FamilyAndModel(
} // namespace internal } // namespace internal
#endif // defined(ARCH_CPU_X86_FAMILY) #endif // defined(ARCH_CPU_X86_FAMILY)
CPU::CPU() CPU::CPU() {
: signature_(0),
type_(0),
family_(0),
model_(0),
stepping_(0),
ext_model_(0),
ext_family_(0),
has_mmx_(false),
has_sse_(false),
has_sse2_(false),
has_sse3_(false),
has_ssse3_(false),
has_sse41_(false),
has_sse42_(false),
has_popcnt_(false),
has_avx_(false),
has_avx2_(false),
has_aesni_(false),
has_non_stop_time_stamp_counter_(false),
is_running_in_vm_(false),
cpu_vendor_("unknown") {
Initialize(); Initialize();
} }
...@@ -307,6 +291,14 @@ void CPU::Initialize() { ...@@ -307,6 +291,14 @@ void CPU::Initialize() {
#elif defined(ARCH_CPU_ARM_FAMILY) #elif defined(ARCH_CPU_ARM_FAMILY)
#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS) #if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS)
cpu_brand_ = *CpuInfoBrand(); cpu_brand_ = *CpuInfoBrand();
#if defined(ARCH_CPU_ARM64)
// Check for Armv8.5-A BTI/MTE support, exposed via HWCAP2
unsigned long hwcap2 = getauxval(AT_HWCAP2);
has_mte_ = hwcap2 & HWCAP2_MTE;
has_bti_ = hwcap2 & HWCAP2_BTI;
#endif
#elif defined(OS_WIN) #elif defined(OS_WIN)
// Windows makes high-resolution thread timing information available in // Windows makes high-resolution thread timing information available in
// user-space. // user-space.
......
...@@ -30,7 +30,7 @@ BASE_EXPORT std::tuple<int, int, int, int> ComputeX86FamilyAndModel( ...@@ -30,7 +30,7 @@ BASE_EXPORT std::tuple<int, int, int, int> ComputeX86FamilyAndModel(
// Query information about the processor. // Query information about the processor.
class BASE_EXPORT CPU final { class BASE_EXPORT CPU final {
public: public:
CPU(); explicit CPU();
enum IntelMicroArchitecture { enum IntelMicroArchitecture {
PENTIUM, PENTIUM,
...@@ -70,6 +70,10 @@ class BASE_EXPORT CPU final { ...@@ -70,6 +70,10 @@ class BASE_EXPORT CPU final {
} }
bool is_running_in_vm() const { return is_running_in_vm_; } bool is_running_in_vm() const { return is_running_in_vm_; }
// Armv8.5-A extensions for control flow and memory safety.
bool has_mte() const { return has_mte_; }
bool has_bti() const { return has_bti_; }
IntelMicroArchitecture GetIntelMicroArchitecture() const; IntelMicroArchitecture GetIntelMicroArchitecture() const;
const std::string& cpu_brand() const { return cpu_brand_; } const std::string& cpu_brand() const { return cpu_brand_; }
...@@ -131,27 +135,29 @@ class BASE_EXPORT CPU final { ...@@ -131,27 +135,29 @@ class BASE_EXPORT CPU final {
// Query the processor for CPUID information. // Query the processor for CPUID information.
void Initialize(); void Initialize();
int signature_; // raw form of type, family, model, and stepping int signature_ = 0; // raw form of type, family, model, and stepping
int type_; // process type int type_ = 0; // process type
int family_; // family of the processor int family_ = 0; // family of the processor
int model_; // model of processor int model_ = 0; // model of processor
int stepping_; // processor revision number int stepping_ = 0; // processor revision number
int ext_model_; int ext_model_ = 0;
int ext_family_; int ext_family_ = 0;
bool has_mmx_; bool has_mmx_ = false;
bool has_sse_; bool has_sse_ = false;
bool has_sse2_; bool has_sse2_ = false;
bool has_sse3_; bool has_sse3_ = false;
bool has_ssse3_; bool has_ssse3_ = false;
bool has_sse41_; bool has_sse41_ = false;
bool has_sse42_; bool has_sse42_ = false;
bool has_popcnt_; bool has_popcnt_ = false;
bool has_avx_; bool has_avx_ = false;
bool has_avx2_; bool has_avx2_ = false;
bool has_aesni_; bool has_aesni_ = false;
bool has_non_stop_time_stamp_counter_; bool has_mte_ = false; // Armv8.5-A MTE (Memory Taggging Extension)
bool is_running_in_vm_; bool has_bti_ = false; // Armv8.5-A BTI (Branch Target Identification)
std::string cpu_vendor_; bool has_non_stop_time_stamp_counter_ = false;
bool is_running_in_vm_ = false;
std::string cpu_vendor_ = "unknown";
std::string cpu_brand_; std::string cpu_brand_;
}; };
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "base/cpu.h" #include "base/cpu.h"
#include "base/logging.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -13,9 +14,9 @@ ...@@ -13,9 +14,9 @@
// "undefined instruction" exceptions. That is, this test succeeds when this // "undefined instruction" exceptions. That is, this test succeeds when this
// test finishes without a crash. // test finishes without a crash.
TEST(CPU, RunExtendedInstructions) { TEST(CPU, RunExtendedInstructions) {
#if defined(ARCH_CPU_X86_FAMILY)
// Retrieve the CPU information. // Retrieve the CPU information.
base::CPU cpu; base::CPU cpu;
#if defined(ARCH_CPU_X86_FAMILY)
ASSERT_TRUE(cpu.has_mmx()); ASSERT_TRUE(cpu.has_mmx());
ASSERT_TRUE(cpu.has_sse()); ASSERT_TRUE(cpu.has_sse());
...@@ -65,7 +66,6 @@ TEST(CPU, RunExtendedInstructions) { ...@@ -65,7 +66,6 @@ TEST(CPU, RunExtendedInstructions) {
// Execute an AVX 2 instruction. // Execute an AVX 2 instruction.
__asm__ __volatile__("vpunpcklbw %%ymm0, %%ymm0, %%ymm0\n" : : : "xmm0"); __asm__ __volatile__("vpunpcklbw %%ymm0, %%ymm0, %%ymm0\n" : : : "xmm0");
} }
// Visual C 32 bit and ClangCL 32/64 bit test. // Visual C 32 bit and ClangCL 32/64 bit test.
#elif defined(COMPILER_MSVC) && (defined(ARCH_CPU_32_BITS) || \ #elif defined(COMPILER_MSVC) && (defined(ARCH_CPU_32_BITS) || \
(defined(ARCH_CPU_64_BITS) && defined(__clang__))) (defined(ARCH_CPU_64_BITS) && defined(__clang__)))
...@@ -113,6 +113,26 @@ TEST(CPU, RunExtendedInstructions) { ...@@ -113,6 +113,26 @@ TEST(CPU, RunExtendedInstructions) {
} }
#endif // defined(COMPILER_GCC) #endif // defined(COMPILER_GCC)
#endif // defined(ARCH_CPU_X86_FAMILY) #endif // defined(ARCH_CPU_X86_FAMILY)
#if defined(ARCH_CPU_ARM64)
// Check that the CPU is correctly reporting support for the Armv8.5-A memory
// tagging extension. The new MTE instructions aren't encoded in NOP space
// like BTI/Pointer Authentication and will crash older cores with a SIGILL if
// used incorrectly. This test demonstrates how it should be done and that
// this approach works.
if (cpu.has_mte()) {
char ptr[32];
uint64_t val;
// Execute a trivial MTE instruction. Normally, MTE should be used via the
// intrinsics documented at
// https://developer.arm.com/documentation/101028/0012/10--Memory-tagging-intrinsics,
// this test uses the irg (Insert Random Tag) instruction directly to make
// sure that it's not optimized out by the compiler.
__asm__ __volatile__("irg %0, %1" : "=r"(val) : "r"(ptr));
} else {
GTEST_SKIP();
}
#endif
} }
// For https://crbug.com/249713 // For https://crbug.com/249713
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment