Skip to content

Commit

Permalink
Improve diagnostic checking for va_start to also warn on other instan…
Browse files Browse the repository at this point in the history
…ces of undefined behavior, such as a parameter declared with the register keyword in C, or a parameter of a type that undergoes default argument promotion.

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).


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@267338 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
AaronBallman committed Apr 24, 2016
1 parent 102c3e1 commit 2340dc0
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 19 deletions.
6 changes: 4 additions & 2 deletions include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -7435,8 +7435,10 @@ def err_ms_va_start_used_in_sysv_function : Error<
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<
"passing %select{an object that undergoes default argument promotion|"
"an object of reference type|a parameter declared with the 'register' "
"keyword}0 to 'va_start' has undefined behavior">, 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<
Expand Down
13 changes: 10 additions & 3 deletions lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2702,6 +2702,7 @@ bool Sema::SemaBuiltinVAStartImpl(CallExpr *TheCall) {
// block.
QualType Type;
SourceLocation ParamLoc;
bool IsCRegister = false;

if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Arg)) {
if (const ParmVarDecl *PV = dyn_cast<ParmVarDecl>(DR->getDecl())) {
Expand All @@ -2718,15 +2719,21 @@ bool Sema::SemaBuiltinVAStartImpl(CallExpr *TheCall) {

Type = PV->getType();
ParamLoc = PV->getLocation();
IsCRegister =
PV->getStorageClass() == SC_Register && !getLangOpts().CPlusPlus;
}
}

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 (IsCRegister || Type->isReferenceType() ||
Type->isPromotableIntegerType() ||
Type->isSpecificBuiltinType(BuiltinType::Float)) {
unsigned Reason = 0;
if (Type->isReferenceType()) Reason = 1;
else if (IsCRegister) Reason = 2;
Diag(Arg->getLocStart(), diag::warn_va_start_type_is_undefined) << Reason;
Diag(ParamLoc, diag::note_parameter_type) << Type;
}

Expand Down
6 changes: 3 additions & 3 deletions test/Sema/varargs-x86-64.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ void __attribute__((ms_abi)) g2(int a, int b, ...) {
__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 {{passing an object that undergoes default argument promotion to 'va_start' has undefined behavior}}
__builtin_ms_va_start(ap, (a)); // expected-warning {{passing an object that undergoes default argument promotion to 'va_start' has undefined behavior}}
}

void __attribute__((ms_abi)) g5() {
Expand Down
19 changes: 15 additions & 4 deletions test/Sema/varargs.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@ void f2(int a, int b, ...)
__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 {{passing an object that undergoes default argument promotion to 'va_start' has undefined behavior}}
__builtin_va_start(ap, (a)); // expected-warning {{passing an object that undergoes default argument promotion to 'va_start' has undefined behavior}}
}


Expand Down Expand Up @@ -83,3 +82,15 @@ void f10(int a, ...) {
i = __builtin_va_start(ap, a); // expected-error {{assigning to 'int' from incompatible type 'void'}}
__builtin_va_end(ap);
}

void f11(short s, ...) { // expected-note {{parameter of type 'short' is declared here}}
__builtin_va_list ap;
__builtin_va_start(ap, s); // expected-warning {{passing an object that undergoes default argument promotion to 'va_start' has undefined behavior}}
__builtin_va_end(ap);
}

void f12(register int i, ...) { // expected-note {{parameter of type 'int' is declared here}}
__builtin_va_list ap;
__builtin_va_start(ap, i); // expected-warning {{passing a parameter declared with the 'register' keyword to 'va_start' has undefined behavior}}
__builtin_va_end(ap);
}
7 changes: 0 additions & 7 deletions test/Sema/varargs.cpp

This file was deleted.

12 changes: 12 additions & 0 deletions test/SemaCXX/varargs.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// RUN: %clang_cc1 -fsyntax-only -std=c++03 -verify %s

class string;
void f(const string& s, ...) { // expected-note {{parameter of type 'const string &' is declared here}}
__builtin_va_list ap;
__builtin_va_start(ap, s); // expected-warning {{passing an object of reference type to 'va_start' has undefined behavior}}
}

void g(register int i, ...) {
__builtin_va_list ap;
__builtin_va_start(ap, i); // okay
}

0 comments on commit 2340dc0

Please sign in to comment.