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

1

2

3

4

5

6

7

8

9

10

11

12

13

23#include "llvm/ADT/SmallString.h"

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

25using namespace clang;

26using namespace ento;

27

28namespace {

29class StackAddrEscapeChecker

30 : public Checker<check::PreCall, check::PreStmt,

31 check::EndFunction> {

32 mutable IdentifierInfo *dispatch_semaphore_tII = nullptr;

33 mutable std::unique_ptr BT_stackleak;

34 mutable std::unique_ptr BT_returnstack;

35 mutable std::unique_ptr BT_capturedstackasync;

36 mutable std::unique_ptr BT_capturedstackret;

37

38public:

39 enum CheckKind {

40 CK_StackAddrEscapeChecker,

41 CK_StackAddrAsyncEscapeChecker,

42 CK_NumCheckKinds

43 };

44

45 bool ChecksEnabled[CK_NumCheckKinds] = {false};

47

51

52private:

55 void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B,

58 const Expr *RetE) const;

59 bool isSemaphoreCaptured(const BlockDecl &B) const;

65};

66}

67

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

70

74 os << "Address of ";

75

76

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

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 MemRegion *R,

123 return S->getStackFrame() != C.getStackFrame();

124}

125

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

127 if (!dispatch_semaphore_tII)

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

130 const auto *T = C.getVariable()->getType()->getAs<TypedefType>();

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

132 return true;

133 }

134 return false;

135}

136

138StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,

142 SVal Val = C.getState()->getSVal(Var.getCapturedRegion());

144 if (Region && isa(Region->getMemorySpace()))

145 Regions.push_back(Region);

146 }

147 return Regions;

148}

149

150void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,

152 const Expr *RetE) const {

154 if (!N)

155 return;

156 if (!BT_returnstack)

157 BT_returnstack = std::make_unique(

158 CheckNames[CK_StackAddrEscapeChecker],

159 "Return of address to stack-allocated memory");

160

162 llvm::raw_svector_ostream os(buf);

164 os << " returned to caller";

165 auto report =

166 std::make_unique(*BT_returnstack, os.str(), N);

168 if (range.isValid())

169 report->addRange(range);

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

171}

172

173void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(

175

176

177

178

179

180

181

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

183 return;

184 for (const MemRegion *Region : getCapturedStackRegions(B, C)) {

185

186

187

188

189

190

191 if (isa(Region))

192 continue;

194 if (!N)

195 continue;

196 if (!BT_capturedstackasync)

197 BT_capturedstackasync = std::make_unique(

198 CheckNames[CK_StackAddrAsyncEscapeChecker],

199 "Address of stack-allocated memory is captured");

201 llvm::raw_svector_ostream Out(Buf);

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

204 auto Report = std::make_unique(

205 *BT_capturedstackasync, Out.str(), N);

206 if (Range.isValid())

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

209 }

210}

211

212void StackAddrEscapeChecker::checkReturnedBlockCaptures(

214 for (const MemRegion *Region : getCapturedStackRegions(B, C)) {

215 if (isNotInCurrentFrame(Region, C))

216 continue;

218 if (!N)

219 continue;

220 if (!BT_capturedstackret)

221 BT_capturedstackret = std::make_unique(

222 CheckNames[CK_StackAddrEscapeChecker],

223 "Address of stack-allocated memory is captured");

225 llvm::raw_svector_ostream Out(Buf);

227 Out << " is captured by a returned block";

228 auto Report = std::make_unique(*BT_capturedstackret,

229 Out.str(), N);

230 if (Range.isValid())

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

233 }

234}

235

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

238 if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])

239 return;

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

241 Call.isGlobalCFunction("dispatch_async"))

242 return;

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

244 if (const BlockDataRegion *B = dyn_cast_or_null(

245 Call.getArgSVal(Idx).getAsRegion()))

246 checkAsyncExecutedBlockCaptures(*B, C);

247 }

248}

249

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

252 if (!ChecksEnabled[CK_StackAddrEscapeChecker])

253 return;

254

256 if (!RetE)

257 return;

259

260 SVal V = C.getSVal(RetE);

261 const MemRegion *R = V.getAsRegion();

262 if (!R)

263 return;

264

265 if (const BlockDataRegion *B = dyn_cast(R))

266 checkReturnedBlockCaptures(*B, C);

267

268 if (!isa(R->getMemorySpace()) || isNotInCurrentFrame(R, C))

269 return;

270

271

272

273

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

275 RetE = Cleanup->getSubExpr();

277 return;

278

279

280

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

282 if (isa(R) &&

283 ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) {

284 return;

285 }

286 }

287

288 EmitStackError(C, R, RetE);

289}

290

292 assert(R);

295 return SSR;

297 return GSR;

298 }

299

300

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

304 }

305 return nullptr;

306}

307

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

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

312 if (!OriginReg)

313 break;

315 }

316 return Reg;

317}

318

320 assert(Referrer);

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

322 if (isa(Space))

323 return "static";

324 if (isa(Space))

325 return "global";

326 assert(isa(Space));

327

328 return "caller";

330

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

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

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

335 } else if (isa(Referrer)) {

336

337

338

339 return std::nullopt;

340 } else if (isa(Referrer)) {

341

342

343 return std::nullopt;

344 } else {

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

346 return std::nullopt;

347 }

348 }

349 assert(Referrer);

351

352 std::string buf;

353 llvm::raw_string_ostream os(buf);

354 os << ReferrerMemorySpace << " variable ";

356 return buf;

357}

358

359

360

363 if (!SymReg)

364 return false;

365 SymbolRef Symbol = SymReg->getSymbol();

366

367 const auto *DerS = dyn_cast(Symbol);

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

369}

370

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

373 if (!ChecksEnabled[CK_StackAddrEscapeChecker])

374 return;

375

377

378 bool ExitingTopFrame =

379 Ctx.getPredecessor()->getLocationContext()->inTopFrame();

380

381 if (ExitingTopFrame &&

383 Node->getFirstPred()) {

384

385

386

387

389 }

390

391

392

394 private:

397 const bool TopFrame;

398

399

400

401

402 bool checkForDanglingStackVariable(const MemRegion *Referrer,

405 const auto *ReferredMemSpace =

407

408 if (!ReferrerMemSpace || !ReferredMemSpace)

409 return false;

410

411 const auto *ReferrerStackSpace =

413

414 if (!ReferrerStackSpace)

415 return false;

416

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

418 ReferredFrame != PoppedFrame) {

419 return false;

420 }

421

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

423 V.emplace_back(Referrer, Referred);

424 return true;

425 }

426 if (isa(ReferrerMemSpace) &&

427

428 isa(Referrer->getBaseRegion()) &&

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

430

431 V.emplace_back(Referrer, Referred);

432 return true;

433 }

434 return false;

435 }

436

437

438

439

440

441 void recordInInvalidatedRegions(const MemRegion *Region) {

444 }

445

446 public:

448

449

450

451

453

455 : Ctx(CC), PoppedFrame(CC.getStackFrame()), TopFrame(TopFrame) {}

456

458 SVal Val) override {

459 recordInInvalidatedRegions(Region);

461 if (!VR)

462 return true;

463

464 if (checkForDanglingStackVariable(Region, VR))

465 return true;

466

467

468 if (!isa_and_nonnull(

470 return true;

471 if (VR && VR->hasStackStorage() && !isNotInCurrentFrame(VR, Ctx))

472 V.emplace_back(Region, VR);

473 return true;

474 }

475 };

476

477 CallBack Cb(Ctx, ExitingTopFrame);

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

480 Cb);

481

482 if (Cb.V.empty())

483 return;

484

485

486 ExplodedNode *N = Ctx.generateNonFatalErrorNode(State, Node);

487 if (!N)

488 return;

489

490 if (!BT_stackleak)

491 BT_stackleak =

492 std::make_unique(CheckNames[CK_StackAddrEscapeChecker],

493 "Stack address leaks outside of stack frame");

494

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

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

497 const MemRegion *Referred = P.second;

499 continue;

500 }

501

502

503 const StringRef CommonSuffix =

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

506 llvm::raw_svector_ostream Out(Buf);

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

508

509 if (isa<CXXTempObjectRegion, CXXLifetimeExtendedObjectRegion>(Referrer)) {

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

511 << CommonSuffix;

513 std::make_unique(*BT_stackleak, Out.str(), N);

514 if (Range.isValid())

516 Ctx.emitReport(std::move(Report));

517 return;

518 }

519

521 if (!ReferrerVariable) {

522 continue;

523 }

524

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

527 std::make_unique(*BT_stackleak, Out.str(), N);

528 if (Range.isValid())

530

531 Ctx.emitReport(std::move(Report));

532 }

533}

534

535void ento::registerStackAddrEscapeBase(CheckerManager &mgr) {

537}

538

539bool ento::shouldRegisterStackAddrEscapeBase(const CheckerManager &mgr) {

540 return true;

541}

542

543#define REGISTER_CHECKER(name) \

544 void ento::register##name(CheckerManager &Mgr) { \

545 StackAddrEscapeChecker *Chk = Mgr.getChecker(); \

546 Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \

547 Chk->CheckNames[StackAddrEscapeChecker::CK_##name] = \

548 Mgr.getCurrentCheckerName(); \

549 } \

550 \

551 bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; }

552

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

Defines the SourceManager interface.

#define REGISTER_CHECKER(name)

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

static bool isInvalidatedSymbolRegion(const MemRegion *Region)

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

static const MemSpaceRegion * getStackOrGlobalSpaceRegion(const MemRegion *R)

static const MemRegion * getOriginBaseRegion(const MemRegion *Reg)

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

Represents a block literal declaration, which is like an unnamed FunctionDecl.

ArrayRef< Capture > captures() const

SourceRange getSourceRange() const override LLVM_READONLY

Source range that this declaration covers.

CompoundLiteralExpr - [C99 6.5.2.5].

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.

One of these records is kept for each identifier that is lexed.

IdentifierInfo & get(StringRef Name)

Return the identifier token info for the specified named identifier.

A (possibly-)qualified type.

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

ReturnStmt - This represents a return, optionally of an expression: return; return 4;.

Encodes a location in the source.

This class handles loading and caching of source files into memory.

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

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

Represents an abstract call to a function or method along a particular path.

CHECKER * registerChecker(AT &&... Args)

Used to register checkers.

This wrapper is used to ensure that only StringRefs originating from the CheckerRegistry are used as ...

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.

LLVM_ATTRIBUTE_RETURNS_NONNULL const MemSpaceRegion * getMemorySpace() const

bool hasStackStorage() const

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...

A Range represents the closed range [from, to].

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

const MemRegion * getAsRegion() const

virtual bool HandleBinding(StoreManager &SMgr, Store store, const MemRegion *region, SVal val)=0

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

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.

const FunctionProtoType * T