Author: DonĂ¡t Nagy Date: 2026-02-23T12:44:58+01:00 New Revision: 36d60b16df50e648f21196f25e5f87dad8659909
URL: https://github.com/llvm/llvm-project/commit/36d60b16df50e648f21196f25e5f87dad8659909 DIFF: https://github.com/llvm/llvm-project/commit/36d60b16df50e648f21196f25e5f87dad8659909.diff LOG: [analyzer] New checker: optin.core.UnconditionalVAArg (#175602) Add a new optin checker which reports variadic functions that unconditionally use `va_arg()`. It would be undefined behavior to call such functions without passing any variadic arguments, so the SEI-CERT rule EXP47-C says that this pattern should be avoided. As this part of this SEI-CERT rule focuses on a very narrow area, I aimed to write a simple and stable checker that can report basic "clear" occurrences of this fault. It would be possible to cover some additional corner cases, but it isn't worth the effort. Added: clang/lib/StaticAnalyzer/Checkers/UnconditionalVAArgChecker.cpp clang/test/Analysis/unconditional-va_arg.c Modified: clang/docs/analyzer/checkers.rst clang/include/clang/StaticAnalyzer/Checkers/Checkers.td clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt clang/lib/StaticAnalyzer/Checkers/VAListChecker.cpp llvm/utils/gn/secondary/clang/lib/StaticAnalyzer/Checkers/BUILD.gn Removed: ################################################################################ diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst index 87a5cb5cab103..d209a5d35132f 100644 --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -903,6 +903,49 @@ reported. Do not use the :ref:`address_space <langext-address_space_documentation>` attribute to suppress the reports - it just happens so that the checker also doesn't raise issues if the attribute is present. +.. _optin-core-UnconditionalVAArg: + +optin.core.UnconditionalVAArg (C, C++) +"""""""""""""""""""""""""""""""""""""" +Check for variadic functions that unconditionally use ``va_arg()``. It is +possible to use such functions safely (as long as they always receive at least +one variadic argument), but their careless use can lead to undefined behavior +(trying to access a variadic argument when there isn't any), so it is better to +avoid them. + +**Note:** This checker only inspects the *definition* of the variadic functions +and reports those that *would* fail if they were called with no variadic +arguments -- even if there is no such call in the codebase. + +This design rule is dictated by the SEI CERT rule `EXP47-C +<https://wiki.sei.cmu.edu/confluence/display/c/EXP47-C.+Do+not+call+va_arg+with+an+argument+of+the+incorrect+type>`_, +which describes several issues related to the use of ``va_arg()``. (The problem +reported by this checker is shown in the second code example; the first, +unrelated code example is covered by the clang diagnostic `-Wvarargs +<https://clang.llvm.org/docs/DiagnosticsReference.html#wvarargs>`_.) + +.. code-block:: cpp + + // This function expects a list of variadic arguments terminated by a NULL pointer. + void log_message(const char *msg, ...) { + va_list va; + const char *arg; + printf("%s\n", msg); + va_start(va, msg); + while ((arg = va_arg(va, const char *))) { + // warn: calls to 'log_message' always reach this va_arg() expression + printf(" * %s\n", arg); + } + va_end(va); + } + +Instead of this bugprone pattern, the SEI-CERT coding standard suggests +approaches where the number of variadic arguments can be specified in a + diff erent manner -- for example, it can be encoded in the initial parameter +like ``void foo(size_t num_varargs, ...)``. Those approaches are still not +foolproof (e.g. ``foo(3, "a")`` will be undefined behavior), but they reduce +the chances of an accidental mistake. + .. _optin-cplusplus-UninitializedObject: optin.cplusplus.UninitializedObject (C++) diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index 9e552d171cd26..6b9e0b50e1f59 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -422,6 +422,11 @@ def FixedAddressDereferenceChecker HelpText<"Check for dereferences of fixed addresses">, Documentation<HasDocumentation>; +def UnconditionalVAArgChecker + : Checker<"UnconditionalVAArg">, + HelpText<"Check variadic functions unconditionally using va_arg">, + Documentation<HasDocumentation>; + } // end "optin.core" //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h index f6a023368f3d2..3546e203b715f 100644 --- a/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h +++ b/clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporter.h @@ -256,6 +256,10 @@ class BasicBugReport : public BugReport { BasicBugReport(const BugType &bt, StringRef desc, PathDiagnosticLocation l) : BugReport(Kind::Basic, bt, desc), Location(l) {} + BasicBugReport(const BugType &BT, StringRef ShortDesc, StringRef Desc, + PathDiagnosticLocation L) + : BugReport(Kind::Basic, BT, ShortDesc, Desc), Location(L) {} + static bool classof(const BugReport *R) { return R->getKind() == Kind::Basic; } diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index dc1feef484302..8a0621077b977 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -115,6 +115,7 @@ add_clang_library(clangStaticAnalyzerCheckers TraversalChecker.cpp TrustNonnullChecker.cpp TrustReturnsNonnullChecker.cpp + UnconditionalVAArgChecker.cpp UndefBranchChecker.cpp UndefCapturedBlockVarChecker.cpp UndefResultChecker.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/UnconditionalVAArgChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UnconditionalVAArgChecker.cpp new file mode 100644 index 0000000000000..a6eeb8b7f2a18 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/UnconditionalVAArgChecker.cpp @@ -0,0 +1,175 @@ +//== UnconditionalVAArgChecker.cpp -----------------------------------------==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines a checker to detect functions that unconditionally call +// va_arg() and would fail if they were called with zero variadic arguments. +// +// This checker is only partially path-sensitive: it relies on the symbolic +// execution of the analyzer engine to follow the execution path from the +// beginning of a function to a va_arg() call and determine whether there are +// any branching points on that path -- but it uses BasicBugReport reports +// because report path wouldn't contain any useful information. (As this +// checker diagnoses a property of a variadic function, the path before that +// function is irrelevant; then the unconditional part of path is trivial.) +// +// The AST matching framework of Clang Tidy is not powerful enough to express +// this "no branching on the execution path" relationship, at least not without +// reimplementing a crude and fragile form of symbolic execution. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace clang; +using namespace ento; +using llvm::formatv; + +/// Either nullptr or a function under symbolic execution; a non-null value +/// means that the analyzer didn't see any branching points from the beginning +/// of that function until the current location. +REGISTER_TRAIT_WITH_PROGRAMSTATE(HasUnconditionalPath, const FunctionDecl *) + +namespace { +class UnconditionalVAArgChecker + : public Checker<check::BeginFunction, check::EndFunction, + check::BranchCondition, check::PreStmt<VAArgExpr>> { + const BugType BT{this, "Unconditional use of va_arg()", + categories::MemoryError}; + + static const FunctionDecl *getCurrentFunction(CheckerContext &C); + +public: + void checkBeginFunction(CheckerContext &C) const; + void checkEndFunction(const ReturnStmt *, CheckerContext &C) const; + void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const; + void checkPreStmt(const VAArgExpr *VAA, CheckerContext &C) const; +}; +} // end anonymous namespace + +const FunctionDecl * +UnconditionalVAArgChecker::getCurrentFunction(CheckerContext &C) { + const Decl *FD = C.getLocationContext()->getDecl(); + return dyn_cast_if_present<FunctionDecl>(FD); +} + +void UnconditionalVAArgChecker::checkBeginFunction(CheckerContext &C) const { + // We only look for unconditional va_arg() use in variadic functions. + // Functions that take a va_list argument are just parts of the argument + // handling, it is more natural for them to have preconditions. + const FunctionDecl *FD = getCurrentFunction(C); + if (FD && FD->isVariadic()) { + // If a variadic function is inlined in the body of another variadic + // function, this overwrites the path tracking for the outer function. As + // this situation is fairly rare and it is very unlikely that the "big" + // outer function still has an unconditional path, there is no need to + // write more complex logic that handles this. + // NOTE: Despite this, the checker can sometimes still report the + // unconditional va_arg() use in the outer function (probably because there + // is an alternative execution path that doesn't enter the inner call). + C.addTransition(C.getState()->set<HasUnconditionalPath>(FD)); + } +} + +void UnconditionalVAArgChecker::checkEndFunction(const ReturnStmt *, + CheckerContext &C) const { + // This callback is just for the sake of cleanliness, to remove data from the + // State after it becomes irrelevant. This checker would function perfectly + // correctly without this callback, and the impact on other checkers is also + // extremely limited (presence of extra metadata might prevent the + // unification of execution paths in some very rare situations). + ProgramStateRef State = C.getState(); + const FunctionDecl *FD = getCurrentFunction(C); + if (FD && FD == State->get<HasUnconditionalPath>()) { + State = State->set<HasUnconditionalPath>(nullptr); + C.addTransition(State); + } +} + +void UnconditionalVAArgChecker::checkBranchCondition(const Stmt *Condition, + CheckerContext &C) const { + // After evaluating a branch condition, the analyzer (which examines + // execution paths individually) won't be able to find a va_arg() expression + // that is _unconditionally_ reached -- so this callback resets the state + // trait HasUnconditionalPath. + // NOTES: + // 1. This is the right thing to do even if the analyzer sees that _in the + // current state_ the execution can only continue in one direction. For + // example, if the variadic function isn't the entrypont, then the parameters + // recieved from the caller may guarantee that va_arg() is used -- but this + // does not mean that the function _unconditionally_ uses va_arg(). + // 2. After other kinds of state splits (e.g. EagerlyAssueme, callbacks of + // StdLibraryFunctions separating diff erent cases for the behavior of a + // library function etc.) the diff erent execution paths will follow the same + // code (until they hit a branch condition), so it is reasonable (although + // not always correct) to assume that a va_arg() reached after those state + // splits is still _unconditionally_ reached if there were no branching + // statements. + // 3. This checker activates _after_ the evaluation of the branch condition, + // so va_arg() in the branch condition can be unconditionally reached. + C.addTransition(C.getState()->set<HasUnconditionalPath>(nullptr)); +} + +void UnconditionalVAArgChecker::checkPreStmt(const VAArgExpr *VAA, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const FunctionDecl *PathFrom = State->get<HasUnconditionalPath>(); + if (!PathFrom) + return; + + // Reset this trait in the state to ensure that multiple consecutive + // va_arg() calls don't produce repeated warnings. + C.addTransition(State->set<HasUnconditionalPath>(nullptr)); + + IdentifierInfo *II = PathFrom->getIdentifier(); + if (!II) + return; + StringRef FN = II->getName(); + + std::string FullMsg = formatv( + "Calls to '{0}' always reach this va_arg() expression, so calling " + "'{0}' with no variadic arguments would be undefined behavior", + FN); + SourceRange SR = VAA->getSourceRange(); + PathDiagnosticLocation PDL(SR.getBegin(), + C.getASTContext().getSourceManager()); + // We create a non-path-sensitive report because the path wouldn't contain + // any useful information: the path leading to the variadic function is + // actively ignored by the checker and the unconditional path from the + // start of the variadic function is trivial. + auto R = + std::make_unique<BasicBugReport>(BT, BT.getDescription(), FullMsg, PDL); + + if (getCurrentFunction(C) != PathFrom) { + // Highlight the definition of the variadic function in the rare case + // when the reached va_arg() expression is in another function. + SourceRange DefSR = PathFrom->getSourceRange(); + PathDiagnosticLocation DefPDL(DefSR.getBegin(), + C.getASTContext().getSourceManager()); + std::string NoteMsg = + formatv("Variadic function '{0}' is defined here", FN); + R->addNote(NoteMsg, DefPDL, DefSR); + } + R->addRange(SR); + R->setDeclWithIssue(PathFrom); + C.emitReport(std::move(R)); +} + +void ento::registerUnconditionalVAArgChecker(CheckerManager &Mgr) { + Mgr.registerChecker<UnconditionalVAArgChecker>(); +} + +bool ento::shouldRegisterUnconditionalVAArgChecker(const CheckerManager &) { + return true; +} diff --git a/clang/lib/StaticAnalyzer/Checkers/VAListChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/VAListChecker.cpp index 503fa5de868f2..e81c8bfa94cb1 100644 --- a/clang/lib/StaticAnalyzer/Checkers/VAListChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/VAListChecker.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// // -// This defines checkers which detect usage of uninitialized va_list values +// This defines a checker which detects usage of uninitialized va_list values // and va_start calls with no matching va_end. // //===----------------------------------------------------------------------===// diff --git a/clang/test/Analysis/unconditional-va_arg.c b/clang/test/Analysis/unconditional-va_arg.c new file mode 100644 index 0000000000000..0f20cd2c1c702 --- /dev/null +++ b/clang/test/Analysis/unconditional-va_arg.c @@ -0,0 +1,274 @@ +// 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 diff erent 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 diff erence + // (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_ diff erent_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_ diff erent_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 diff erent + // 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}} +} diff --git a/llvm/utils/gn/secondary/clang/lib/StaticAnalyzer/Checkers/BUILD.gn b/llvm/utils/gn/secondary/clang/lib/StaticAnalyzer/Checkers/BUILD.gn index 85905ad40a329..b3ac48975ee85 100644 --- a/llvm/utils/gn/secondary/clang/lib/StaticAnalyzer/Checkers/BUILD.gn +++ b/llvm/utils/gn/secondary/clang/lib/StaticAnalyzer/Checkers/BUILD.gn @@ -123,6 +123,7 @@ static_library("Checkers") { "TraversalChecker.cpp", "TrustNonnullChecker.cpp", "TrustReturnsNonnullChecker.cpp", + "UnconditionalVAArgChecker.cpp", "UndefBranchChecker.cpp", "UndefCapturedBlockVarChecker.cpp", "UndefResultChecker.cpp", _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
