Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -100,6 +100,10 @@ let ParentPackage = Core in { +def ArgumentSizeChecker : Checker<"ArgumentSize">, + HelpText<"Check that function pointers are called with the correct number of arguments">, + DescFile<"ArgumentSizeChecker.cpp">; + def DereferenceChecker : Checker<"NullDereference">, HelpText<"Check for dereferences of null pointers">, DescFile<"DereferenceChecker.cpp">; Index: clang/lib/StaticAnalyzer/Checkers/ArgumentSizeChecker.cpp =================================================================== --- /dev/null +++ clang/lib/StaticAnalyzer/Checkers/ArgumentSizeChecker.cpp @@ -0,0 +1,52 @@ +#include "ClangSACheckers.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/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +class ArgumentSizeChecker : public Checker< check::PreCall > { + mutable std::unique_ptr BT; + +public: + // TODO: do not duplicate compiler warnings. + void checkPreCall(const CallEvent &Call, CheckerContext &Ctx) const { + if (Call.getNumArgs() > Call.parameters().size()) { + + // Skip functions without declarations. + if (!Call.getDecl()) + return; + + // FIXME: investigate is this a good idea. + if (!Call.getRuntimeDefinition().getDecl()) + return; + + // Skip variadic functions. + if (CallEvent::isVariadic(Call.getDecl())) + return; + + // Skip implicit declarations. + if (Call.getDecl()->isImplicit()) + return; + + if (!BT) + BT.reset(new BuiltinBug( + this, "Function called with too many arguments")); + + ExplodedNode *N = Ctx.generateErrorNode(); + + // TODO: more detailed error message. + auto R = llvm::make_unique(*BT, BT->getDescription(), N); + Ctx.emitReport(std::move(R)); + } + } +}; + +void ento::registerArgumentSizeChecker(CheckerManager &Mgr) { + Mgr.registerChecker(); +} Index: clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -6,6 +6,7 @@ AllocationDiagnostics.cpp AnalysisOrderChecker.cpp AnalyzerStatsChecker.cpp + ArgumentSizeChecker.cpp ArrayBoundChecker.cpp ArrayBoundCheckerV2.cpp BasicObjCFoundationChecks.cpp Index: clang/test/Analysis/argument_size_checker.c =================================================================== --- /dev/null +++ clang/test/Analysis/argument_size_checker.c @@ -0,0 +1,6 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify -Wno-implicit-function-declaration %s +// expected-no-diagnostics + +void no_warn_on_implicit_funcs() { + implicit_func(1); +} Index: clang/test/Analysis/argument_size_checker.cc =================================================================== --- /dev/null +++ clang/test/Analysis/argument_size_checker.cc @@ -0,0 +1,18 @@ +// RUN: %clang_analyze_cc1 -x c++ -analyzer-checker=core -analyzer-output=text -verify %s + +int printf(const char *restrict,...); + +void *has_no_argument_and_returns_null(void) { + return 0; +} + +void too_many_args() { + int local; + void (*takes_int_ptr_argument)(int *) = (void (*)(int*))has_no_argument_and_returns_null; + + takes_int_ptr_argument(&local); // expected-warning{{Function called with too many arguments}} +} + +void no_warning_on_variadic_func() { + printf("str", 1, 2, 3, 4, 5); +} Index: clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp =================================================================== --- clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp +++ clang/test/Analysis/diagnostics/no-store-func-path-notes.cpp @@ -151,12 +151,12 @@ } void rdar40335545() { - int local; // expected-note{{}} + int local; void (*takes_int_ptr_argument)(int *) = (void (*)(int*))has_no_argument_and_returns_null; - takes_int_ptr_argument(&local); // no-crash + takes_int_ptr_argument(&local); // expected-warning{{Function called with too many arguments}} + // expected-note@-1{{Function called with too many arguments}} - int useLocal = local; //expected-warning{{}} - //expected-note@-1{{}} + int useLocal = local; (void)useLocal; } Index: clang/test/Analysis/inline.c =================================================================== --- clang/test/Analysis/inline.c +++ clang/test/Analysis/inline.c @@ -30,6 +30,7 @@ void test2_f3() { test2_f1(test2_f2()); // expected-warning{{too many arguments in call to 'test2_f1'}} + // expected-warning@-1{{Function called with too many arguments}} } // Test that inlining works with recursive functions.