diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -376,6 +376,11 @@ "are not null">, Documentation; +def TrustReturnsNonnullChecker : Checker<"TrustReturnsNonnull">, + HelpText<"Trust that returns from methods annotated with returns_nonnull " + "are not null">, + Documentation; + } // end "apiModeling" //===----------------------------------------------------------------------===// diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -111,6 +111,7 @@ TestAfterDivZeroChecker.cpp TraversalChecker.cpp TrustNonnullChecker.cpp + TrustReturnsNonnullChecker.cpp UndefBranchChecker.cpp UndefCapturedBlockVarChecker.cpp UndefResultChecker.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/TrustReturnsNonnullChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/TrustReturnsNonnullChecker.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/TrustReturnsNonnullChecker.cpp @@ -0,0 +1,60 @@ +//== TrustReturnsNonnullChecker.cpp -- API nullability modeling -*- C++ -*--==// +// +// 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 checker adds nullability-related assumptions to methods annotated with +// returns_nonnull attribute. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { + +class TrustReturnsNonnullChecker : public Checker { + +public: + TrustReturnsNonnullChecker(ASTContext &Ctx) {} + + void checkPostCall(const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + if (isNonNullPtr(Call)) + if (auto L = Call.getReturnValue().getAs()) + State = State->assume(*L, /*assumption=*/true); + + C.addTransition(State); + } + +private: + /// \returns Whether the method declaration has the attribute returns_nonnull. + bool isNonNullPtr(const CallEvent &Call) const { + QualType ExprRetType = Call.getResultType(); + const Decl *CallDeclaration = Call.getDecl(); + if (!ExprRetType->isAnyPointerType() || !CallDeclaration) + return false; + + return CallDeclaration->hasAttr(); + } +}; + +} // namespace + +void ento::registerTrustReturnsNonnullChecker(CheckerManager &Mgr) { + Mgr.registerChecker(Mgr.getASTContext()); +} + +bool ento::shouldRegisterTrustReturnsNonnullChecker(const CheckerManager &mgr) { + return true; +} diff --git a/clang/test/Analysis/analyzer-enabled-checkers.c b/clang/test/Analysis/analyzer-enabled-checkers.c --- a/clang/test/Analysis/analyzer-enabled-checkers.c +++ b/clang/test/Analysis/analyzer-enabled-checkers.c @@ -9,6 +9,7 @@ // CHECK-NEXT: core.CallAndMessageModeling // CHECK-NEXT: apiModeling.StdCLibraryFunctions // CHECK-NEXT: apiModeling.TrustNonnull +// CHECK-NEXT: apiModeling.TrustReturnsNonnull // CHECK-NEXT: apiModeling.llvm.CastValue // CHECK-NEXT: apiModeling.llvm.ReturnValue // CHECK-NEXT: core.CallAndMessage diff --git a/clang/test/Analysis/returns_nonnull-attribute.cpp b/clang/test/Analysis/returns_nonnull-attribute.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/returns_nonnull-attribute.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,apiModeling.TrustReturnsNonnull -verify %s + +int *foo() __attribute__((returns_nonnull)); + +int *foo_no_attribute(); + +int test_foo() { + int *x = foo(); + if (x) {} + return *x; // no-warning +} + +int test_foo_no_attribute() { + int *x = foo_no_attribute(); + if (x) {} + return *x; // expected-warning{{Dereference of null pointer}} +} + +void test(void *(*f)(void)) { + f(); // Shouldn't crash compiler +} diff --git a/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c b/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c --- a/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c +++ b/clang/test/Analysis/std-c-library-functions-arg-enabled-checkers.c @@ -22,6 +22,7 @@ // CHECK-NEXT: apiModeling.StdCLibraryFunctions // CHECK-NEXT: alpha.unix.StdCLibraryFunctionArgs // CHECK-NEXT: apiModeling.TrustNonnull +// CHECK-NEXT: apiModeling.TrustReturnsNonnull // CHECK-NEXT: apiModeling.llvm.CastValue // CHECK-NEXT: apiModeling.llvm.ReturnValue // CHECK-NEXT: core.DivideZero diff --git a/llvm/utils/gn/secondary/clang/lib/StaticAnalyzer/Checkers/BUILD.gn b/llvm/utils/gn/secondary/clang/lib/StaticAnalyzer/Checkers/BUILD.gn --- a/llvm/utils/gn/secondary/clang/lib/StaticAnalyzer/Checkers/BUILD.gn +++ b/llvm/utils/gn/secondary/clang/lib/StaticAnalyzer/Checkers/BUILD.gn @@ -118,6 +118,7 @@ "TestAfterDivZeroChecker.cpp", "TraversalChecker.cpp", "TrustNonnullChecker.cpp", + "TrustReturnsNonnullChecker.cpp", "UndefBranchChecker.cpp", "UndefCapturedBlockVarChecker.cpp", "UndefResultChecker.cpp",