clang: lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

13

24#include "llvm/ADT/STLExtras.h"

25#include "llvm/ADT/SmallPtrSet.h"

26#include "llvm/Support/raw_ostream.h"

27using namespace clang;

28using namespace ento;

29

30namespace {

31class StackAddrEscapeChecker

32 : public CheckerFamily<check::PreCall, check::PreStmt,

33 check::EndFunction> {

34 mutable IdentifierInfo *dispatch_semaphore_tII = nullptr;

35

36public:

37 StringRef getDebugTag() const override { return "StackAddrEscapeChecker"; }

38

39 CheckerFrontend StackAddrEscape;

40 CheckerFrontend StackAddrAsyncEscape;

41

42 const BugType StackLeak{&StackAddrEscape,

43 "Stack address leaks outside of stack frame"};

44 const BugType ReturnStack{&StackAddrEscape,

45 "Return of address to stack-allocated memory"};

46 const BugType CapturedStackAsync{

47 &StackAddrAsyncEscape, "Address of stack-allocated memory is captured"};

48

49 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;

50 void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;

51 void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;

52

53private:

54 void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B,

55 CheckerContext &C) const;

56 void EmitReturnLeakError(CheckerContext &C, const MemRegion *LeakedRegion,

57 const Expr *RetE) const;

58 bool isSemaphoreCaptured(const BlockDecl &B) const;

59 static SourceRange genName(raw_ostream &os, const MemRegion *R,

60 ASTContext &Ctx);

61 static SmallVector<std::pair<const MemRegion *, const StackSpaceRegion *>, 4>

62 getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C);

63 static bool isNotInCurrentFrame(const StackSpaceRegion *MS,

64 CheckerContext &C);

65};

66}

67

68SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,

70

73 SourceRange range;

74 os << "Address of ";

75

76

77 if (const auto *CR = dyn_cast(R)) {

78 const CompoundLiteralExpr *CL = CR->getLiteralExpr();

79 os << "stack memory associated with a compound literal "

80 "declared on line "

83 } else if (const auto *AR = dyn_cast(R)) {

84 const Expr *ARE = AR->getExpr();

87 os << "stack memory allocated by call to alloca() on line "

88 << SM.getExpansionLineNumber(L);

89 } else if (const auto *BR = dyn_cast(R)) {

90 const BlockDecl *BD = BR->getCodeRegion()->getDecl();

93 os << "stack-allocated block declared on line "

94 << SM.getExpansionLineNumber(L);

95 } else if (const auto *VR = dyn_cast(R)) {

96 os << "stack memory associated with local variable '" << VR->getString()

97 << '\'';

98 range = VR->getDecl()->getSourceRange();

99 } else if (const auto *LER = dyn_cast(R)) {

100 QualType Ty = LER->getValueType().getLocalUnqualifiedType();

101 os << "stack memory associated with temporary object of type '";

103 os << "' lifetime extended by local variable";

104 if (const IdentifierInfo *ID = LER->getExtendingDecl()->getIdentifier())

105 os << " '" << ID->getName() << '\'';

106 range = LER->getExpr()->getSourceRange();

107 } else if (const auto *TOR = dyn_cast(R)) {

108 QualType Ty = TOR->getValueType().getLocalUnqualifiedType();

109 os << "stack memory associated with temporary object of type '";

111 os << "'";

112 range = TOR->getExpr()->getSourceRange();

113 } else {

114 llvm_unreachable("Invalid region in ReturnStackAddressChecker.");

115 }

116

118}

119

120bool StackAddrEscapeChecker::isNotInCurrentFrame(const StackSpaceRegion *MS,

121 CheckerContext &C) {

123}

124

125bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const {

126 if (!dispatch_semaphore_tII)

128 for (const auto &C : B.captures()) {

129 const auto *T = C.getVariable()->getType()->getAs();

130 if (T && T->getDecl()->getIdentifier() == dispatch_semaphore_tII)

131 return true;

132 }

133 return false;

134}

135

136SmallVector<std::pair<const MemRegion *, const StackSpaceRegion *>, 4>

137StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,

138 CheckerContext &C) {

139 SmallVector<std::pair<const MemRegion *, const StackSpaceRegion *>, 4>

140 Regions;

143 SVal Val = State->getSVal(Var.getCapturedRegion());

144 if (const MemRegion *Region = Val.getAsRegion()) {

145 if (const auto *Space =

146 Region->getMemorySpaceAs(State)) {

147 Regions.emplace_back(Region, Space);

148 }

149 }

150 }

151 return Regions;

152}

153

158 OS << " is captured by a returned block";

159 return;

160 }

161 }

162

163

164 OS << " returned to caller";

165}

166

167void StackAddrEscapeChecker::EmitReturnLeakError(CheckerContext &C,

168 const MemRegion *R,

169 const Expr *RetE) const {

170 ExplodedNode *N = C.generateNonFatalErrorNode();

171 if (!N)

172 return;

173

174

175 SmallString<128> buf;

176 llvm::raw_svector_ostream os(buf);

177

178

179 SourceRange range = genName(os, R, C.getASTContext());

181

182 auto report =

183 std::make_unique(ReturnStack, os.str(), N);

185 if (range.isValid())

186 report->addRange(range);

187 C.emitReport(std::move(report));

188}

189

190void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(

191 const BlockDataRegion &B, CheckerContext &C) const {

192

193

194

195

196

197

198

199 if (isSemaphoreCaptured(*B.getDecl()))

200 return;

201 auto Regions = getCapturedStackRegions(B, C);

202 for (const MemRegion *Region : llvm::make_first_range(Regions)) {

203

204

205

206

207

208

210 continue;

211 ExplodedNode *N = C.generateNonFatalErrorNode();

212 if (!N)

213 continue;

214 SmallString<128> Buf;

215 llvm::raw_svector_ostream Out(Buf);

216 SourceRange Range = genName(Out, Region, C.getASTContext());

217 Out << " is captured by an asynchronously-executed block";

218 auto Report = std::make_unique(CapturedStackAsync,

219 Out.str(), N);

220 if (Range.isValid())

221 Report->addRange(Range);

222 C.emitReport(std::move(Report));

223 }

224}

225

226void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,

227 CheckerContext &C) const {

228 if (!StackAddrAsyncEscape.isEnabled())

229 return;

230 if (Call.isGlobalCFunction("dispatch_after") &&

231 Call.isGlobalCFunction("dispatch_async"))

232 return;

233 for (unsigned Idx = 0, NumArgs = Call.getNumArgs(); Idx < NumArgs; ++Idx) {

234 if (const BlockDataRegion *B = dyn_cast_or_null(

235 Call.getArgSVal(Idx).getAsRegion()))

236 checkAsyncExecutedBlockCaptures(*B, C);

237 }

238}

239

240

241

242

243

244

245

246

252

253public:

257 : Ctxt(Ctxt), PoppedStackFrame(Ctxt.getStackFrame()),

258 EscapingStackRegions(StorageForStackRegions) {}

259

261

263 if (!VisitedRegions.insert(MR).second)

264 return true;

265

266 SaveIfEscapes(MR);

267

269 return VisitBlockDataRegionCaptures(BDR);

270

271 return true;

272 }

273

274private:

275 void SaveIfEscapes(const MemRegion *MR) {

277

278 if (!SSR)

279 return;

280

282 if (CapturedSFC == PoppedStackFrame ||

283 PoppedStackFrame->isParentOf(CapturedSFC))

284 EscapingStackRegions.push_back(MR);

285 }

286

287 bool VisitBlockDataRegionCaptures(const BlockDataRegion *BDR) {

289 SVal Val = Ctxt.getState()->getSVal(Var.getCapturedRegion());

290 const MemRegion *Region = Val.getAsRegion();

291 if (Region) {

292 SaveIfEscapes(Region);

293 VisitMemRegion(Region);

294 }

295 }

296

297 return false;

298 }

299};

300

301

302

303

304

305

308 const Expr *RetE, SVal &RetVal) {

309

311

313

314

315

316

317 if (const ExprWithCleanups *Cleanup = dyn_cast(RetE))

318 RetE = Cleanup->getSubExpr();

319 bool IsConstructExpr =

321

322

323

324 bool IsCopyAndAutoreleaseBlockObj = false;

325 if (const auto *ICE = dyn_cast(RetE)) {

326 IsCopyAndAutoreleaseBlockObj =

327 isa_and_nonnull(RetRegion) &&

328 ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject;

329 }

330

331 for (const MemRegion *MR : MaybeEscaped) {

332 if (RetRegion == MR && (IsCopyAndAutoreleaseBlockObj || IsConstructExpr))

333 continue;

334

335 WillEscape.push_back(MR);

336 }

337

338 return WillEscape;

339}

340

341

342

343static SmallVector<const MemRegion *>

346

349 Scanner.scan(RetVal);

350

352}

353

354void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,

355 CheckerContext &C) const {

356 if (!StackAddrEscape.isEnabled())

357 return;

358

360 if (!RetE)

361 return;

363

364 SVal V = C.getSVal(RetE);

365

366 SmallVector<const MemRegion *> EscapedStackRegions =

368

369 for (const MemRegion *ER : EscapedStackRegions)

370 EmitReturnLeakError(C, ER, RetE);

371}

372

375 assert(R);

378 return MemSpace;

379

380

381

383 if (const auto *OriginReg = SymReg->getSymbol()->getOriginRegion())

385 }

386 return nullptr;

387}

388

391 while (const auto *SymReg = dyn_cast(Reg)) {

392 const auto *OriginReg = SymReg->getSymbol()->getOriginRegion();

393 if (!OriginReg)

394 break;

396 }

397 return Reg;

398}

399

402 assert(Referrer);

403 const StringRef ReferrerMemorySpace = [](const MemSpaceRegion *Space) {

405 return "static";

407 return "global";

409

410 return "caller";

412

414 if (const auto *SymReg = dyn_cast(Referrer);

415 SymReg && SymReg->getSymbol()->getOriginRegion()) {

416 Referrer = SymReg->getSymbol()->getOriginRegion()->getBaseRegion();

418

419

420

421 return std::nullopt;

423

424

425 return std::nullopt;

426 } else {

427 assert(false && "Unexpected referrer region type.");

428 return std::nullopt;

429 }

430 }

431 assert(Referrer);

433

434 std::string buf;

435 llvm::raw_string_ostream os(buf);

436 os << ReferrerMemorySpace << " variable ";

438 return buf;

439}

440

441

442

445 if (!SymReg)

446 return false;

447 SymbolRef Symbol = SymReg->getSymbol();

448

449 const auto *DerS = dyn_cast(Symbol);

450 return DerS && isa_and_nonnull(DerS->getParentSymbol());

451}

452

453void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,

454 CheckerContext &Ctx) const {

455 if (!StackAddrEscape.isEnabled())

456 return;

457

459

460 bool ExitingTopFrame =

462

463 if (ExitingTopFrame &&

466

467

468

469

471 }

472

473

474

475 class CallBack : public StoreManager::BindingsHandler {

476 private:

477 CheckerContext &Ctx;

479 const StackFrameContext *PoppedFrame;

480 const bool TopFrame;

481

482

483

484

485 bool checkForDanglingStackVariable(const MemRegion *Referrer,

486 const MemRegion *Referred) {

487 const auto *ReferrerMemSpace =

489 const auto *ReferredMemSpace =

491

492 if (!ReferrerMemSpace || !ReferredMemSpace)

493 return false;

494

495 const auto *ReferrerStackSpace =

496 ReferrerMemSpace->getAs();

497

498 if (!ReferrerStackSpace)

499 return false;

500

501 if (const auto *ReferredFrame = ReferredMemSpace->getStackFrame();

502 ReferredFrame != PoppedFrame) {

503 return false;

504 }

505

506 if (ReferrerStackSpace->getStackFrame()->isParentOf(PoppedFrame)) {

507 V.emplace_back(Referrer, Referred);

508 return true;

509 }

511

513 ReferrerStackSpace->getStackFrame() == PoppedFrame && TopFrame) {

514

515 V.emplace_back(Referrer, Referred);

516 return true;

517 }

518 return false;

519 }

520

521

522

523

524

525 void recordInInvalidatedRegions(const MemRegion *Region) {

528 }

529

530 public:

531 SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V;

532

533

534

535

536 llvm::SmallPtrSet<const MemRegion *, 4> ExcludedRegions;

537

538 CallBack(CheckerContext &CC, bool TopFrame)

539 : Ctx(CC), State(CC.getState()), PoppedFrame(CC.getStackFrame()),

540 TopFrame(TopFrame) {}

541

542 bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region,

543 SVal Val) override {

544 recordInInvalidatedRegions(Region);

546 if (!VR)

547 return true;

548

549 if (checkForDanglingStackVariable(Region, VR))

550 return true;

551

552

553 if (!isa_and_nonnull(

555 return true;

556

557 if (VR) {

558 if (const auto *S = VR->getMemorySpaceAs(State);

559 S && !isNotInCurrentFrame(S, Ctx)) {

560 V.emplace_back(Region, VR);

561 }

562 }

563 return true;

564 }

565 };

566

567 CallBack Cb(Ctx, ExitingTopFrame);

569 State->getStateManager().getStoreManager().iterBindings(State->getStore(),

570 Cb);

571

572 if (Cb.V.empty())

573 return;

574

575

577 if (!N)

578 return;

579

580 for (const auto &P : Cb.V) {

581 const MemRegion *Referrer = P.first->getBaseRegion();

582 const MemRegion *Referred = P.second;

584 continue;

585 }

586

587

588 const StringRef CommonSuffix =

589 " upon returning to the caller. This will be a dangling reference";

590 SmallString<128> Buf;

591 llvm::raw_svector_ostream Out(Buf);

592 const SourceRange Range = genName(Out, Referred, Ctx.getASTContext());

593

595 Out << " is still referred to by a temporary object on the stack"

596 << CommonSuffix;

598 std::make_unique(StackLeak, Out.str(), N);

599 if (Range.isValid())

600 Report->addRange(Range);

602 return;

603 }

604

605 auto ReferrerVariable = printReferrer(State, Referrer);

606 if (!ReferrerVariable) {

607 continue;

608 }

609

610 Out << " is still referred to by the " << *ReferrerVariable << CommonSuffix;

612 std::make_unique(StackLeak, Out.str(), N);

613 if (Range.isValid())

614 Report->addRange(Range);

615

617 }

618}

619

620#define REGISTER_CHECKER(NAME) \

621 void ento::register##NAME##Checker(CheckerManager &Mgr) { \

622 Mgr.getChecker()->NAME.enable(Mgr); \

623 } \

624 \

625 bool ento::shouldRegister##NAME##Checker(const CheckerManager &) { \

626 return true; \

627 }

628

#define REGISTER_CHECKER(name)

Defines the clang::Expr interface and subclasses for C++ expressions.

Defines the SourceManager interface.

static SmallVector< const MemRegion * > FilterReturnExpressionLeaks(const SmallVectorImpl< const MemRegion * > &MaybeEscaped, CheckerContext &C, const Expr *RetE, SVal &RetVal)

Given some memory regions that are flagged by FindStackRegionsSymbolVisitor, this function filters ou...

Definition StackAddrEscapeChecker.cpp:306

static bool isInvalidatedSymbolRegion(const MemRegion *Region)

Check whether Region refers to a freshly minted symbol after an opaque function call.

Definition StackAddrEscapeChecker.cpp:443

static void EmitReturnedAsPartOfError(llvm::raw_ostream &OS, SVal ReturnedVal, const MemRegion *LeakedRegion)

Definition StackAddrEscapeChecker.cpp:154

static std::optional< std::string > printReferrer(ProgramStateRef State, const MemRegion *Referrer)

Definition StackAddrEscapeChecker.cpp:400

static const MemSpaceRegion * getStackOrGlobalSpaceRegion(ProgramStateRef State, const MemRegion *R)

Definition StackAddrEscapeChecker.cpp:373

static const MemRegion * getOriginBaseRegion(const MemRegion *Reg)

Definition StackAddrEscapeChecker.cpp:389

static SmallVector< const MemRegion * > FindEscapingStackRegions(CheckerContext &C, const Expr *RetE, SVal RetVal)

For use in finding regions that live on the checker context's current stack frame,...

Definition StackAddrEscapeChecker.cpp:344

A visitor made for use with a ScanReachableSymbols scanner, used for finding stack regions within an ...

Definition StackAddrEscapeChecker.cpp:247

bool VisitMemRegion(const MemRegion *MR) override

Definition StackAddrEscapeChecker.cpp:262

FindStackRegionsSymbolVisitor(CheckerContext &Ctxt, SmallVectorImpl< const MemRegion * > &StorageForStackRegions)

Definition StackAddrEscapeChecker.cpp:254

bool VisitSymbol(SymbolRef sym) override

A visitor method invoked by ProgramStateManager::scanReachableSymbols.

Definition StackAddrEscapeChecker.cpp:260

Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...

SourceManager & getSourceManager()

const clang::PrintingPolicy & getPrintingPolicy() const

ArrayRef< Capture > captures() const

SourceRange getSourceRange() const override LLVM_READONLY

Source range that this declaration covers.

SourceLocation getBeginLoc() const LLVM_READONLY

ASTContext & getASTContext() const LLVM_READONLY

SourceLocation getBeginLoc() const LLVM_READONLY

Represents an expression – generally a full-expression – that introduces cleanups to be run at the en...

This represents one expression.

Expr * IgnoreParens() LLVM_READONLY

Skip past any parentheses which might surround this expression until reaching a fixed point.

IdentifierInfo & get(StringRef Name)

Return the identifier token info for the specified named identifier.

bool isParentOf(const LocationContext *LC) const

const StackFrameContext * getStackFrame() const

virtual bool inTopFrame() const

const ProgramPointTag * getTag() const

void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine(), unsigned Indentation=0) const

A trivial tuple used to represent a source range.

It represents a stack frame of the call stack (based on CallEvent).

SourceRange getSourceRange() const LLVM_READONLY

SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...

SourceLocation getBeginLoc() const LLVM_READONLY

const T * getAs() const

Member-template getAs'.

bool isRecordType() const

BlockDataRegion - A region that represents a block instance.

LLVM_ATTRIBUTE_RETURNS_NONNULL const BlockDecl * getDecl() const

llvm::iterator_range< referenced_vars_iterator > referenced_vars() const

ExplodedNode * getPredecessor()

Returns the previous node in the exploded graph, which includes the state of the program before the c...

ASTContext & getASTContext()

ExplodedNode * generateNonFatalErrorNode(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)

Generate a transition to a node that will be used to report an error.

void emitReport(std::unique_ptr< BugReport > R)

Emit the diagnostics report.

Checker families (where a single backend class implements multiple related frontends) should derive f...

const ProgramStateRef & getState() const

ProgramPoint getLocation() const

getLocation - Returns the edge associated with the given node.

const LocationContext * getLocationContext() const

ExplodedNode * getFirstPred()

static const ProgramPointTag * cleanupNodeTag()

A tag to track convenience transitions, which can be removed at cleanup.

MemRegion - The root abstract class for all memory regions.

const MemSpace * getMemorySpaceAs(ProgramStateRef State) const

LLVM_ATTRIBUTE_RETURNS_NONNULL const MemSpaceRegion * getMemorySpace(ProgramStateRef State) const

Returns the most specific memory space for this memory region in the given ProgramStateRef.

LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getBaseRegion() const

virtual void printPretty(raw_ostream &os) const

Print the region for use in diagnostics.

const RegionTy * getAs() const

virtual bool canPrintPretty() const

Returns true if this region can be printed in a user-friendly way.

MemSpaceRegion - A memory region that represents a "memory space"; for example, the set of global var...

SVal - This represents a symbolic expression, which can be either an L-value or an R-value.

const MemRegion * getAsRegion() const

A utility class that visits the reachable symbols using a custom SymbolVisitor.

LLVM_ATTRIBUTE_RETURNS_NONNULL const StackFrameContext * getStackFrame() const

SymbolicRegion - A special, "non-concrete" region.

IntrusiveRefCntPtr< const ProgramState > ProgramStateRef

const SymExpr * SymbolRef

@ OS

Indicates that the tracking object is a descendant of a referenced-counted OSObject,...

const void * Store

Store - This opaque type encapsulates an immutable mapping from locations to values.

RangeSelector range(RangeSelector Begin, RangeSelector End)

DEPRECATED. Use enclose.

The JSON file list parser is used to communicate input to InstallAPI.

bool isa(CodeGen::Address addr)

const FunctionProtoType * T