131 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"10#include "CheckerRegistration.h"11#include "clang/Basic/LLVM.h"12#include "clang/Frontend/CompilerInstance.h"13#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"14#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"15#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"16#include "clang/StaticAnalyzer/Core/Checker.h"17#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"18#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"19#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"20#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"21#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"22#include "clang/Tooling/Tooling.h"23#include "gtest/gtest.h"24 25namespace clang {26namespace ento {27namespace {28 29void reportBug(const CheckerBase *Checker, const CallEvent &Call,30 CheckerContext &C, StringRef WarningMsg) {31 C.getBugReporter().EmitBasicReport(32 nullptr, Checker, "", categories::LogicError, WarningMsg,33 PathDiagnosticLocation(Call.getOriginExpr(), C.getSourceManager(),34 C.getLocationContext()),35 {});36}37 38class CXXDeallocatorChecker : public Checker<check::PreCall> {39public:40 void checkPreCall(const CallEvent &Call, CheckerContext &C) const {41 const auto *DC = dyn_cast<CXXDeallocatorCall>(&Call);42 if (!DC) {43 return;44 }45 46 SmallString<100> WarningBuf;47 llvm::raw_svector_ostream WarningOS(WarningBuf);48 WarningOS << "NumArgs: " << DC->getNumArgs();49 50 reportBug(this, *DC, C, WarningBuf);51 }52};53 54void addCXXDeallocatorChecker(AnalysisASTConsumer &AnalysisConsumer,55 AnalyzerOptions &AnOpts) {56 AnOpts.CheckersAndPackages = {{"test.CXXDeallocator", true}};57 AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {58 Registry.addChecker<CXXDeallocatorChecker>("test.CXXDeallocator",59 "MockDescription");60 });61}62 63// TODO: What we should really be testing here is all the different varieties64// of delete operators, and whether the retrieval of their arguments works as65// intended. At the time of writing this file, CXXDeallocatorCall doesn't pick66// up on much of those due to the AST not containing CXXDeleteExpr for most of67// the standard/custom deletes.68TEST(CXXDeallocatorCall, SimpleDestructor) {69 std::string Diags;70 EXPECT_TRUE(runCheckerOnCode<addCXXDeallocatorChecker>(R"(71 struct A {};72 73 void f() {74 A *a = new A;75 delete a;76 }77 )",78 Diags));79#if defined(_AIX) || defined(__MVS__) || defined(__MINGW32__)80 // AIX, ZOS and MinGW default to -fno-sized-deallocation.81 EXPECT_EQ(Diags, "test.CXXDeallocator: NumArgs: 1\n");82#else83 EXPECT_EQ(Diags, "test.CXXDeallocator: NumArgs: 2\n");84#endif85}86 87TEST(PrivateMethodCache, NeverReturnDanglingPointersWithMultipleASTs) {88 // Each iteration will load and unload an AST multiple times. Since the code89 // is always the same, we increase the chance of hitting a bug in the private90 // method cache, returning a dangling pointer and crashing the process. If the91 // cache is properly cleared between runs, the test should pass.92 for (int I = 0; I < 100; ++I) {93 auto const *Code = R"(94 typedef __typeof(sizeof(int)) size_t;95 96 extern void *malloc(size_t size);97 extern void *memcpy(void *dest, const void *src, size_t n);98 99 @interface SomeMoreData {100 char const* _buffer;101 int _size;102 }103 @property(nonatomic, readonly) const char* buffer;104 @property(nonatomic) int size;105 106 - (void)appendData:(SomeMoreData*)other;107 108 @end109 110 @implementation SomeMoreData111 @synthesize size = _size;112 @synthesize buffer = _buffer;113 114 - (void)appendData:(SomeMoreData*)other {115 int const len = (_size + other.size); // implicit self._length116 char* d = malloc(sizeof(char) * len);117 memcpy(d + 20, other.buffer, len);118 }119 120 @end121 )";122 std::string Diags;123 EXPECT_TRUE(runCheckerOnCodeWithArgs<addCXXDeallocatorChecker>(124 Code, {"-x", "objective-c", "-Wno-objc-root-class"}, Diags));125 }126}127 128} // namespace129} // namespace ento130} // namespace clang131