From faabdfcf7f6704d5a7241d8b79b4dd5cc7a5527e Mon Sep 17 00:00:00 2001 From: Rashmi Mudduluru Date: Tue, 1 Feb 2022 18:28:42 -0800 Subject: [PATCH] [analyzer] Add support for __attribute__((returns_nonnull)). Differential Revision: https://reviews.llvm.org/D118657 --- .../clang/StaticAnalyzer/Checkers/Checkers.td | 5 ++ .../StaticAnalyzer/Checkers/CMakeLists.txt | 1 + .../Checkers/TrustReturnsNonnullChecker.cpp | 60 +++++++++++++++++++ .../test/Analysis/analyzer-enabled-checkers.c | 1 + .../Analysis/returns_nonnull-attribute.cpp | 21 +++++++ ...c-library-functions-arg-enabled-checkers.c | 1 + .../lib/StaticAnalyzer/Checkers/BUILD.gn | 1 + 7 files changed, 90 insertions(+) create mode 100644 clang/lib/StaticAnalyzer/Checkers/TrustReturnsNonnullChecker.cpp create mode 100644 clang/test/Analysis/returns_nonnull-attribute.cpp diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index f037c33a1304..fae4240e9d70 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -376,6 +376,11 @@ def TrustNonnullChecker : Checker<"TrustNonnull">, "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 index 3e85fadef0a2..44fa2a06c399 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -111,6 +111,7 @@ add_clang_library(clangStaticAnalyzerCheckers 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 index 000000000000..d80559c6a915 --- /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 index 7c00e78c16ac..7437e79a498f 100644 --- 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 index 000000000000..32d7b5200ef3 --- /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 index 9ad1be053851..a64ec4a71487 100644 --- 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 index 95868a290c27..8287c9f7e39a 100644 --- 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 @@ static_library("Checkers") { "TestAfterDivZeroChecker.cpp", "TraversalChecker.cpp", "TrustNonnullChecker.cpp", + "TrustReturnsNonnullChecker.cpp", "UndefBranchChecker.cpp", "UndefCapturedBlockVarChecker.cpp", "UndefResultChecker.cpp",