aaron.ballman created this revision. aaron.ballman added reviewers: rsmith, dblaikie. aaron.ballman added a subscriber: cfe-commits. Herald added a subscriber: aemerson.
The va_start macro currently diagnoses passing a reference as the second argument (the one representing the parameter before the ellipsis) because this is undefined behavior in C++. This patch extends the checking to cover additional cases of undefined behavior in both C and C++. The C11 Standard, 7.16.1.4p4 states, in part: If the parameter parmN is declared with the register storage class, with a function or array type, or with a type that is not compatible with the type that results after application of the default argument promotions, the behavior is undefined. (This is picked up by reference in C++ under [support.runtime]p3.) This patch adds a check for default argument promotions as well as parameters declared with the register storage class. This helps cover some more of the CERT secure coding rule EXP58-CPP. Pass an object of the correct type to va_start (https://www.securecoding.cert.org/confluence/display/cplusplus/EXP58-CPP.+Pass+an+object+of+the+correct+type+to+va_start). http://reviews.llvm.org/D19244 Files: include/clang/Basic/DiagnosticSemaKinds.td lib/Sema/SemaChecking.cpp test/Sema/varargs-x86-64.c test/Sema/varargs.c
Index: test/Sema/varargs.c =================================================================== --- test/Sema/varargs.c +++ test/Sema/varargs.c @@ -18,12 +18,11 @@ __builtin_va_start(ap, b); } -void f3(float a, ...) -{ +void f3(float a, ...) { // expected-note 2{{parameter of type 'float' is declared here}} __builtin_va_list ap; - __builtin_va_start(ap, a); - __builtin_va_start(ap, (a)); + __builtin_va_start(ap, a); // expected-warning {{'va_start' has undefined behavior with types that undergo default argument promotion}} + __builtin_va_start(ap, (a)); // expected-warning {{'va_start' has undefined behavior with types that undergo default argument promotion}} } @@ -83,3 +82,21 @@ i = __builtin_va_start(ap, a); // expected-error {{assigning to 'int' from incompatible type 'void'}} __builtin_va_end(ap); } + +void f11(float f, ...) { // expected-note {{parameter of type 'float' is declared here}} + __builtin_va_list ap; + __builtin_va_start(ap, f); // expected-warning {{'va_start' has undefined behavior with types that undergo default argument promotion}} + __builtin_va_end(ap); +} + +void f12(short s, ...) { // expected-note {{parameter of type 'short' is declared here}} + __builtin_va_list ap; + __builtin_va_start(ap, s); // expected-warning {{'va_start' has undefined behavior with types that undergo default argument promotion}} + __builtin_va_end(ap); +} + +void f13(register int i, ...) { // expected-note {{parameter of type 'int' is declared here}} + __builtin_va_list ap; + __builtin_va_start(ap, i); // expected-warning {{'va_start' has undefined behavior with an object declared with the register storage class}} + __builtin_va_end(ap); +} Index: test/Sema/varargs-x86-64.c =================================================================== --- test/Sema/varargs-x86-64.c +++ test/Sema/varargs-x86-64.c @@ -26,11 +26,11 @@ __builtin_ms_va_start(ap, b); } -void __attribute__((ms_abi)) g3(float a, ...) { +void __attribute__((ms_abi)) g3(float a, ...) { // expected-note 2{{parameter of type 'float' is declared here}} __builtin_ms_va_list ap; - __builtin_ms_va_start(ap, a); - __builtin_ms_va_start(ap, (a)); + __builtin_ms_va_start(ap, a); // expected-warning {{'va_start' has undefined behavior with types that undergo default argument promotion}} + __builtin_ms_va_start(ap, (a)); // expected-warning {{'va_start' has undefined behavior with types that undergo default argument promotion}} } void __attribute__((ms_abi)) g5() { Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -2702,6 +2702,7 @@ // block. QualType Type; SourceLocation ParamLoc; + bool IsRegister = false; if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Arg)) { if (const ParmVarDecl *PV = dyn_cast<ParmVarDecl>(DR->getDecl())) { @@ -2718,6 +2719,7 @@ Type = PV->getType(); ParamLoc = PV->getLocation(); + IsRegister = PV->getStorageClass() == SC_Register; } } @@ -2724,9 +2726,13 @@ if (!SecondArgIsLastNamedArgument) Diag(TheCall->getArg(1)->getLocStart(), diag::warn_second_arg_of_va_start_not_last_named_param); - else if (Type->isReferenceType()) { - Diag(Arg->getLocStart(), - diag::warn_va_start_of_reference_type_is_undefined); + else if (IsRegister || Type->isReferenceType() || + Type->isPromotableIntegerType() || + Type->isSpecificBuiltinType(BuiltinType::Float)) { + unsigned Reason = 0; + if (Type->isReferenceType()) Reason = 1; + else if (IsRegister) Reason = 2; + Diag(Arg->getLocStart(), diag::warn_va_start_type_is_undefined) << Reason; Diag(ParamLoc, diag::note_parameter_type) << Type; } Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -7414,8 +7414,11 @@ def warn_second_arg_of_va_start_not_last_named_param : Warning< "second argument to 'va_start' is not the last named parameter">, InGroup<Varargs>; -def warn_va_start_of_reference_type_is_undefined : Warning< - "'va_start' has undefined behavior with reference types">, InGroup<Varargs>; +def warn_va_start_type_is_undefined : Warning< + "'va_start' has undefined behavior with " + "%select{types that undergo default argument promotion|reference types|" + "an object declared with the register storage class}0">, + InGroup<Varargs>; def err_first_argument_to_va_arg_not_of_type_va_list : Error< "first argument to 'va_arg' is of type %0 and not 'va_list'">; def err_second_parameter_to_va_arg_incomplete: Error<
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits