blob: 0f20cd2c1c702444e20be486333852579aec4b16 [file] [edit]
// RUN: %clang_analyze_cc1 -triple hexagon-unknown-linux -verify %s \
// RUN: -analyzer-checker=core,optin.core.UnconditionalVAArg \
// RUN: -analyzer-output=text
//
// RUN: %clang_analyze_cc1 -triple x86_64-pc-linux-gnu -verify %s \
// RUN: -analyzer-checker=core,optin.core.UnconditionalVAArg \
// RUN: -analyzer-output=text
#include "Inputs/system-header-simulator-for-valist.h"
int printf(const char *format, ...);
void abort(void);
void log_message(const char *msg, ...) {
// This artifical but plausible example function expects that the list of
// variadic arguments is terminated by a NULL pointer. Although careful use
// of this function is well-defined, passing no variadic arguments would
// trigger undefined behavior, so it may be better to choose a different way
// to mark the count of variadic arguments.
va_list va;
const char *arg;
printf("%s\n", msg);
va_start(va, msg);
while ((arg = va_arg(va, const char *))) {
// expected-warning@-1 {{Unconditional use of va_arg()}}
// expected-note@-2 {{Calls to 'log_message' always reach this va_arg() expression, so calling 'log_message' with no variadic arguments would be undefined behavior}}
printf(" * %s\n", arg);
}
va_end(va);
}
void never_called(int fst, ...) {
// This simple test function is not called in this test file.
va_list va;
va_start(va, fst);
(void)va_arg(va, int); // expected-warning{{Unconditional use of va_arg()}}
// expected-note@-1 {{Calls to 'never_called' always reach this va_arg() expression, so calling 'never_called' with no variadic arguments would be undefined behavior}}
va_end(va);
}
void called_with_varargs(int fst, ...) {
// This function is identical to never_called(), but it is called by another
// function (which will serve as the entrypoint) and the call passes several
// variadic arguments. The diagnostic is still raised.
va_list va;
va_start(va, fst);
(void)va_arg(va, int); // expected-warning{{Unconditional use of va_arg()}}
// expected-note@-1 {{Calls to 'called_with_varargs' always reach this va_arg() expression, so calling 'called_with_varargs' with no variadic arguments would be undefined behavior}}
va_end(va);
}
void caller1(void) {
called_with_varargs(1, 2, 3, 4);
}
void called_without_varargs(int fst, ...) {
// This function is identical to never_called(), but it is called by another
// function (which will serve as the entrypoint) and the call passes no
// variadic arguments. The diagnostic is still raised.
va_list va;
va_start(va, fst);
(void)va_arg(va, int); // expected-warning{{Unconditional use of va_arg()}}
// expected-note@-1 {{Calls to 'called_without_varargs' always reach this va_arg() expression, so calling 'called_without_varargs' with no variadic arguments would be undefined behavior}}
// FIXME: Here the checker should mention that this function is _actually_
// called with no variadic arguments.
va_end(va);
}
void caller2(void) {
called_without_varargs(1);
}
void multiple_va_arg_calls(int fst, ...) {
// This function function unconditionally calls va_arg() multiple times. To
// avoid spamming the user, only the first use is reported.
va_list va;
va_start(va, fst);
(void)va_arg(va, int); // expected-warning{{Unconditional use of va_arg()}}
// expected-note@-1 {{Calls to 'multiple_va_arg_calls' always reach this va_arg() expression, so calling 'multiple_va_arg_calls' with no variadic arguments would be undefined behavior}}
(void)va_arg(va, int); // no-warning
(void)va_arg(va, int); // no-warning
va_end(va);
}
void has_conditional_return(int fst, ...) {
// The va_arg call is not always executed, so don't report this function.
// Actually understanding the conditional logic is infeasible, so the checker
// should accept any conditional logic that sometimes avoids the va_arg().
va_list va;
if (fst < 0)
return;
va_start(va, fst);
(void)va_arg(va, int); // no-warning
va_end(va);
}
void has_conditional_logic(int fst, ...) {
// As static analyzer only follows one execution path, it cannot see that
// va_arg() is always executed in this function. Theoretically it would be
// better to produce a report here, but implementing this would be a very
// ineffective use of development effort.
va_list va;
if (fst < 0)
fst = -fst;
va_start(va, fst);
(void)va_arg(va, int); // no-warning
va_end(va);
}
#define IS_FEATURE_ENABLED 1
void has_constant_condition(int fst, ...) {
// This checker doesn't have special logic for recognizing and ignoring
// conditionals that can be resolved at compile time. This feature could be
// added in the future.
va_list va;
if (IS_FEATURE_ENABLED) {
va_start(va, fst);
(void)va_arg(va, int); // no-warning
va_end(va);
}
}
void caller_has_conditional_logic(int fst, ...) {
// This function is identical to never_called(), but it is used by a function
// that performs a state split before the call.
// This test validates that state splits _before_ the variadic call (which
// are irrelevant) do not influence the report creation. This is basically a
// workaround for the fact that not all functions are entrypoints and before
// reaching "our" variadic function the analyzer may go through arbitrary
// irrelevant code.
va_list va;
va_start(va, fst);
(void)va_arg(va, int); // expected-warning{{Unconditional use of va_arg()}}
// expected-note@-1 {{Calls to 'caller_has_conditional_logic' always reach this va_arg() expression, so calling 'caller_has_conditional_logic' with no variadic arguments would be undefined behavior}}
va_end(va);
}
void caller_with_conditional_logic(int flag) {
if (flag)
caller_has_conditional_logic(1, 2, 3);
}
int has_shortcircuiting_operator(int fst, ...) {
// In addition to 'if' and similar statements, ternary operators and
// shortcircuiting logical operators can also ensure that the use of va_arg()
// is not unconditional. (These all behave identically in the BranchCondition
// callback, so this simple testcase is mostly for documentation purposes.)
va_list va;
int x;
va_start(va, fst);
x = fst || va_arg(va, int); // no-warning
va_end(va);
return x;
}
void validate_argument(int fst) {
if (fst <= 0) {
printf("Negative first argument is illegal: %d!\n", fst);
abort();
}
}
void noreturn_in_function(int fst, ...) {
// This function calls a function which may call the noreturn function
// abort() before reaching the va_arg() call, so the checker doesn't produce
// a warning. This behavior is important e.g. for cases when a function uses
// assertions to mark preconditions.
va_list va;
validate_argument(fst);
va_start(va, fst);
(void)va_arg(va, int); // no-warning
va_end(va);
}
void print_negative(int fst) {
if (fst <= 0) {
printf("First argument is negative: %d!\n", fst);
}
}
void conditional_in_function(int fst, ...) {
// This function is very similar to noreturn_in_function(), but the called
// function always returns. However, the analyzer doesn't see this difference
// (because it follows a _single_ execution path and cannot see the
// alternative) so the checker behaves as in noreturn_in_function.
va_list va;
print_negative(fst);
va_start(va, fst);
(void)va_arg(va, int); // no-warning
va_end(va);
}
void defined_in_different_tu(int);
void unknown_call(int fst, ...) {
// This function is very similar to noreturn_in_function() or
// conditional_in_function() but the definition of the called function is not
// visible for the analyzer. In theis situation the analyzer ignores the call
// and reports the va_arg() expression that unconditionally appears after it.
// This would be incorrect behavior only in a situation when the called
// function can return _and_ can be noreturn which should be fairly rare.
va_list va;
defined_in_different_tu(fst);
va_start(va, fst);
(void)va_arg(va, int); // expected-warning{{Unconditional use of va_arg()}}
// expected-note@-1 {{Calls to 'unknown_call' always reach this va_arg() expression, so calling 'unknown_call' with no variadic arguments would be undefined behavior}}
va_end(va);
}
void trivial_variadic(int fst, ...) {
// Do nothing.
}
void calls_other_variadic(int fst, ...) {
// As the 'HasUnconditionalPath' can "remember" only one variadic function,
// I would expect that the presence of 'trivial_variadic' would prevent the
// checker from reporting the unconditional use of va_arg() in this function.
// However, for some unclear reason the checker is still able to produce this
// (true positive) report.
va_list va;
trivial_variadic(fst, 2);
va_start(va, fst);
(void)va_arg(va, int); // expected-warning{{Unconditional use of va_arg()}}
// expected-note@-1 {{Calls to 'calls_other_variadic' always reach this va_arg() expression, so calling 'calls_other_variadic' with no variadic arguments would be undefined behavior}}
va_end(va);
}
int get_int_from_va_list(va_list *va) {
// This checker can report va_arg() expressions that are unconditionally
// reached from a variadic function, even if they are located in a different
// function. In this case the originating variadic function is marked with a
// note (to make it easier to find).
return va_arg(*va, int); // expected-warning{{Unconditional use of va_arg()}}
// expected-note@-1 {{Calls to 'uses_wrapped_va_arg' always reach this va_arg() expression, so calling 'uses_wrapped_va_arg' with no variadic arguments would be undefined behavior}}
}
void uses_wrapped_va_arg(int fst, ...) {
// expected-note@-1 {{Variadic function 'uses_wrapped_va_arg' is defined here}}
va_list va;
va_start(va, fst);
(void)get_int_from_va_list(&va);
va_end(va);
}
int standalone_non_variadic(va_list *va) {
// A va_arg() expression is only reported if it is unconditionally reached
// from the beginning of a _variadic_ function. Functions that use va_arg()
// on a va_list obtained from other sources (e.g. as an argument) are
// presumed to be small helper subroutines of a complex variadic function, so
// we do not report cases where the va_arg() is unconditionally reached from
// the beginning of a _non-variadic_ function.
return va_arg(*va, int); //no-warning
}
int variadic_but_also_takes_va_list(va_list *va, ...) {
// The checker just looks for va_arg() calls without checking the origin of
// their argument, so in this (very artifical) example it produces a
// result that is arguably a false positive.
return va_arg(*va, int); // expected-warning{{Unconditional use of va_arg()}}
// expected-note@-1 {{Calls to 'variadic_but_also_takes_va_list' always reach this va_arg() expression, so calling 'variadic_but_also_takes_va_list' with no variadic arguments would be undefined behavior}}
}