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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

27#include

28#include

29

30using namespace clang;

31using namespace ento;

32

33namespace {

34class CastValueChecker : public Checker<check::DeadSymbols, eval::Call> {

35 enum class CallKind { Function, Method, InstanceOf };

36

37 using CastCheck =

38 std::function<void(const CastValueChecker *, const CallEvent &Call,

40

41public:

42

43

44

45

46

47

48

49

50

51

52

53

56

57private:

58

59

61 {{CDM::SimpleFunc, {"llvm", "cast"}, 1},

62 {&CastValueChecker::evalCast, CallKind::Function}},

63 {{CDM::SimpleFunc, {"llvm", "dyn_cast"}, 1},

64 {&CastValueChecker::evalDynCast, CallKind::Function}},

65 {{CDM::SimpleFunc, {"llvm", "cast_or_null"}, 1},

66 {&CastValueChecker::evalCastOrNull, CallKind::Function}},

67 {{CDM::SimpleFunc, {"llvm", "dyn_cast_or_null"}, 1},

68 {&CastValueChecker::evalDynCastOrNull, CallKind::Function}},

69 {{CDM::CXXMethod, {"clang", "castAs"}, 0},

70 {&CastValueChecker::evalCastAs, CallKind::Method}},

71 {{CDM::CXXMethod, {"clang", "getAs"}, 0},

72 {&CastValueChecker::evalGetAs, CallKind::Method}},

73 {{CDM::SimpleFunc, {"llvm", "isa"}, 1},

74 {&CastValueChecker::evalIsa, CallKind::InstanceOf}},

75 {{CDM::SimpleFunc, {"llvm", "isa_and_nonnull"}, 1},

76 {&CastValueChecker::evalIsaAndNonNull, CallKind::InstanceOf}}};

77

94};

95}

96

98 bool CastSucceeds) {

99 if (!CastInfo)

100 return false;

101

102 return CastSucceeds ? CastInfo->fails() : CastInfo->succeeds();

103}

104

108 bool CastSucceeds, bool IsKnownCast) {

109 std::string CastToName =

112 Object = Object->IgnoreParenImpCasts();

113

114 return C.getNoteTag(

115 [=]() -> std::string {

117 llvm::raw_svector_ostream Out(Msg);

118

119 if (!IsKnownCast)

120 Out << "Assuming ";

121

122 if (const auto *DRE = dyn_cast(Object)) {

123 Out << '\'' << DRE->getDecl()->getDeclName() << '\'';

124 } else if (const auto *ME = dyn_cast(Object)) {

125 Out << (IsKnownCast ? "Field '" : "field '")

126 << ME->getMemberDecl()->getDeclName() << '\'';

127 } else {

128 Out << (IsKnownCast ? "The object" : "the object");

129 }

130

131 Out << ' ' << (CastSucceeds ? "is a" : "is not a") << " '" << CastToName

132 << '\'';

133

134 return std::string(Out.str());

135 },

136 true);

137}

138

141 const Expr *Object,

142 bool IsKnownCast) {

143 Object = Object->IgnoreParenImpCasts();

144

145 return C.getNoteTag(

146 [=]() -> std::string {

148 llvm::raw_svector_ostream Out(Msg);

149

150 if (!IsKnownCast)

151 Out << "Assuming ";

152

153 if (const auto *DRE = dyn_cast(Object)) {

154 Out << '\'' << DRE->getDecl()->getNameAsString() << '\'';

155 } else if (const auto *ME = dyn_cast(Object)) {

156 Out << (IsKnownCast ? "Field '" : "field '")

157 << ME->getMemberDecl()->getNameAsString() << '\'';

158 } else {

159 Out << (IsKnownCast ? "The object" : "the object");

160 }

161 Out << " is";

162

163 bool First = true;

164 for (QualType CastToTy: CastToTyVec) {

165 std::string CastToName =

166 CastToTy->getAsCXXRecordDecl()

167 ? CastToTy->getAsCXXRecordDecl()->getNameAsString()

168 : CastToTy.getAsString();

169 Out << ' ' << ((CastToTyVec.size() == 1) ? "not" :

170 (First ? "neither" : "nor")) << " a '" << CastToName

171 << '\'';

173 }

174

175 return std::string(Out.str());

176 },

177 true);

178}

179

180

181

182

183

194

195 llvm_unreachable("Must align towards a reference type!");

196}

197

200 bool IsNonNullReturn,

201 bool IsCheckedCast = false) {

202 ProgramStateRef State = C.getState()->assume(DV, IsNonNullParam);

203 if (!State)

204 return;

205

206 const Expr *Object;

209

210 if (Call.getNumArgs() > 0) {

211 Object = Call.getArgExpr(0);

212 CastFromTy = Call.parameters()[0]->getType();

213 } else {

214 Object = cast(&Call)->getCXXThisExpr();

215 CastFromTy = Object->getType();

218 return;

219 } else {

221 return;

222

224 }

225 }

226

230

231

232 bool CastSucceeds = IsCheckedCast || CastFromTy == CastToTy;

233 if (!CastSucceeds) {

234 if (CastInfo)

235 CastSucceeds = IsNonNullReturn && CastInfo->succeeds();

236 else

237 CastSucceeds = IsNonNullReturn;

238 }

239

240

242 C.generateSink(State, C.getPredecessor());

243 return;

244 }

245

246

247 bool IsKnownCast = CastInfo || IsCheckedCast || CastFromTy == CastToTy;

248 if (!IsKnownCast || IsCheckedCast)

250 CastSucceeds);

251

252 SVal V = CastSucceeds ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy)

253 : C.getSValBuilder().makeNullWithType(CastToTy);

254 C.addTransition(

255 State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false),

256 getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast));

257}

258

262 bool IsInstanceOf) {

264 QualType CastFromTy = Call.parameters()[0]->getType();

267 ++idx) {

270 switch (CastToTempArg.getKind()) {

271 default:

272 return;

274 CastToTyVec.push_back(CastToTempArg.getAsType());

275 break;

278 CastToTyVec.push_back(ArgInPack.getAsType());

279 break;

280 }

281 }

282

285 MR = State->getSVal(DV.castAs<Loc>()).getAsRegion();

286

288 bool IsAnyKnown = false;

289 for (QualType CastToTy: CastToTyVec) {

291 CastToTy = C.getASTContext().getPointerType(CastToTy);

294 else

295 return;

296

299

300 bool CastSucceeds;

301 if (CastInfo)

302 CastSucceeds = IsInstanceOf && CastInfo->succeeds();

303 else

304 CastSucceeds = IsInstanceOf || CastFromTy == CastToTy;

305

306

307 bool IsKnownCast = CastInfo || CastFromTy == CastToTy;

308 IsAnyKnown = IsAnyKnown || IsKnownCast;

310 if (!IsKnownCast)

312 IsInstanceOf);

313

314 if (CastSucceeds) {

316 C.addTransition(

317 NewState->BindExpr(Call.getOriginExpr(), C.getLocationContext(),

318 C.getSValBuilder().makeTruthVal(true)),

319 getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), true,

320 IsKnownCast));

321 if (IsKnownCast)

322 return;

323 } else if (CastInfo && CastInfo->succeeds()) {

324 C.generateSink(NewState, C.getPredecessor());

325 return;

326 }

327 }

328

330 C.addTransition(

331 State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),

332 C.getSValBuilder().makeTruthVal(false)),

333 getNoteTag(C, CastToTyVec, Call.getArgExpr(0), IsAnyKnown));

334 }

335}

336

337

338

339

340

344 bool IsCheckedCast = false) {

346 true, IsCheckedCast);

347}

348

353 false);

354}

355

360 C.addTransition(State->BindExpr(Call.getOriginExpr(),

361 C.getLocationContext(),

362 C.getSValBuilder().makeNullWithType(

363 Call.getOriginExpr()->getType()),

364 false),

365 C.getNoteTag("Assuming null pointer is passed into cast",

366 true));

367}

368

372}

373

374void CastValueChecker::evalDynCast(const CallEvent &Call,

379}

380

381void CastValueChecker::evalCastOrNull(const CallEvent &Call,

386}

387

388void CastValueChecker::evalDynCastOrNull(const CallEvent &Call,

394}

395

396

397

398

399

403 bool IsCheckedCast = false) {

405 true, IsCheckedCast);

406}

407

412 false);

413}

414

415void CastValueChecker::evalCastAs(const CallEvent &Call,

419}

420

425}

426

427

428

429

430

434 std::tie(NonNullState, NullState) = C.getState()->assume(DV);

435

436 if (NonNullState) {

439 }

440

441 if (NullState) {

442 C.generateSink(NullState, C.getPredecessor());

443 }

444}

445

446void CastValueChecker::evalIsaAndNonNull(const CallEvent &Call,

450 std::tie(NonNullState, NullState) = C.getState()->assume(DV);

451

452 if (NonNullState) {

455 }

456

457 if (NullState) {

459 }

460}

461

462

463

464

465

466bool CastValueChecker::evalCall(const CallEvent &Call,

468 const auto *Lookup = CDM.lookup(Call);

469 if (!Lookup)

470 return false;

471

472 const CastCheck &Check = Lookup->first;

473 CallKind Kind = Lookup->second;

474

475 std::optional DV;

476

477 switch (Kind) {

478 case CallKind::Function: {

479

480

481

482 QualType ParamT = Call.parameters()[0]->getType();

486 return false;

487 }

488

490 break;

491 }

492 case CallKind::InstanceOf: {

493

496 return false;

497

499 break;

500 }

501 case CallKind::Method:

502 const auto *InstanceCall = dyn_cast(&Call);

503 if (!InstanceCall)

504 return false;

505

507 break;

508 }

509

510 if (!DV)

511 return false;

512

513 Check(this, Call, *DV, C);

514 return true;

515}

516

517void CastValueChecker::checkDeadSymbols(SymbolReaper &SR,

520}

521

522void ento::registerCastValueChecker(CheckerManager &Mgr) {

524}

525

526bool ento::shouldRegisterCastValueChecker(const CheckerManager &mgr) {

527 return true;

528}

static bool isInfeasibleCast(const DynamicCastInfo *CastInfo, bool CastSucceeds)

static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C, bool IsNonNullParam, bool IsNonNullReturn, bool IsCheckedCast=false)

static const NoteTag * getNoteTag(CheckerContext &C, const DynamicCastInfo *CastInfo, QualType CastToTy, const Expr *Object, bool CastSucceeds, bool IsKnownCast)

static void evalNonNullParamNullReturn(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C)

static void addInstanceOfTransition(const CallEvent &Call, DefinedOrUnknownSVal DV, ProgramStateRef State, CheckerContext &C, bool IsInstanceOf)

static void evalZeroParamNonNullReturn(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C, bool IsCheckedCast=false)

static QualType alignReferenceTypes(QualType toAlign, QualType alignTowards, ASTContext &ACtx)

static void evalNullParamNullReturn(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C)

static void evalZeroParamNullReturn(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C)

static void evalNonNullParamNonNullReturn(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C, bool IsCheckedCast=false)

Defines the C++ template declaration subclasses.

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

QualType getRValueReferenceType(QualType T) const

Return the uniqued reference to the type for an rvalue reference to the specified type.

QualType getLValueReferenceType(QualType T, bool SpelledAsLValue=true) const

Return the uniqued reference to the type for an lvalue reference to the specified type.

This represents one expression.

Represents a function declaration or definition.

const TemplateArgumentList * getTemplateSpecializationArgs() const

Retrieve the template arguments used to produce this function template specialization from the primar...

std::string getNameAsString() const

Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...

A (possibly-)qualified type.

void addConst()

Add the const type qualifier to this QualType.

bool isConstQualified() const

Determine whether this type is const-qualified.

static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)

const TemplateArgument & get(unsigned Idx) const

Retrieve the template argument at a given index.

Represents a template argument.

QualType getAsType() const

Retrieve the type for a type template argument.

ArrayRef< TemplateArgument > pack_elements() const

Iterator range referencing all of the elements of a template argument pack.

@ Pack

The template argument is actually a parameter pack.

@ Type

The template argument is a type.

ArgKind getKind() const

Return the kind of stored template argument.

CXXRecordDecl * getAsCXXRecordDecl() const

Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...

bool isRValueReferenceType() const

bool isPointerType() const

bool isReferenceType() const

bool isLValueReferenceType() const

An immutable map from CallDescriptions to arbitrary data.

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

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

Used to register checkers.

MemRegion - The root abstract class for all memory regions.

The tag upon which the TagVisitor reacts.

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

const MemRegion * getAsRegion() const

T castAs() const

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

A class responsible for cleaning up unused symbols.

const DynamicCastInfo * getDynamicCastInfo(ProgramStateRef State, const MemRegion *MR, QualType CastFromTy, QualType CastToTy)

Get dynamic cast information from CastFromTy to CastToTy of MR.

ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR)

Removes the dead cast informations from State.

ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State, const MemRegion *MR, QualType CastFromTy, QualType CastToTy, bool IsCastSucceeds)

Set dynamic type and cast information of the region; return the new state.

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

@ Success

Template argument deduction was successful.