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

1

2

3

4

5

6

7

8

9

10

11

12

13

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

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

24#include

25

26using namespace clang;

27using namespace ento;

28

29namespace {

30class MacOSKeychainAPIChecker : public Checker<check::PreStmt,

31 check::PostStmt,

32 check::DeadSymbols,

33 check::PointerEscape,

34 eval::Assume> {

35 const BugType BT{this, "Improper use of SecKeychain API",

37

38public:

39

40

41 struct AllocationState {

42

43 unsigned int AllocatorIdx;

45

46 AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) :

47 AllocatorIdx(Idx),

48 Region(R) {}

49

50 bool operator==(const AllocationState &X) const {

51 return (AllocatorIdx == X.AllocatorIdx &&

52 Region == X.Region);

53 }

54

55 void Profile(llvm::FoldingSetNodeID &ID) const {

56 ID.AddInteger(AllocatorIdx);

57 ID.AddPointer(Region);

58 }

59 };

60

61 void checkPreStmt(const CallExpr *S, CheckerContext &C) const;

62 void checkPostStmt(const CallExpr *S, CheckerContext &C) const;

63 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;

66 const CallEvent *Call,

69 bool Assumption) const;

71 const char *NL, const char *Sep) const override;

72

73private:

74 typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;

75 typedef SmallVector<AllocationPair, 2> AllocationPairVec;

76

77 enum APIKind {

78

79 ValidAPI = 0,

80

81 ErrorAPI = 1,

82

83

84 PossibleAPI = 2

85 };

86

87

88 struct ADFunctionInfo {

89 const char* Name;

90 unsigned int Param;

91 unsigned int DeallocatorIdx;

92 APIKind Kind;

93 };

94 static const unsigned InvalidIdx = 100000;

95 static const unsigned FunctionsToTrackSize = 8;

96 static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];

97

98 static const unsigned NoErr = 0;

99

100

101

102 static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator);

103

104 void generateDeallocatorMismatchReport(const AllocationPair &AP,

105 const Expr *ArgExpr,

106 CheckerContext &C) const;

107

108

109 const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym,

110 CheckerContext &C) const;

111

112 std::unique_ptr

113 generateAllocatedDataNotReleasedReport(const AllocationPair &AP,

114 ExplodedNode *N,

115 CheckerContext &C) const;

116

117

118 void markInteresting(PathSensitiveBugReport *R,

119 const AllocationPair &AP) const {

122 }

123

124

125

126

127 class SecKeychainBugVisitor : public BugReporterVisitor {

128 protected:

129

131

132 public:

133 SecKeychainBugVisitor(SymbolRef S) : Sym(S) {}

134

135 void Profile(llvm::FoldingSetNodeID &ID) const override {

136 static int X = 0;

137 ID.AddPointer(&X);

138 ID.AddPointer(Sym);

139 }

140

142 BugReporterContext &BRC,

143 PathSensitiveBugReport &BR) override;

144 };

145};

146}

147

148

149

150

153 MacOSKeychainAPIChecker::AllocationState)

154

155static bool isEnclosingFunctionParam(const Expr *E) {

157 if (const DeclRefExpr *DRE = dyn_cast(E)) {

158 const ValueDecl *VD = DRE->getDecl();

160 return true;

161 }

162 return false;

163}

164

165const MacOSKeychainAPIChecker::ADFunctionInfo

166 MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {

167 {"SecKeychainItemCopyContent", 4, 3, ValidAPI},

168 {"SecKeychainFindGenericPassword", 6, 3, ValidAPI},

169 {"SecKeychainFindInternetPassword", 13, 3, ValidAPI},

170 {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI},

171 {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI},

172 {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI},

173 {"free", 0, InvalidIdx, ErrorAPI},

174 {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI},

175};

176

177unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,

178 bool IsAllocator) {

179 for (unsigned I = 0; I < FunctionsToTrackSize; ++I) {

180 ADFunctionInfo FI = FunctionsToTrack[I];

181 if (FI.Name != Name)

182 continue;

183

184 if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))

185 return InvalidIdx;

186 if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))

187 return InvalidIdx;

188

189 return I;

190 }

191

192 return InvalidIdx;

193}

194

196 if (!Arg)

197 return false;

199}

200

201

202

207

210 SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol();

211 if (sym)

212 return sym;

213 }

214 return nullptr;

215}

216

217

218

219void MacOSKeychainAPIChecker::

220 generateDeallocatorMismatchReport(const AllocationPair &AP,

221 const Expr *ArgExpr,

222 CheckerContext &C) const {

224 State = State->remove(AP.first);

225 ExplodedNode *N = C.generateNonFatalErrorNode(State);

226

227 if (!N)

228 return;

229 SmallString<80> sbuf;

230 llvm::raw_svector_ostream os(sbuf);

231 unsigned int PDeallocIdx =

232 FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;

233

234 os << "Deallocator doesn't match the allocator: '"

235 << FunctionsToTrack[PDeallocIdx].Name << "' should be used.";

236 auto Report = std::make_unique(BT, os.str(), N);

237 Report->addVisitor(std::make_unique(AP.first));

239 markInteresting(Report.get(), AP);

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

241}

242

243void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,

244 CheckerContext &C) const {

245 unsigned idx = InvalidIdx;

247

248 const FunctionDecl *FD = C.getCalleeDecl(CE);

249 if (!FD || FD->getKind() != Decl::Function)

250 return;

251

252 StringRef funName = C.getCalleeName(FD);

253 if (funName.empty())

254 return;

255

256

257 idx = getTrackedFunctionIndex(funName, true);

258 if (idx != InvalidIdx) {

259 unsigned paramIdx = FunctionsToTrack[idx].Param;

261 return;

262

263 const Expr *ArgExpr = CE->getArg(paramIdx);

265 if (const AllocationState *AS = State->get(V)) {

266

267

268 State = State->remove(V);

269 ExplodedNode *N = C.generateNonFatalErrorNode(State);

270 if (!N)

271 return;

272 SmallString<128> sbuf;

273 llvm::raw_svector_ostream os(sbuf);

274 unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;

275 os << "Allocated data should be released before another call to "

276 << "the allocator: missing a call to '"

277 << FunctionsToTrack[DIdx].Name

278 << "'.";

279 auto Report = std::make_unique(BT, os.str(), N);

280 Report->addVisitor(std::make_unique(V));

282 Report->markInteresting(AS->Region);

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

284 }

285 return;

286 }

287

288

289 idx = getTrackedFunctionIndex(funName, false);

290 if (idx == InvalidIdx)

291 return;

292

293 unsigned paramIdx = FunctionsToTrack[idx].Param;

295 return;

296

297

298 const Expr *ArgExpr = CE->getArg(paramIdx);

299 SVal ArgSVal = C.getSVal(ArgExpr);

300

301

303 return;

304

306

307

308

309 bool RegionArgIsBad = false;

310 if (!ArgSM) {

312 return;

313 RegionArgIsBad = true;

314 }

315

316

317 const AllocationState *AS = State->get(ArgSM);

318 if (!AS)

319 return;

320

321

322

323 if (RegionArgIsBad) {

324

325

326 if (isEnclosingFunctionParam(ArgExpr))

327 return;

328

329 ExplodedNode *N = C.generateNonFatalErrorNode(State);

330 if (!N)

331 return;

332 auto Report = std::make_unique(

333 BT, "Trying to free data which has not been allocated.", N);

335 if (AS)

336 Report->markInteresting(AS->Region);

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

338 return;

339 }

340

341

342 if (FunctionsToTrack[idx].Kind == PossibleAPI) {

343

344 if (funName == "CFStringCreateWithBytesNoCopy") {

346

349 const AllocationPair AP = std::make_pair(ArgSM, AS);

350 generateDeallocatorMismatchReport(AP, ArgExpr, C);

351 return;

352 }

353

354 if (const DeclRefExpr *DE = dyn_cast(DeallocatorExpr)) {

355 StringRef DeallocatorName = DE->getFoundDecl()->getName();

356 if (DeallocatorName == "kCFAllocatorDefault" ||

357 DeallocatorName == "kCFAllocatorSystemDefault" ||

358 DeallocatorName == "kCFAllocatorMalloc") {

359 const AllocationPair AP = std::make_pair(ArgSM, AS);

360 generateDeallocatorMismatchReport(AP, ArgExpr, C);

361 return;

362 }

363

364

365 if (DE->getFoundDecl()->getName() == "kCFAllocatorNull")

366 return;

367 }

368

369

370 State = State->remove(ArgSM);

371 C.addTransition(State);

372 return;

373 }

374

375 llvm_unreachable("We know of no other possible APIs.");

376 }

377

378

379

380 State = State->remove(ArgSM);

381

382

383 unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;

384 if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) {

385 const AllocationPair AP = std::make_pair(ArgSM, AS);

386 generateDeallocatorMismatchReport(AP, ArgExpr, C);

387 return;

388 }

389

390 C.addTransition(State);

391}

392

393void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,

394 CheckerContext &C) const {

396 const FunctionDecl *FD = C.getCalleeDecl(CE);

397 if (!FD || FD->getKind() != Decl::Function)

398 return;

399

400 StringRef funName = C.getCalleeName(FD);

401

402

403 unsigned idx = getTrackedFunctionIndex(funName, true);

404 if (idx == InvalidIdx)

405 return;

406

407 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);

408

409

410 if (isEnclosingFunctionParam(ArgExpr) &&

411 C.getLocationContext()->getParent() == nullptr)

412 return;

413

415

416

417

418

419

420

421

422

423

424

425

426

427 SymbolRef RetStatusSymbol = C.getSVal(CE).getAsSymbol();

428 C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol);

429

430

431 State = State->set(V, AllocationState(ArgExpr, idx,

432 RetStatusSymbol));

433 assert(State);

434 C.addTransition(State);

435 }

436}

437

438

439const ExplodedNode *

440MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N,

442 CheckerContext &C) const {

444

445

446 const ExplodedNode *AllocNode = N;

447

448 while (N) {

449 if (!N->getState()->get(Sym))

450 break;

451

452

454 if (NContext == LeakContext ||

456 AllocNode = N;

458 }

459

460 return AllocNode;

461}

462

463std::unique_ptr

464MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(

465 const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const {

466 const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];

467 SmallString<70> sbuf;

468 llvm::raw_svector_ostream os(sbuf);

469 os << "Allocated data is not released: missing a call to '"

470 << FunctionsToTrack[FI.DeallocatorIdx].Name << "'.";

471

472

473

474

475 PathDiagnosticLocation LocUsedForUniqueing;

476 const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C);

478

479 if (AllocStmt)

481 C.getSourceManager(),

483

484 auto Report = std::make_unique(

485 BT, os.str(), N, LocUsedForUniqueing,

487

488 Report->addVisitor(std::make_unique(AP.first));

489 markInteresting(Report.get(), AP);

491}

492

493

494

497 bool Assumption) const {

498 AllocatedDataTy AMap = State->get();

499 if (AMap.isEmpty())

500 return State;

501

502 auto *CondBSE = dyn_cast_or_null(Cond.getAsSymbol());

503 if (!CondBSE)

504 return State;

506 if (OpCode != BO_EQ && OpCode != BO_NE)

507 return State;

508

509

510

511 SymbolRef ReturnSymbol = nullptr;

512 if (auto *SIE = dyn_cast(CondBSE)) {

513 const llvm::APInt &RHS = SIE->getRHS();

514 bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) ||

515 (OpCode == BO_NE && RHS == NoErr);

516 if (!Assumption)

517 ErrorIsReturned = !ErrorIsReturned;

518 if (ErrorIsReturned)

519 ReturnSymbol = SIE->getLHS();

520 }

521

522 if (ReturnSymbol)

523 for (auto [Sym, AllocState] : AMap) {

524 if (ReturnSymbol == AllocState.Region)

525 State = State->remove(Sym);

526 }

527

528 return State;

529}

530

531void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,

532 CheckerContext &C) const {

534 AllocatedDataTy AMap = State->get();

535 if (AMap.isEmpty())

536 return;

537

539 AllocationPairVec Errors;

540 for (const auto &[Sym, AllocState] : AMap) {

542 continue;

543

545 State = State->remove(Sym);

546

547 ConstraintManager &CMgr = State->getConstraintManager();

548 ConditionTruthVal AllocFailed = CMgr.isNull(State, Sym);

550 continue;

551 Errors.push_back(std::make_pair(Sym, &AllocState));

552 }

553 if (!Changed) {

554

555 C.addTransition(State);

556 return;

557 }

558

559 ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());

560 if (!N)

561 return;

562

563

564 for (const auto &P : Errors)

565 C.emitReport(generateAllocatedDataNotReleasedReport(P, N, C));

566

567

568 C.addTransition(State, N);

569}

570

571ProgramStateRef MacOSKeychainAPIChecker::checkPointerEscape(

574

575

576 if (Call || Call->getDecl())

577 return State;

578

579 for (auto I : State->get()) {

581 if (Escaped.count(Sym))

582 State = State->remove(Sym);

583

584

585

586

587

588

589

590

591

592

593

594

595

596

597

598

599

600 if (const auto *SD = dyn_cast(Sym)) {

601 SymbolRef ParentSym = SD->getParentSymbol();

602 if (Escaped.count(ParentSym))

603 State = State->remove(Sym);

604 }

605 }

606 return State;

607}

608

610MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(

611 const ExplodedNode *N, BugReporterContext &BRC,

612 PathSensitiveBugReport &BR) {

613 const AllocationState *AS = N->getState()->get(Sym);

614 if (!AS)

615 return nullptr;

616 const AllocationState *ASPrev =

618 if (ASPrev)

619 return nullptr;

620

621

622

623 const CallExpr *CE =

626 assert(funDecl && "We do not support indirect function calls as of now.");

627 StringRef funName = funDecl->getName();

628

629

630 unsigned Idx = getTrackedFunctionIndex(funName, true);

631 assert(Idx != InvalidIdx && "This should be a call to an allocator.");

632 const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param);

635 return std::make_shared(Pos,

636 "Data is allocated here.");

637}

638

639void MacOSKeychainAPIChecker::printState(raw_ostream &Out,

641 const char *NL,

642 const char *Sep) const {

643

644 AllocatedDataTy AMap = State->get();

645

646 if (!AMap.isEmpty()) {

647 Out << Sep << "KeychainAPIChecker :" << NL;

648 for (SymbolRef Sym : llvm::make_first_range(AMap)) {

650 }

651 }

652}

653

654

655void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {

657}

658

659bool ento::shouldRegisterMacOSKeychainAPIChecker(const CheckerManager &mgr) {

660 return true;

661}

static bool isBadDeallocationArgument(const MemRegion *Arg)

Definition MacOSKeychainAPIChecker.cpp:195

static SymbolRef getAsPointeeSymbol(const Expr *Expr, CheckerContext &C)

Given the address expression, retrieve the value it's pointing to.

Definition MacOSKeychainAPIChecker.cpp:203

#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)

Declares an immutable map of type NameTy, suitable for placement into the ProgramState.

BinaryOperatorKind Opcode

Expr * getArg(unsigned Arg)

getArg - Return the specified argument.

FunctionDecl * getDirectCallee()

If the callee is a FunctionDecl, return it. Otherwise return null.

unsigned getNumArgs() const

getNumArgs - Return the number of actual arguments to this call.

A reference to a declared variable, function, enum, etc.

This represents one expression.

Expr * IgnoreParenCasts() LLVM_READONLY

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

@ NPC_ValueDependentIsNotNull

Specifies that a value-dependent expression should be considered to never be a null pointer constant.

NullPointerConstantKind isNullPointerConstant(ASTContext &Ctx, NullPointerConstantValueDependence NPC) const

isNullPointerConstant - C99 6.3.2.3p3 - Test if this reduces down to a Null pointer constant.

bool isParentOf(const LocationContext *LC) const

const Decl * getDecl() const

StringRef getName() const

Get the name of identifier for this declaration as a StringRef.

T castAs() const

Convert to the specified ProgramPoint type, asserting that this ProgramPoint is of the desired type.

SourceRange getSourceRange() const LLVM_READONLY

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

Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...

const SourceManager & getSourceManager() const

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

Register a single-part checker (derived from Checker): construct its singleton instance,...

Simple checker classes that implement one frontend (i.e.

bool isConstrainedTrue() const

Return true if the constraint is perfectly constrained to 'true'.

const ProgramStateRef & getState() const

pred_iterator pred_begin()

const Stmt * getStmtForDiagnostics() const

If the node's program point corresponds to a statement, retrieve that statement.

ProgramPoint getLocation() const

getLocation - Returns the edge associated with the given node.

const LocationContext * getLocationContext() const

ExplodedNode * getFirstPred()

MemRegion - The root abstract class for all memory regions.

static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)

Create a location for the beginning of the declaration.

void markInteresting(SymbolRef sym, bugreporter::TrackingKind TKind=bugreporter::TrackingKind::Thorough)

Marks a symbol as interesting.

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

std::optional< T > getAs() const

Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.

SymbolRef getAsLocSymbol(bool IncludeBaseRegions=false) const

If this SVal is a location and wraps a symbol, return that SymbolRef.

const MemRegion * getAsRegion() const

virtual void dumpToStream(raw_ostream &os) const

bool isDead(SymbolRef sym)

Returns whether or not a symbol has been confirmed dead.

const char *const AppleAPIMisuse

PointerEscapeKind

Describes the different reasons a pointer escapes during analysis.

llvm::DenseSet< SymbolRef > InvalidatedSymbols

IntrusiveRefCntPtr< const ProgramState > ProgramStateRef

const SymExpr * SymbolRef

std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef

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

bool isa(CodeGen::Address addr)

bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)

nullptr

This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...

U cast(CodeGen::Address addr)