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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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

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

27#include

28

29using namespace clang;

30using namespace ento;

31

34

35

36

37

38

39namespace {

40class NSErrorMethodChecker

41 : public Checker< check::ASTDecl > {

43

44public:

45 NSErrorMethodChecker() = default;

46

49};

50}

51

52void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D,

55 if (D->isThisDeclarationADefinition())

56 return;

57 if (D->getReturnType()->isVoidType())

58 return;

59

60 if (!II)

62

63 bool hasNSError = false;

64 for (const auto *I : D->parameters()) {

65 if (IsNSError(I->getType(), II)) {

66 hasNSError = true;

67 break;

68 }

69 }

70

71 if (hasNSError) {

72 const char *err = "Method accepting NSError** "

73 "should have a non-void return value to indicate whether or not an "

74 "error occurred";

77 BR.EmitBasicReport(D, this, "Bad return type when passing NSError**",

78 "Coding conventions (Apple)", err, L);

79 }

80}

81

82

83

84

85

86namespace {

87class CFErrorFunctionChecker

88 : public Checker< check::ASTDecl > {

90

91public:

92 CFErrorFunctionChecker() : II(nullptr) {}

93

96};

97}

98

100 if (isa(D))

101 return true;

102

103

106}

107

108void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,

111 if (D->doesThisDeclarationHaveABody())

112 return;

113 if (D->getReturnType()->isVoidType())

114 return;

116 return;

117

118 if (!II)

120

121 bool hasCFError = false;

122 for (auto *I : D->parameters()) {

123 if (IsCFError(I->getType(), II)) {

124 hasCFError = true;

125 break;

126 }

127 }

128

129 if (hasCFError) {

130 const char *err = "Function accepting CFErrorRef* "

131 "should have a non-void return value to indicate whether or not an "

132 "error occurred";

135 BR.EmitBasicReport(D, this, "Bad return type when passing CFErrorRef*",

136 "Coding conventions (Apple)", err, L);

137 }

138}

139

140

141

142

143

144namespace {

145

146class NSErrorDerefBug : public BugType {

147public:

150 "Coding conventions (Apple)") {}

151};

152

153class CFErrorDerefBug : public BugType {

154public:

157 "Coding conventions (Apple)") {}

158};

159

160}

161

162namespace {

163class NSOrCFErrorDerefChecker

164 : public Checker< check::Location,

165 check::Event > {

167 mutable std::unique_ptr NSBT;

168 mutable std::unique_ptr CFBT;

169public:

170 bool ShouldCheckNSError = false, ShouldCheckCFError = false;

172 NSOrCFErrorDerefChecker() : NSErrorII(nullptr), CFErrorII(nullptr) {}

173

174 void checkLocation(SVal loc, bool isLoad, const Stmt *S,

177};

178}

179

180typedef llvm::ImmutableMap<SymbolRef, unsigned> ErrorOutFlag;

183

184template

187 if (const unsigned *attachedFlags = state->get<T>(sym))

188 return *attachedFlags;

189 return false;

190}

191

192template

194

196 C.addTransition(state->set<T>(sym, true));

197}

198

205 stackReg = dyn_cast(VR->getMemorySpace()))

206 if (stackReg->getStackFrame() == SFC)

207 return VR->getValueType();

208 }

209

211}

212

213void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad,

214 const Stmt *S,

216 if (!isLoad)

217 return;

218 if (loc.isUndef() || !isa(loc))

219 return;

220

223

224

225

226

227

228

229

232 return;

233

234 if (!NSErrorII)

235 NSErrorII = &Ctx.Idents.get("NSError");

236 if (!CFErrorII)

237 CFErrorII = &Ctx.Idents.get("CFErrorRef");

238

239 if (ShouldCheckNSError && IsNSError(parmT, NSErrorII)) {

240 setFlag(state, state->getSVal(loc.castAs<Loc>()), C);

241 return;

242 }

243

244 if (ShouldCheckCFError && IsCFError(parmT, CFErrorII)) {

245 setFlag(state, state->getSVal(loc.castAs<Loc>()), C);

246 return;

247 }

248}

249

252 return;

253

254 SVal loc = event.Location;

257

258 bool isNSError = hasFlag(loc, state);

259 bool isCFError = false;

260 if (!isNSError)

261 isCFError = hasFlag(loc, state);

262

263 if (!(isNSError || isCFError))

264 return;

265

266

268 llvm::raw_svector_ostream os(Buf);

269

270 os << "Potential null dereference. According to coding standards ";

271 os << (isNSError

272 ? "in 'Creating and Returning NSError Objects' the parameter"

273 : "documented in CoreFoundation/CFError.h the parameter");

274

275 os << " may be null";

276

278 if (isNSError) {

279 if (!NSBT)

280 NSBT.reset(new NSErrorDerefBug(NSErrorName));

281 bug = NSBT.get();

282 }

283 else {

284 if (!CFBT)

285 CFBT.reset(new CFErrorDerefBug(CFErrorName));

286 bug = CFBT.get();

287 }

289 std::make_unique(*bug, os.str(), event.SinkNode));

290}

291

293

295 if (!PPT)

296 return false;

297

300

301 if (!PT)

302 return false;

303

305

306

307 if (ID)

308 return II == ID->getIdentifier();

309

310 return false;

311}

312

315 if (!PPT) return false;

316

318 if (!TT) return false;

319

321}

322

323void ento::registerNSOrCFErrorDerefChecker(CheckerManager &mgr) {

325}

326

327bool ento::shouldRegisterNSOrCFErrorDerefChecker(const CheckerManager &mgr) {

328 return true;

329}

330

331void ento::registerNSErrorChecker(CheckerManager &mgr) {

333 NSOrCFErrorDerefChecker *checker = mgr.getChecker();

334 checker->ShouldCheckNSError = true;

336}

337

338bool ento::shouldRegisterNSErrorChecker(const CheckerManager &mgr) {

339 return true;

340}

341

342void ento::registerCFErrorChecker(CheckerManager &mgr) {

344 NSOrCFErrorDerefChecker *checker = mgr.getChecker();

345 checker->ShouldCheckCFError = true;

347}

348

349bool ento::shouldRegisterCFErrorChecker(const CheckerManager &mgr) {

350 return true;

351}

static bool hasFlag(SVal val, ProgramStateRef state)

static bool IsCFError(QualType T, IdentifierInfo *II)

static void setFlag(ProgramStateRef state, SVal val, CheckerContext &C)

static QualType parameterTypeFromSVal(SVal val, CheckerContext &C)

static bool IsNSError(QualType T, IdentifierInfo *II)

static bool hasReservedReturnType(const FunctionDecl *D)

llvm::ImmutableMap< SymbolRef, unsigned > ErrorOutFlag

#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)

Declares a program state trait for type Type called Name, and introduce a type named NameTy.

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

ASTContext & getASTContext() const LLVM_READONLY

Represents a function declaration or definition.

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.

IdentifierInfo * getIdentifier() const

Get the identifier that names this declaration, if there is one.

Represents an ObjC class declaration.

ObjCMethodDecl - Represents an instance or class method declaration.

Represents a pointer to an Objective C object.

ObjCInterfaceDecl * getInterfaceDecl() const

If this pointer points to an Objective @interface type, gets the declaration for that interface.

PointerType - C99 6.7.5.1 - Pointer Declarators.

QualType getPointeeType() const

A (possibly-)qualified type.

bool isNull() const

Return true if this QualType doesn't point to a type yet.

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

Stmt - This represents one statement.

const T * getAs() const

Member-template getAs'.

TypedefNameDecl * getDecl() const

BugReporter is a utility class for generating PathDiagnostics for analysis.

const SourceManager & getSourceManager()

void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges={}, ArrayRef< FixItHint > Fixits={})

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

Add the given report to the set of reports tracked by BugReporter.

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

Used to register checkers.

CheckerNameRef getCurrentCheckerName() const

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

MemRegion - The root abstract class for all memory regions.

const RegionTy * getAs() const

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

Create a location corresponding to the given declaration.

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

SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const

If this SVal wraps a symbol return that SymbolRef.

std::optional< T > getAs() const

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

T castAs() const

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

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

const FunctionProtoType * T

We dereferenced a location that may be null.