kristof.beyls created this revision.
Herald added subscribers: cfe-commits, aheejin, dschuff.

This is part of implementing a technique to mitigate against Spectre v1,
similar in spirit to what has been proposed by Chandler for X86_64 at
http://lists.llvm.org/pipermail/llvm-dev/2018-March/122085.html.

This patch adds a new builtin function that provides a mechanism for
limiting the effects of miss-speculation by a CPU.
This patch provides the clang-side of the needed functionality; there is
also an llvm-side patch this patch is dependent on.

We've tried to design this in such a way that it can be used for any
target where this might be necessary. The patch provides a generic
implementation of the builtin, with most of the target-specific
support in the LLVM counter part to this clang patch.

The signature of the new, polymorphic, builtin is:

T __builtin_speculation_safe_value(T v)

T can be any integral type (signed or unsigned char, int, short, long,
etc) or any pointer type.

The builtin assures that value v will be made 0 on execution paths that
are being executed under control flow miss-speculation by the CPU, when
the miss-speculated path originated due to misprediction of a direct
conditional branch.

Whereas this still leaves open the possibility of execution on a
miss-speculated path starting at misprediction of other control flow
instructions, our believe is that the above guarantee is still useful in
mitigating vulnerability to Spectre v1-style attacks and implementable
for most, if not all, target instruction sets.

This also introduces the predefined pre-processor macro
__HAVE_SPECULATION_SAFE_LOAD, that allows users to check if their
version of the compiler supports this intrinsic.


Repository:
  rC Clang

https://reviews.llvm.org/D49073

Files:
  include/clang/Basic/Builtins.def
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/Sema.h
  lib/CodeGen/CGBuiltin.cpp
  lib/Frontend/InitPreprocessor.cpp
  lib/Sema/SemaChecking.cpp
  test/CodeGen/builtin-speculation-safe-value.c
  test/Preprocessor/init.c
  test/Sema/builtin-speculation-safe-value.c
  test/Sema/builtin-speculation-safe-value.cpp

Index: test/Sema/builtin-speculation-safe-value.cpp
===================================================================
--- /dev/null
+++ test/Sema/builtin-speculation-safe-value.cpp
@@ -0,0 +1,58 @@
+// REQUIRES: aarch64-registered-target
+// RUN: %clang_cc1 -triple aarch64 -x c++ -std=c++11 -DENABLE_ERRORS -verify %s
+// RUN: %clang_cc1 -triple aarch64 -x c++ -std=c++11 %s -emit-llvm -o -
+
+void test_type() {
+  char c;
+  c = __builtin_speculation_safe_value(c);
+
+  short s;
+  s = __builtin_speculation_safe_value(s);
+
+  int i;
+  i = __builtin_speculation_safe_value(i);
+
+  long l;
+  l = __builtin_speculation_safe_value(l);
+
+  long long ll;
+  ll = __builtin_speculation_safe_value(ll);
+
+  int *ip;
+  ip = __builtin_speculation_safe_value(ip);
+
+  int (*fp)(int, int);
+  fp = __builtin_speculation_safe_value(fp);
+
+  enum {e1, e2} e;
+  e = __builtin_speculation_safe_value(e);
+
+#ifdef enable_errors
+  float f;
+  f = __builtin_speculation_safe_value(f); // expected-error {{argument to speculation_safe_value builtin must be a pointer or integer ('float' invalid)}}
+
+  struct s { int a; } s;
+  s = __builtin_speculation_safe_value(s); // expected-error {{argument to speculation_safe_value builtin must be a pointer or integer ('struct s' invalid)}}
+
+  union u { int a; } u;
+  u = __builtin_speculation_safe_value(u); // expected-error {{argument to speculation_safe_value builtin must be a pointer or integer ('union u' invalid)}}
+
+  char __attribute__((vector_size(16))) v;
+  v = __builtin_speculation_safe_value(v); // expected-error {{argument to speculation_safe_value builtin must be a pointer or integer ('__attribute__((__vector_size__(16 * sizeof(char)))) char' (vector of 16 'char' values) invalid)}}
+#endif
+}
+
+#ifdef ENABLE_ERRORS
+template<typename T>
+T load(const T v) {
+  return __builtin_speculation_safe_value(v); // expected-error {{argument to speculation_safe_value builtin must be a pointer or integer ('float' invalid)}}
+}
+
+void test_templates() {
+  int i;
+  load(i);
+
+  float f;
+  load(f); // expected-note {{in instantiation of function template specialization 'load<float>' requested here}}
+}
+#endif
Index: test/Sema/builtin-speculation-safe-value.c
===================================================================
--- /dev/null
+++ test/Sema/builtin-speculation-safe-value.c
@@ -0,0 +1,46 @@
+// REQUIRES: aarch64-registered-target
+// RUN: %clang_cc1 -triple aarch64 -DENABLE_ERRORS -verify %s
+// RUN: %clang_cc1 -triple aarch64 %s -emit-llvm -o -
+
+void test_type() {
+  char c;
+  c = __builtin_speculation_safe_value(c);
+
+  short s;
+  s = __builtin_speculation_safe_value(s);
+
+  int i;
+  i = __builtin_speculation_safe_value(i);
+
+  long l;
+  l = __builtin_speculation_safe_value(l);
+
+  long long ll;
+  ll = __builtin_speculation_safe_value(ll);
+
+  int *ip;
+  ip = __builtin_speculation_safe_value(ip);
+
+  int (*fp)(int, int);
+  fp = __builtin_speculation_safe_value(fp);
+
+  enum {e1, e2} e;
+  e = __builtin_speculation_safe_value(e);
+
+#ifdef ENABLE_ERRORS
+  float f;
+  f = __builtin_speculation_safe_value(f); // expected-error {{argument to speculation_safe_value builtin must be a pointer or integer ('float' invalid)}}
+
+  struct S { int a; } S;
+  S = __builtin_speculation_safe_value(S); // expected-error {{argument to speculation_safe_value builtin must be a pointer or integer ('struct S' invalid)}}
+
+  union U { int a; } U;
+  U = __builtin_speculation_safe_value(U); // expected-error {{argument to speculation_safe_value builtin must be a pointer or integer ('union U' invalid)}}
+
+  char __attribute__((vector_size(16))) v;
+  v = __builtin_speculation_safe_value(v); // expected-error {{argument to speculation_safe_value builtin must be a pointer or integer ('__attribute__((__vector_size__(16 * sizeof(char)))) char' (vector of 16 'char' values) invalid)}}
+
+  c = __builtin_speculation_safe_value(); // expected-error {{too few arguments to function call, expected at least 1, have 0}}
+  c = __builtin_speculation_safe_value(c, s); // expected-error {{too many arguments to function call, expected at most 1, have 2}}
+#endif
+}
Index: test/Preprocessor/init.c
===================================================================
--- test/Preprocessor/init.c
+++ test/Preprocessor/init.c
@@ -9144,6 +9144,7 @@
 // WEBASSEMBLY32-NEXT:#define __GNUC_STDC_INLINE__ 1
 // WEBASSEMBLY32-NEXT:#define __GNUC__ {{.*}}
 // WEBASSEMBLY32-NEXT:#define __GXX_ABI_VERSION 1002
+// WEBASSEMBLY32-NEXT:#define __HAVE_SPECULATION_SAFE_VALUE 1
 // WEBASSEMBLY32-NEXT:#define __ILP32__ 1
 // WEBASSEMBLY32-NEXT:#define __INT16_C_SUFFIX__
 // WEBASSEMBLY32-NEXT:#define __INT16_FMTd__ "hd"
@@ -9476,6 +9477,7 @@
 // WEBASSEMBLY64-NEXT:#define __GNUC_STDC_INLINE__ 1
 // WEBASSEMBLY64-NEXT:#define __GNUC__ {{.}}
 // WEBASSEMBLY64-NEXT:#define __GXX_ABI_VERSION 1002
+// WEBASSEMBLY64-NEXT:#define __HAVE_SPECULATION_SAFE_VALUE 1
 // WEBASSEMBLY64-NOT:#define __ILP32__
 // WEBASSEMBLY64-NEXT:#define __INT16_C_SUFFIX__
 // WEBASSEMBLY64-NEXT:#define __INT16_FMTd__ "hd"
Index: test/CodeGen/builtin-speculation-safe-value.c
===================================================================
--- /dev/null
+++ test/CodeGen/builtin-speculation-safe-value.c
@@ -0,0 +1,19 @@
+// REQUIRES: aarch64-registered-target
+// RUN: %clang_cc1 -triple aarch64-linux-gnu -emit-llvm %s -o - | FileCheck -check-prefix=CHECK-SUPPORTED %s
+
+void test(char c, int i, void *p) {
+  // CHECK-LABEL-SUPPORTED: define void @test
+
+  char c_safe = __builtin_speculation_safe_value(c);
+  // CHECK-SUPPORTED: call i8 @llvm.speculationsafevalue.i8(i8 %{{[0-9a-z]+}})
+
+  int i_safe = __builtin_speculation_safe_value(i);
+  // CHECK-SUPPORTED: call i32 @llvm.speculationsafevalue.i32(i32 %{{[0-9a-z]+}})
+
+  void *p_safe = __builtin_speculation_safe_value(p);
+  // CHECK-SUPPORTED: call i8* @llvm.speculationsafevalue.p0i8(i8* %{{[0-9a-z]+}})
+
+  int arr[4];
+  int *arr_safe = __builtin_speculation_safe_value(arr);
+  // CHECK-SUPPORTED: call i32* @llvm.speculationsafevalue.p0i32(i32* %{{[0-9a-z]+}})
+}
Index: lib/Sema/SemaChecking.cpp
===================================================================
--- lib/Sema/SemaChecking.cpp
+++ lib/Sema/SemaChecking.cpp
@@ -1333,6 +1333,8 @@
     if (SemaBuiltinOSLogFormat(TheCall))
       return ExprError();
     break;
+  case Builtin::BI__builtin_speculation_safe_value:
+    return SemaBuiltinSpeculationSafeValueOverloaded(TheCallResult);
   }
 
   // Since the target specific builtins for each arch overlap, only check those
@@ -4271,6 +4273,50 @@
   return TheCallResult;
 }
 
+ExprResult
+Sema::SemaBuiltinSpeculationSafeValueOverloaded(ExprResult TheCallResult) {
+  CallExpr *TheCall = (CallExpr *)TheCallResult.get();
+  DeclRefExpr *DRE =
+      cast<DeclRefExpr>(TheCall->getCallee()->IgnoreParenCasts());
+  FunctionDecl *FDecl = cast<FunctionDecl>(DRE->getDecl());
+  unsigned BuiltinID = FDecl->getBuiltinID();
+  assert(BuiltinID == Builtin::BI__builtin_speculation_safe_value &&
+         "Unexpected speculation_safe_value builtin!");
+
+  // Too few args.
+  if (TheCall->getNumArgs() < 1)
+    return Diag(TheCall->getLocEnd(),
+                diag::err_typecheck_call_too_few_args_at_least)
+           << 0 /*function call*/ << 1 /* min args */ << TheCall->getNumArgs();
+
+  // Too many args.
+  if (TheCall->getNumArgs() > 1)
+    return Diag(TheCall->getArg(1)->getLocStart(),
+                diag::err_typecheck_call_too_many_args_at_most)
+           << 0 /*function call*/ << 1 << TheCall->getNumArgs()
+           << SourceRange(TheCall->getArg(1)->getLocStart(),
+                          (*(TheCall->arg_end() - 1))->getLocEnd());
+
+  // Derive the return type from the pointer argument.
+  ExprResult FirstArg =
+      DefaultFunctionArrayLvalueConversion(TheCall->getArg(0));
+  if (FirstArg.isInvalid())
+    return true;
+  TheCall->setArg(0, FirstArg.get());
+  QualType FirstArgType = FirstArg.get()->getType();
+
+  TheCall->setType(FirstArgType);
+
+  // The first argument must be an integer or pointer type.
+  if (!(FirstArgType->isIntegerType() || FirstArgType->isAnyPointerType()))
+    return Diag(TheCall->getArg(0)->getLocStart(),
+                diag::err_specsafevalue_builtin_must_be_pointer_or_integral)
+           << TheCall->getArg(0)->getType()
+           << TheCall->getArg(0)->getSourceRange();
+
+  return TheCallResult;
+}
+
 /// CheckObjCString - Checks that the argument to the builtin
 /// CFString constructor is correct
 /// Note: It might also make sense to do the UTF-16 conversion here (would
Index: lib/Frontend/InitPreprocessor.cpp
===================================================================
--- lib/Frontend/InitPreprocessor.cpp
+++ lib/Frontend/InitPreprocessor.cpp
@@ -1095,6 +1095,8 @@
     Builder.defineMacro("__GLIBCXX_BITSIZE_INT_N_0", "128");
   }
 
+  Builder.defineMacro("__HAVE_SPECULATION_SAFE_VALUE");
+
   // Get other target #defines.
   TI.getTargetDefines(LangOpts, Builder);
 }
Index: lib/CodeGen/CGBuiltin.cpp
===================================================================
--- lib/CodeGen/CGBuiltin.cpp
+++ lib/CodeGen/CGBuiltin.cpp
@@ -3632,6 +3632,17 @@
     Value *ArgPtr = Builder.CreateLoad(SrcAddr, "ap.val");
     return RValue::get(Builder.CreateStore(ArgPtr, DestAddr));
   }
+
+  case Builtin::BI__builtin_speculation_safe_value: {
+    Value *Val = EmitScalarExpr(E->getArg(0));
+
+    llvm::Type *T = ConvertType(E->getType());
+    assert((isa<llvm::IntegerType>(T) || isa<llvm::PointerType>(T)) &&
+           "unsupported type");
+
+    return RValue::get(Builder.CreateCall(
+        CGM.getIntrinsic(Intrinsic::speculationsafevalue, T), {Val}));
+  }
   }
 
   // If this is an alias for a lib function (e.g. __builtin_sin), emit
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -10430,6 +10430,8 @@
                                      AtomicExpr::AtomicOp Op);
   ExprResult SemaBuiltinOperatorNewDeleteOverloaded(ExprResult TheCallResult,
                                                     bool IsDelete);
+  ExprResult
+  SemaBuiltinSpeculationSafeValueOverloaded(ExprResult TheCallResult);
   bool SemaBuiltinConstantArg(CallExpr *TheCall, int ArgNum,
                               llvm::APSInt &Result);
   bool SemaBuiltinConstantArgRange(CallExpr *TheCall, int ArgNum, int Low,
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -9324,4 +9324,8 @@
    "member '%2' is missing|"
    "the type is not trivially copyable|"
    "the type does not have the expected form}1">;
+
+def err_specsafevalue_builtin_must_be_pointer_or_integral : Error<
+  "argument to speculation_safe_value builtin must be a pointer or integer "
+  "(%0 invalid)">;
 } // end of sema component.
Index: include/clang/Basic/Builtins.def
===================================================================
--- include/clang/Basic/Builtins.def
+++ include/clang/Basic/Builtins.def
@@ -1495,6 +1495,9 @@
 BUILTIN(__builtin_ms_va_end, "vc*&", "n")
 BUILTIN(__builtin_ms_va_copy, "vc*&c*&", "n")
 
+// T __builtin_speculation_safe_value (T val)
+BUILTIN(__builtin_speculation_safe_value, "vv", "t")
+
 #undef BUILTIN
 #undef LIBBUILTIN
 #undef LANGBUILTIN
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to