erik.pilkington created this revision.
erik.pilkington added reviewers: aaron.ballman, rjmccall, eli.friedman.
Herald added subscribers: dexonsmith, jkorous.
Herald added a project: clang.

This attribute applies to declarations of C stdlib functions (sprintf, 
memcpy...) that have known fortified variants (__sprintf_chk, __memcpy_chk, 
...). When applied, clang will emit calls to the fortified variant functions. 
Without this attribute, its impossible to write `gnu_inline`-style wrappers to 
the variadic functions because we don't support `__builtin_va_arg_pack`, and 
don't intend to (see https://reviews.llvm.org/D57635).

Thanks for taking a look!
Erik


Repository:
  rC Clang

https://reviews.llvm.org/D57918

Files:
  clang/include/clang/Basic/Attr.td
  clang/include/clang/Basic/AttrDocs.td
  clang/include/clang/Basic/Builtins.h
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Basic/Builtins.cpp
  clang/lib/CodeGen/CGBuiltin.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/test/CodeGen/fortify-std-lib.c
  clang/test/Misc/pragma-attribute-supported-attributes-list.test
  clang/test/Sema/fortify-std-lib.c

Index: clang/test/Sema/fortify-std-lib.c
===================================================================
--- /dev/null
+++ clang/test/Sema/fortify-std-lib.c
@@ -0,0 +1,18 @@
+// RUN: %clang_cc1 -triple x86_64-apple-macosx10.14.0 -fsyntax-only %s -verify
+
+typedef unsigned long size_t;
+
+__attribute__((fortify_stdlib(0, 0)))
+int not_anyting_special(); // expected-error {{'fortify_stdlib' attribute applied to an unknown function}}
+
+__attribute__((fortify_stdlib(4, 0))) // expected-error {{'fortify_stdlib' attribute requires integer constant between 0 and 3 inclusive}}
+int sprintf(char *, const char *, ...);
+
+__attribute__((fortify_stdlib())) // expected-error {{'fortify_stdlib' attribute requires exactly 2 arguments}}
+int sprintf(char *, const char *, ...);
+
+__attribute__((fortify_stdlib(1, 2, 3))) // expected-error {{'fortify_stdlib' attribute requires exactly 2 arguments}}
+int sprintf(char *, const char *, ...);
+
+__attribute__((fortify_stdlib(-1, 2))) // expected-error {{'fortify_stdlib' attribute requires a non-negative integral compile time constant expression}}
+int sprintf(char *, const char *, ...);
Index: clang/test/Misc/pragma-attribute-supported-attributes-list.test
===================================================================
--- clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -52,6 +52,7 @@
 // CHECK-NEXT: ExternalSourceSymbol ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable))
 // CHECK-NEXT: FlagEnum (SubjectMatchRule_enum)
 // CHECK-NEXT: Flatten (SubjectMatchRule_function)
+// CHECK-NEXT: FortifyStdLib (SubjectMatchRule_function)
 // CHECK-NEXT: GNUInline (SubjectMatchRule_function)
 // CHECK-NEXT: Hot (SubjectMatchRule_function)
 // CHECK-NEXT: IBAction (SubjectMatchRule_objc_method_is_instance)
Index: clang/test/CodeGen/fortify-std-lib.c
===================================================================
--- /dev/null
+++ clang/test/CodeGen/fortify-std-lib.c
@@ -0,0 +1,220 @@
+// RUN: %clang_cc1       -triple x86_64-apple-macosx10.14.0 %s -emit-llvm -O0 -disable-llvm-passes -o - -Wno-format-security | FileCheck %s
+// RUN: %clang_cc1 -xc++ -triple x86_64-apple-macosx10.14.0 %s -emit-llvm -O0 -disable-llvm-passes -o - -Wno-format-security | FileCheck %s
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+#else
+#define EXTERN
+#endif
+
+#define FSL(x,y) __attribute__((fortify_stdlib(x,y)))
+typedef unsigned long size_t;
+
+FSL(0, 0) EXTERN
+void *memcpy(void *dst, const void *src, size_t sz);
+
+EXTERN
+void call_memcpy(void *dst, const void *src, size_t sz) {
+  memcpy(dst, src, sz);
+  // CHECK-LABEL: define void @call_memcpy
+  // CHECK: [[REG:%[0-9]+]] = call i64 @llvm.objectsize.i64.p0i8(i8*{{.*}}, i1 false, i1 true, i1 false)
+  // CHECK: call i8* @__memcpy_chk(i8* {{.*}}, i8* {{.*}}, i64 {{.*}}, i64 [[REG]])
+}
+
+FSL(0, 0) EXTERN
+void *memmove(void *dst, const void *src, size_t sz);
+
+EXTERN
+void call_memmove(void *dst, const void *src, size_t sz) {
+  memmove(dst, src, sz);
+  // CHECK-LABEL: define void @call_memmove
+  // CHECK: [[REG:%[0-9]+]] = call i64 @llvm.objectsize.i64.p0i8(i8*{{.*}}, i1 false, i1 true, i1 false)
+  // CHECK: call i8* @__memmove_chk(i8* {{.*}}, i8* {{.*}}, i64 {{.*}}, i64 [[REG]])
+}
+
+FSL(0, 0) EXTERN
+void *memset(void *dst, int c, size_t sz);
+
+EXTERN
+void call_memset(void *dst, int c, size_t sz) {
+  memset(dst, c, sz);
+  // CHECK-LABEL: define void @call_memset
+  // CHECK: [[REG:%[0-9]+]] = call i64 @llvm.objectsize.i64.p0i8(i8*{{.*}}, i1 false, i1 true, i1 false)
+  // CHECK: call i8* @__memset_chk(i8* {{.*}}, i32 {{.*}}, i64 {{.*}}, i64 [[REG]])
+}
+
+FSL(0, 0) EXTERN
+char *stpcpy(char* dst, const char *src);
+
+EXTERN
+void call_stpcpy(char *dst, const char *src) {
+  stpcpy(dst, src);
+  // CHECK-LABEL: define void @call_stpcpy
+  // CHECK: [[REG:%[0-9]+]] = call i64 @llvm.objectsize.i64.p0i8(i8*{{.*}}, i1 false, i1 true, i1 false)
+  // CHECK: call i8* @__stpcpy_chk(i8* {{.*}}, i8* {{.*}}, i64 [[REG]])
+}
+
+FSL(0, 0) EXTERN
+char *strcat(char* dst, const char *src);
+
+EXTERN
+void call_strcat(char *dst, const char *src) {
+  strcat(dst, src);
+  // CHECK-LABEL: define void @call_strcat
+  // CHECK: [[REG:%[0-9]+]] = call i64 @llvm.objectsize.i64.p0i8(i8*{{.*}}, i1 false, i1 true, i1 false)
+  // CHECK: call i8* @__strcat_chk(i8* {{.*}}, i8* {{.*}}, i64 [[REG]])
+}
+
+FSL(0, 0) EXTERN
+char *strcpy(char* dst, const char *src);
+
+EXTERN
+void call_strcpy(char *dst, const char *src) {
+  strcpy(dst, src);
+  // CHECK-LABEL: define void @call_strcpy
+  // CHECK: [[REG:%[0-9]+]] = call i64 @llvm.objectsize.i64.p0i8(i8*{{.*}}, i1 false, i1 true, i1 false)
+  // CHECK: call i8* @__strcpy_chk(i8* {{.*}}, i8* {{.*}}, i64 [[REG]])
+}
+
+FSL(0, 0) EXTERN
+size_t strlcat(char* dst, const char *src, size_t len);
+
+EXTERN
+void call_strlcat(char *dst, const char *src, size_t len) {
+  strlcat(dst, src, len);
+  // CHECK-LABEL: define void @call_strlcat
+  // CHECK: [[REG:%[0-9]+]] = call i64 @llvm.objectsize.i64.p0i8(i8*{{.*}}, i1 false, i1 true, i1 false)
+  // CHECK: call i64 @__strlcat_chk(i8* {{.*}}, i8* {{.*}}, i64 {{.*}}, i64 [[REG]])
+}
+
+FSL(0, 0) EXTERN
+size_t strlcpy(char* dst, const char *src, size_t len);
+
+EXTERN
+void call_strlcpy(char *dst, const char *src, size_t len) {
+  strlcpy(dst, src, len);
+  // CHECK-LABEL: define void @call_strlcpy
+  // CHECK: [[REG:%[0-9]+]] = call i64 @llvm.objectsize.i64.p0i8(i8*{{.*}}, i1 false, i1 true, i1 false)
+  // CHECK: call i64 @__strlcpy_chk(i8* {{.*}}, i8* {{.*}}, i64 {{.*}}, i64 [[REG]])
+}
+
+FSL(0, 0) EXTERN
+char *strncat(char* dst, const char *src, size_t len);
+
+EXTERN
+void call_strncat(char *dst, const char *src, size_t len) {
+  strncat(dst, src, len);
+  // CHECK-LABEL: define void @call_strncat
+  // CHECK: [[REG:%[0-9]+]] = call i64 @llvm.objectsize.i64.p0i8(i8*{{.*}}, i1 false, i1 true, i1 false)
+  // CHECK: call i8* @__strncat_chk(i8* {{.*}}, i8* {{.*}}, i64 {{.*}}, i64 [[REG]])
+}
+
+FSL(0, 0) EXTERN
+char *strncpy(char* dst, const char *src, size_t len);
+
+EXTERN
+void call_strncpy(char *dst, const char *src, size_t len) {
+  strncpy(dst, src, len);
+  // CHECK-LABEL: define void @call_strncpy
+  // CHECK: [[REG:%[0-9]+]] = call i64 @llvm.objectsize.i64.p0i8(i8*{{.*}}, i1 false, i1 true, i1 false)
+  // CHECK: call i8* @__strncpy_chk(i8* {{.*}}, i8* {{.*}}, i64 {{.*}}, i64 [[REG]])
+}
+
+FSL(0, 0) EXTERN
+char *stpncpy(char* dst, const char *src, size_t len);
+
+EXTERN
+void call_stpncpy(char *dst, const char *src, size_t len) {
+  stpncpy(dst, src, len);
+  // CHECK-LABEL: define void @call_stpncpy
+  // CHECK: [[REG:%[0-9]+]] = call i64 @llvm.objectsize.i64.p0i8(i8*{{.*}}, i1 false, i1 true, i1 false)
+  // CHECK: call i8* @__stpncpy_chk(i8* {{.*}}, i8* {{.*}}, i64 {{.*}}, i64 [[REG]])
+}
+
+FSL(0, 0) EXTERN
+int snprintf(char *buf, size_t n, const char *fmt, ...);
+
+EXTERN
+void call_snprintf(char *buf, size_t n, const char *fmt) {
+  snprintf(buf, n, fmt);
+  // CHECK-LABEL: define void @call_snprintf
+  // CHECK: [[REG:%[0-9]+]] = call i64 @llvm.objectsize.i64.p0i8(i8*{{.*}}, i1 false, i1 true, i1 false)
+  // CHECK: call i32 (i8*, i64, i32, i64, i8*, ...) @__snprintf_chk(i8* {{.*}}, i64 {{.*}}, i32 0, i64 [[REG]]
+}
+
+FSL(0, 0) EXTERN
+int vsnprintf(char *buf, size_t n, const char *fmt, __builtin_va_list lst);
+
+EXTERN
+void call_vsnprintf(char *buf, size_t n, const char *fmt, __builtin_va_list lst) {
+  vsnprintf(buf, n, fmt, lst);
+  // CHECK-LABEL: define void @call_vsnprintf
+  // CHECK: [[REG:%[0-9]+]] = call i64 @llvm.objectsize.i64.p0i8(i8*{{.*}}, i1 false, i1 true, i1 false)
+  // CHECK: call i32 @__vsnprintf_chk(i8* {{.*}}, i64 {{.*}}, i32 0, i64 [[REG]]
+}
+
+FSL(0,0) EXTERN
+int sprintf(char *buf, const char *fmt, ...);
+
+void call_sprintf(char *buf, const char* fmt) {
+  sprintf(buf, fmt);
+  // CHECK: [[REG:%[0-9]+]] = call i64 @llvm.objectsize.i64.p0i8(i8*{{.*}}, i1 false, i1 true, i1 false)
+  // CHECK: call i32 (i8*, i32, i64, i8*, ...) @__sprintf_chk(i8* {{.*}}, i32 0, i64 [[REG]]
+  sprintf(buf, fmt, 1, 2, 3);
+  // CHECK: [[REG:%[0-9]+]] = call i64 @llvm.objectsize.i64.p0i8(i8*{{.*}}, i1 false, i1 true, i1 false)
+  // CHECK: call i32 (i8*, i32, i64, i8*, ...) @__sprintf_chk(i8* {{.*}}, i32 0, i64 [[REG]], i8* {{.*}}, i32 1, i32 2, i32 3)
+}
+
+FSL(0, 0) EXTERN
+int vsprintf(char *buf, const char *fmt, __builtin_va_list lst);
+
+EXTERN
+void call_vsprintf(char *buf, const char *fmt, __builtin_va_list lst) {
+  vsprintf(buf, fmt, lst);
+  // CHECK-LABEL: define void @call_vsprintf
+  // CHECK: [[REG:%[0-9]+]] = call i64 @llvm.objectsize.i64.p0i8(i8*{{.*}}, i1 false, i1 true, i1 false)
+  // CHECK: call i32 @__vsprintf_chk(i8* {{.*}}, i32 0, i64 [[REG]]
+}
+
+typedef struct {} FILE;
+
+FSL(0, 0) EXTERN
+int fprintf(FILE *file, const char *fmt, ...);
+
+EXTERN
+void call_fprintf(FILE *file, const char *fmt) {
+  fprintf(file, fmt);
+  // CHECK-LABEL: define void @call_fprintf
+  // CHECK: call i32 ({{.*}}*, i32, i8*, ...) @__fprintf_chk({{.*}}, i32 0, i8* {{.*}})
+}
+
+FSL(0, 0) EXTERN
+int vfprintf(FILE *file, const char *fmt, __builtin_va_list lst);
+
+EXTERN
+void call_vfprintf(FILE *file, const char *fmt, __builtin_va_list lst) {
+  vfprintf(file, fmt, lst);
+  // CHECK-LABEL: define void @call_vfprintf
+  // CHECK: call i32 @__vfprintf_chk({{.*}}, i32 0, i8* {{.*}}, {{.*}})
+}
+
+FSL(0, 0) EXTERN
+int printf(const char *fmt, ...);
+
+EXTERN
+void call_printf(const char *fmt) {
+  printf(fmt);
+  // CHECK-LABEL: define void @call_printf
+  // CHECK: call i32 (i32, i8*, ...) @__printf_chk(i32 0, i8* {{.*}})
+}
+
+FSL(0, 0) EXTERN
+int vprintf(const char *fmt, __builtin_va_list lst);
+
+EXTERN
+void call_vprintf(const char *fmt, __builtin_va_list lst) {
+  vprintf(fmt, lst);
+  // CHECK-LABEL: define void @call_vprintf
+  // CHECK: call i32 @__vprintf_chk(i32 0, {{.*}})
+}
+
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -6419,6 +6419,31 @@
   handleSimpleAttribute<ObjCExternallyRetainedAttr>(S, D, AL);
 }
 
+static void handleFortifyStdLib(Sema &S, Decl *D, const ParsedAttr &AL) {
+  auto *FD = cast<FunctionDecl>(D);
+  unsigned VariantID = Builtin::getFortifiedVariantFunction(FD->getBuiltinID());
+  if (VariantID == 0) {
+    S.Diag(D->getLocation(), diag::err_fortify_std_lib_bad_decl);
+    return;
+  }
+
+  uint32_t BOSType, Flag;
+  if (!checkUInt32Argument(S, AL, AL.getArgAsExpr(0), BOSType, 0, true) ||
+      !checkUInt32Argument(S, AL, AL.getArgAsExpr(1), Flag, 1, true))
+    return;
+
+  if (BOSType > 3) {
+    S.Diag(AL.getArgAsExpr(0)->getBeginLoc(),
+           diag::err_attribute_argument_outof_range)
+        << AL << 0 << 3;
+    return;
+  }
+
+  D->addAttr(::new (S.getASTContext()) FortifyStdLibAttr(
+      AL.getLoc(), S.getASTContext(), BOSType, Flag,
+      AL.getAttributeSpellingListIndex()));
+}
+
 //===----------------------------------------------------------------------===//
 // Top Level Sema Entry Points
 //===----------------------------------------------------------------------===//
@@ -7148,6 +7173,10 @@
   case ParsedAttr::AT_ObjCExternallyRetained:
     handleObjCExternallyRetainedAttr(S, D, AL);
     break;
+
+  case ParsedAttr::AT_FortifyStdLib:
+    handleFortifyStdLib(S, D, AL);
+    break;
   }
 }
 
Index: clang/lib/CodeGen/CGBuiltin.cpp
===================================================================
--- clang/lib/CodeGen/CGBuiltin.cpp
+++ clang/lib/CodeGen/CGBuiltin.cpp
@@ -1487,6 +1487,131 @@
                                                Result.Val.getFloat()));
   }
 
+  if (auto *Attr = FD->getAttr<FortifyStdLibAttr>()) {
+    SmallVector<llvm::Value *, 8> ArgVals;
+    for (const Expr *Arg : E->arguments())
+      ArgVals.push_back(EmitScalarExpr(Arg));
+
+    llvm::Value *Flag = llvm::ConstantInt::get(IntTy, Attr->getFlag());
+    auto emitObjSize = [&]() -> llvm::Value * {
+      return evaluateOrEmitBuiltinObjectSize(E->getArg(0), Attr->getType(),
+                                             SizeTy, ArgVals[0], false);
+    };
+
+    unsigned FortifiedVariantID =
+        Builtin::getFortifiedVariantFunction(BuiltinID);
+    assert(FortifiedVariantID != 0 && "Should be diagnosed in Sema");
+
+    // Adjust ArgVals to include a __builtin_object_size(n) or flag argument at
+    // the right position.
+    switch (BuiltinID) {
+    case Builtin::BImemcpy:
+      // void *memcpy(void *dst, const void *src, size_t len);
+      // void *__memcpy_chk(void *dst, const void *src, size_t len, size_t);
+    case Builtin::BImemmove:
+      // void *memmove(void *dst, const void *src, size_t len);
+      // void *___memmove_chk(void *dst, const void *src, size_t len, size_t);
+    case Builtin::BImemset:
+      // void *memset(void *b, int c, size_t len);
+      // void *__memset_chk(void *b, int c, size_t len, size_t);
+      ArgVals.push_back(emitObjSize());
+      break;
+
+    case Builtin::BIstpcpy:
+      // char *stpcpy(char *dst, const char *src);
+      // char *__stpcpy_chk(char *dst, const char *src, size_t);
+    case Builtin::BIstrcat:
+      // char *strcat(char *s1, const char *s2);
+      // char *__strcat_chk(char *s1, const char *s2, size_t);
+    case Builtin::BIstrcpy:
+      // char *strcpy(char *dst, const char *src);
+      // char *__strcpy_chk(char *dst, const char *src, size_t);
+      ArgVals.push_back(emitObjSize());
+      break;
+
+    case Builtin::BIstrlcat:
+      // size_t strlcat(char *dst, const char *source, size_t size);
+      // size_t __strlcat_chk(char *dst, const char *source, size_t size,
+      //                      size_t);
+    case Builtin::BIstrlcpy:
+      // size_t strlcpy(char *dst, const char *source, size_t size);
+      // size_t __strlcpy_chk(char *dst, const char *source, size_t size,
+      //                      size_t);
+    case Builtin::BIstrncat:
+      // char *strncat(char *s1, const char *s2, size_t n);
+      // char *__strncat_chk(char *s1, const char *s2, size_t n, size_t);
+    case Builtin::BIstrncpy:
+      // char *strncpy(char *dst, const char *src, size_t n);
+      // char *__strncpy_chk(char *dst, const char *src, size_t n, size_t);
+    case Builtin::BIstpncpy:
+        // char *stpncpy(char *dst, const char *src, size_t n);
+        // char *__stpncpy_chk(char *dst, const char *src, size_t n, size_t);
+      ArgVals.push_back(emitObjSize());
+      break;
+
+
+    case Builtin::BIsnprintf:
+      // int snprintf(char *buf, size_t n, const char *fmt, ...);
+      // int __snprintf_chk(char *buf, size_t n, int _flag, size_t _bos,
+      //                    const char *fmt, ...);
+    case Builtin::BIvsnprintf:
+      // int vsnprintf(char *buf, size_t n, const char *fmt, va_list lst);
+      // int __vsnprintf_chk(char *buf, size_t n, int _flag, size_t _bos,
+      //                     const char *fmt, va_list lst);
+      ArgVals.insert(ArgVals.begin() + 2, Flag);
+      ArgVals.insert(ArgVals.begin() + 3, emitObjSize());
+      break;
+
+    case Builtin::BIsprintf:
+      // int sprintf(char *buf, const char *fmt, ...);
+      // int __sprintf_chk(char *buf, int _flag, size_t _bos,
+      //                   const char *fmt, ...)
+    case Builtin::BIvsprintf:
+      // int vsprintf(char *buf, const char *fmt, va_list lst);
+      // int __vsprintf_chk(char *buf, int _flag, size_t _bos, const char *fmt,
+      //                    va_list lst);
+      ArgVals.insert(ArgVals.begin() + 1, Flag);
+      ArgVals.insert(ArgVals.begin() + 2, emitObjSize());
+      break;
+
+    case Builtin::BIfprintf:
+      // int fprintf(FILE *file, const char *fmt, ...);
+      // int __fprintf_chk(FILE *file, int _flag, const char *fmt, ...);
+    case Builtin::BIvfprintf:
+      // int vfprintf(FILE *file, const char *fmt, va_list lst);
+      // int __vfprintf_chk(FILE *file, int _flag, const char *fmt,
+      //                    va_list lst);
+      ArgVals.insert(ArgVals.begin() + 1, Flag);
+      break;
+
+    case Builtin::BIprintf:
+      // int fprintf(const char *fmt, ...);
+      // int __fprintf_chk(int _flag, const char *fmt, ...);
+    case Builtin::BIvprintf:
+      // int vprintf(const char *fmt, va_list lst);
+      // int __vprintf_chk(int _flags, const char *fmt, va_list lst);
+      ArgVals.insert(ArgVals.begin(), Flag);
+      break;
+
+    default:
+      llvm_unreachable("Unknown fortified builtin?");
+    }
+
+
+    ASTContext::GetBuiltinTypeError Err;
+    QualType VariantTy = getContext().GetBuiltinType(FortifiedVariantID, Err);
+    assert(Err == ASTContext::GE_None && "Should not codegen an error");
+    llvm::FunctionType *LLVMVariantTy =
+        cast<llvm::FunctionType>(ConvertType(VariantTy));
+    StringRef VariantName =
+        getContext().BuiltinInfo.getName(FortifiedVariantID) +
+        strlen("__builtin_");
+
+    llvm::Value *V = Builder.CreateCall(
+        CGM.CreateRuntimeFunction(LLVMVariantTy, VariantName), ArgVals);
+    return RValue::get(V);
+  }
+
   // There are LLVM math intrinsics/instructions corresponding to math library
   // functions except the LLVM op will never set errno while the math library
   // might. Also, math builtins have the same semantics as their math library
Index: clang/lib/Basic/Builtins.cpp
===================================================================
--- clang/lib/Basic/Builtins.cpp
+++ clang/lib/Basic/Builtins.cpp
@@ -188,3 +188,28 @@
          (!hasReferenceArgsOrResult(ID) &&
           !hasCustomTypechecking(ID));
 }
+
+unsigned Builtin::getFortifiedVariantFunction(unsigned BuiltinID) {
+  switch (BuiltinID) {
+  case Builtin::BImemcpy:    return Builtin::BI__builtin___memcpy_chk;
+  case Builtin::BImemmove:   return Builtin::BI__builtin___memmove_chk;
+  case Builtin::BImemset:    return Builtin::BI__builtin___memset_chk;
+  case Builtin::BIstpcpy:    return Builtin::BI__builtin___stpcpy_chk;
+  case Builtin::BIstrcat:    return Builtin::BI__builtin___strcat_chk;
+  case Builtin::BIstrcpy:    return Builtin::BI__builtin___strcpy_chk;
+  case Builtin::BIstrlcat:   return Builtin::BI__builtin___strlcat_chk;
+  case Builtin::BIstrlcpy:   return Builtin::BI__builtin___strlcpy_chk;
+  case Builtin::BIstrncat:   return Builtin::BI__builtin___strncat_chk;
+  case Builtin::BIstrncpy:   return Builtin::BI__builtin___strncpy_chk;
+  case Builtin::BIstpncpy:   return Builtin::BI__builtin___stpncpy_chk;
+  case Builtin::BIsnprintf:  return Builtin::BI__builtin___snprintf_chk;
+  case Builtin::BIvsnprintf: return Builtin::BI__builtin___vsnprintf_chk;
+  case Builtin::BIsprintf:   return Builtin::BI__builtin___sprintf_chk;
+  case Builtin::BIvsprintf:  return Builtin::BI__builtin___vsprintf_chk;
+  case Builtin::BIfprintf:   return Builtin::BI__builtin___fprintf_chk;
+  case Builtin::BIvfprintf:  return Builtin::BI__builtin___vfprintf_chk;
+  case Builtin::BIprintf:    return Builtin::BI__builtin___printf_chk;
+  case Builtin::BIvprintf:   return Builtin::BI__builtin___vprintf_chk;
+  default: return 0;
+  }
+}
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4818,6 +4818,8 @@
 def ext_forward_ref_enum_def : Extension<
   "redeclaration of already-defined enum %0 is a GNU extension">,
   InGroup<GNURedeclaredEnum>;
+def err_fortify_std_lib_bad_decl : Error<
+  "'fortify_stdlib' attribute applied to an unknown function">;
 
 def err_redefinition_of_enumerator : Error<"redefinition of enumerator %0">;
 def err_duplicate_member : Error<"duplicate member %0">;
Index: clang/include/clang/Basic/Builtins.h
===================================================================
--- clang/include/clang/Basic/Builtins.h
+++ clang/include/clang/Basic/Builtins.h
@@ -242,7 +242,13 @@
               const char *Fmt) const;
 };
 
-}
+/// For a given BuiltinID, return the ID of the fortified variant function. For
+/// instance, if Builtin::BIsprintf is passed, this function will return
+/// Builtin::BI__builtin___sprintf_chk. If BuiltinID doesn't have a fortified
+/// variant, 0 is returned.
+unsigned getFortifiedVariantFunction(unsigned BuiltinID);
+
+} // end namespace Builtin
 
 /// Kinds of BuiltinTemplateDecl.
 enum BuiltinTemplateKind : int {
Index: clang/include/clang/Basic/AttrDocs.td
===================================================================
--- clang/include/clang/Basic/AttrDocs.td
+++ clang/include/clang/Basic/AttrDocs.td
@@ -965,6 +965,27 @@
   }];
 }
 
+def FortifyStdLibDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+The ``fortify_stdlib`` attribute applies to declarations of C stdlib functions
+(memcpy, sprintf, ...), and causes clang to emit calls to their fortified
+variants with ``__builtin_object_size``. For instance:
+
+.. code-block:: c
+
+  __attribute__((fortify_stdlib(0, 0)))
+  int sprintf(char *buf, const char *fmt, ...);
+
+  int main() {
+    char buf[5];
+    sprintf(buf, "%f", 42.0);
+    // Clang generates code equivalent to:
+    // __sprintf_chk(buf, 0, __builtin_object_size(0), "%f", 42.0);
+  }
+  }];
+}
+
 def ConvergentDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{
Index: clang/include/clang/Basic/Attr.td
===================================================================
--- clang/include/clang/Basic/Attr.td
+++ clang/include/clang/Basic/Attr.td
@@ -1567,6 +1567,13 @@
   let Documentation = [PassObjectSizeDocs];
 }
 
+def FortifyStdLib : InheritableAttr {
+  let Spellings = [Clang<"fortify_stdlib">];
+  let Args = [IntArgument<"Type">, IntArgument<"Flag">];
+  let Subjects = SubjectList<[Function]>;
+  let Documentation = [FortifyStdLibDocs];
+}
+
 // Nullability type attributes.
 def TypeNonNull : TypeAttr {
   let Spellings = [Keyword<"_Nonnull">];
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to