Revert "Add a new attribute, fortify_stdlib"

This reverts commit r353765. After talking with our c stdlib folks, we decided
to use the existing pass_object_size attribute to implement _FORTIFY_SOURCE
wrappers, like Bionic does (I didn't realize that pass_object_size could be used
for this purpose). Sorry for the flip/flop, and thanks to James Y. Knight for
pointing this out to me.

llvm-svn: 356103
This commit is contained in:
Erik Pilkington 2019-03-13 21:37:01 +00:00
parent a688a42cdd
commit 02886e5476
11 changed files with 1 additions and 434 deletions

View File

@ -1566,13 +1566,6 @@ def PassObjectSize : InheritableParamAttr {
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">];

View File

@ -965,43 +965,6 @@ of the condition.
}];
}
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``. This attribute is intended for use
within standard C library implementations and should not generally be used for
user applications. 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);
}
The first argument to the attribute is the integer `type` argument passed to
`__builtin_object_size` (you can read more about `__builtin_object_size`
`here <https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/Object-Size-Checking.html>`_),
The second argument to this attribute is the flag that the fortified format
functions accept.
Only a specific set of standard library functions are supported:
- memcpy, memmove, memset
- stpcpy, strcat, strcpy
- strlcat, strlcpy
- strncat, strncpy, stpncpy
- snprintf, vsnprintf, sprintf, vsprintf
- fprintf, vfprintf, printf, vprintf
}];
}
def ConvergentDocs : Documentation {
let Category = DocCatFunction;
let Content = [{

View File

@ -242,13 +242,7 @@ private:
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 {

View File

@ -4826,8 +4826,6 @@ def ext_ms_forward_ref_enum : ExtWarn<
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">;

View File

@ -188,28 +188,3 @@ bool Builtin::Context::canBeRedeclared(unsigned ID) const {
(!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;
}
}

View File

@ -1474,86 +1474,6 @@ RValue CodeGenFunction::emitRotate(const CallExpr *E, bool IsRotateRight) {
return RValue::get(Builder.CreateCall(F, { Src, Src, ShiftAmt }));
}
/// For a call to a builtin C standard library function, emit a call to a
/// fortified variant using __builtin_object_size. For instance, instead of
/// emitting `sprintf(buf, "%d", 32)`, this function would emit
/// `__sprintf_chk(buf, Flag, __builtin_object_size(buf, 0), "%d", 32)`.
RValue CodeGenFunction::emitFortifiedStdLibCall(CodeGenFunction &CGF,
const CallExpr *CE,
unsigned BuiltinID,
unsigned BOSType,
unsigned Flag) {
SmallVector<llvm::Value *, 8> ArgVals;
for (const Expr *Arg : CE->arguments())
ArgVals.push_back(EmitScalarExpr(Arg));
llvm::Value *FlagVal = llvm::ConstantInt::get(IntTy, Flag);
auto emitObjSize = [&]() {
return evaluateOrEmitBuiltinObjectSize(CE->getArg(0), BOSType, 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. Variadic printf-like functions take a flag and object
// size (if they're printing to a string) before the format string, and all
// other functions just take the object size as their last argument. The
// object size, if present, always corresponds to the first argument.
switch (BuiltinID) {
case Builtin::BImemcpy:
case Builtin::BImemmove:
case Builtin::BImemset:
case Builtin::BIstpcpy:
case Builtin::BIstrcat:
case Builtin::BIstrcpy:
case Builtin::BIstrlcat:
case Builtin::BIstrlcpy:
case Builtin::BIstrncat:
case Builtin::BIstrncpy:
case Builtin::BIstpncpy:
ArgVals.push_back(emitObjSize());
break;
case Builtin::BIsnprintf:
case Builtin::BIvsnprintf:
ArgVals.insert(ArgVals.begin() + 2, FlagVal);
ArgVals.insert(ArgVals.begin() + 3, emitObjSize());
break;
case Builtin::BIsprintf:
case Builtin::BIvsprintf:
ArgVals.insert(ArgVals.begin() + 1, FlagVal);
ArgVals.insert(ArgVals.begin() + 2, emitObjSize());
break;
case Builtin::BIfprintf:
case Builtin::BIvfprintf:
ArgVals.insert(ArgVals.begin() + 1, FlagVal);
break;
case Builtin::BIprintf:
case Builtin::BIvprintf:
ArgVals.insert(ArgVals.begin(), FlagVal);
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");
auto *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);
}
RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
const CallExpr *E,
ReturnValueSlot ReturnValue) {
@ -1570,10 +1490,6 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
Result.Val.getFloat()));
}
if (const auto *FortifyAttr = FD->getAttr<FortifyStdLibAttr>())
return emitFortifiedStdLibCall(*this, E, BuiltinID, FortifyAttr->getType(),
FortifyAttr->getFlag());
// 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

View File

@ -3697,10 +3697,6 @@ public:
RValue EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
const CallExpr *E, ReturnValueSlot ReturnValue);
RValue emitFortifiedStdLibCall(CodeGenFunction &CGF, const CallExpr *CE,
unsigned BuiltinID, unsigned BOSType,
unsigned Flag);
RValue emitRotate(const CallExpr *E, bool IsRotateRight);
/// Emit IR for __builtin_os_log_format.

View File

@ -6524,31 +6524,6 @@ static void handleObjCExternallyRetainedAttr(Sema &S, Decl *D,
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_out_of_range)
<< AL << 0 << 3;
return;
}
D->addAttr(::new (S.getASTContext()) FortifyStdLibAttr(
AL.getLoc(), S.getASTContext(), BOSType, Flag,
AL.getAttributeSpellingListIndex()));
}
static void handleMIGServerRoutineAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
// Check that the return type is a `typedef int kern_return_t` or a typedef
// around it, because otherwise MIG convention checks make no sense.
@ -7301,10 +7276,6 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
handleObjCExternallyRetainedAttr(S, D, AL);
break;
case ParsedAttr::AT_FortifyStdLib:
handleFortifyStdLib(S, D, AL);
break;
case ParsedAttr::AT_MIGServerRoutine:
handleMIGServerRoutineAttr(S, D, AL);
break;

View File

@ -1,220 +0,0 @@
// 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, {{.*}})
}

View File

@ -52,7 +52,6 @@
// 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)

View File

@ -1,18 +0,0 @@
// 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_anything_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 *, ...);