| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 | |
| 14 | |
| 15 | |
| 16 | |
| 17 | |
| 18 | |
| 19 | |
| 20 | |
| 21 | |
| 22 | |
| 23 | |
| 24 | |
| 25 | |
| 26 | |
| 27 | |
| 28 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
| 29 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
| 30 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
| 31 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
| 32 | #include "clang/StaticAnalyzer/Core/Checker.h" |
| 33 | #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" |
| 34 | #include "clang/Lex/Lexer.h" |
| 35 | #include "llvm/ADT/APSInt.h" |
| 36 | |
| 37 | using namespace clang; |
| 38 | using namespace ento; |
| 39 | using namespace ast_matchers; |
| 40 | |
| 41 | namespace { |
| 42 | |
| 43 | class NumberObjectConversionChecker : public Checker<check::ASTCodeBody> { |
| 44 | public: |
| 45 | bool Pedantic; |
| 46 | |
| 47 | void checkASTCodeBody(const Decl *D, AnalysisManager &AM, |
| 48 | BugReporter &BR) const; |
| 49 | }; |
| 50 | |
| 51 | class Callback : public MatchFinder::MatchCallback { |
| 52 | const NumberObjectConversionChecker *C; |
| 53 | BugReporter &BR; |
| 54 | AnalysisDeclContext *ADC; |
| 55 | |
| 56 | public: |
| 57 | Callback(const NumberObjectConversionChecker *C, |
| 58 | BugReporter &BR, AnalysisDeclContext *ADC) |
| 59 | : C(C), BR(BR), ADC(ADC) {} |
| 60 | virtual void run(const MatchFinder::MatchResult &Result); |
| 61 | }; |
| 62 | } |
| 63 | |
| 64 | void Callback::run(const MatchFinder::MatchResult &Result) { |
| 65 | bool IsPedanticMatch = |
| 66 | (Result.Nodes.getNodeAs<Stmt>("pedantic") != nullptr); |
| 67 | if (IsPedanticMatch && !C->Pedantic) |
| 68 | return; |
| 69 | |
| 70 | ASTContext &ACtx = ADC->getASTContext(); |
| 71 | |
| 72 | if (const Expr *CheckIfNull = |
| 73 | Result.Nodes.getNodeAs<Expr>("check_if_null")) { |
| 74 | |
| 75 | |
| 76 | |
| 77 | |
| 78 | bool MacroIndicatesWeShouldSkipTheCheck = false; |
| 79 | SourceLocation Loc = CheckIfNull->getBeginLoc(); |
| 80 | if (Loc.isMacroID()) { |
| 81 | StringRef MacroName = Lexer::getImmediateMacroName( |
| 82 | Loc, ACtx.getSourceManager(), ACtx.getLangOpts()); |
| 83 | if (MacroName == "NULL" || MacroName == "nil") |
| 84 | return; |
| 85 | if (MacroName == "YES" || MacroName == "NO") |
| 86 | MacroIndicatesWeShouldSkipTheCheck = true; |
| 87 | } |
| 88 | if (!MacroIndicatesWeShouldSkipTheCheck) { |
| 89 | Expr::EvalResult EVResult; |
| 90 | if (CheckIfNull->IgnoreParenCasts()->EvaluateAsInt( |
| 91 | EVResult, ACtx, Expr::SE_AllowSideEffects)) { |
| 92 | llvm::APSInt Result = EVResult.Val.getInt(); |
| 93 | if (Result == 0) { |
| 94 | if (!C->Pedantic) |
| 95 | return; |
| 96 | IsPedanticMatch = true; |
| 97 | } |
| 98 | } |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | const Stmt *Conv = Result.Nodes.getNodeAs<Stmt>("conv"); |
| 103 | assert(Conv); |
| 104 | |
| 105 | const Expr *ConvertedCObject = Result.Nodes.getNodeAs<Expr>("c_object"); |
| 106 | const Expr *ConvertedCppObject = Result.Nodes.getNodeAs<Expr>("cpp_object"); |
| 107 | const Expr *ConvertedObjCObject = Result.Nodes.getNodeAs<Expr>("objc_object"); |
| 108 | bool IsCpp = (ConvertedCppObject != nullptr); |
| 109 | bool IsObjC = (ConvertedObjCObject != nullptr); |
| 110 | const Expr *Obj = IsObjC ? ConvertedObjCObject |
| 111 | : IsCpp ? ConvertedCppObject |
| 112 | : ConvertedCObject; |
| 113 | assert(Obj); |
| 114 | |
| 115 | bool IsComparison = |
| 116 | (Result.Nodes.getNodeAs<Stmt>("comparison") != nullptr); |
| 117 | |
| 118 | bool IsOSNumber = |
| 119 | (Result.Nodes.getNodeAs<Decl>("osnumber") != nullptr); |
| 120 | |
| 121 | bool IsInteger = |
| 122 | (Result.Nodes.getNodeAs<QualType>("int_type") != nullptr); |
| 123 | bool IsObjCBool = |
| 124 | (Result.Nodes.getNodeAs<QualType>("objc_bool_type") != nullptr); |
| 125 | bool IsCppBool = |
| 126 | (Result.Nodes.getNodeAs<QualType>("cpp_bool_type") != nullptr); |
| 127 | |
| 128 | llvm::SmallString<64> Msg; |
| 129 | llvm::raw_svector_ostream OS(Msg); |
| 130 | |
| 131 | |
| 132 | QualType ObjT = Obj->getType().getUnqualifiedType(); |
| 133 | |
| 134 | |
| 135 | if (IsCpp) { |
| 136 | isPointerType()", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp", 136, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(ObjT.getCanonicalType()->isPointerType()); |
| 137 | ObjT = ACtx.getPointerType( |
| 138 | ObjT->getPointeeType().getCanonicalType().getUnqualifiedType()); |
| 139 | } |
| 140 | |
| 141 | if (IsComparison) |
| 142 | OS << "Comparing "; |
| 143 | else |
| 144 | OS << "Converting "; |
| 145 | |
| 146 | OS << "a pointer value of type '" << ObjT.getAsString() << "' to a "; |
| 147 | |
| 148 | std::string EuphemismForPlain = "primitive"; |
| 149 | std::string SuggestedApi = IsObjC ? (IsInteger ? "" : "-boolValue") |
| 150 | : IsCpp ? (IsOSNumber ? "" : "getValue()") |
| 151 | : "CFNumberGetValue()"; |
| 152 | if (SuggestedApi.empty()) { |
| 153 | |
| 154 | |
| 155 | SuggestedApi = |
| 156 | "a method on '" + ObjT.getAsString() + "' to get the scalar value"; |
| 157 | |
| 158 | |
| 159 | |
| 160 | |
| 161 | EuphemismForPlain = "scalar"; |
| 162 | } |
| 163 | |
| 164 | if (IsInteger) |
| 165 | OS << EuphemismForPlain << " integer value"; |
| 166 | else if (IsObjCBool) |
| 167 | OS << EuphemismForPlain << " BOOL value"; |
| 168 | else if (IsCppBool) |
| 169 | OS << EuphemismForPlain << " bool value"; |
| 170 | else |
| 171 | OS << EuphemismForPlain << " boolean value"; |
| 172 | |
| 173 | |
| 174 | if (IsPedanticMatch) |
| 175 | OS << "; instead, either compare the pointer to " |
| 176 | << (IsObjC ? "nil" : IsCpp ? "nullptr" : "NULL") << " or "; |
| 177 | else |
| 178 | OS << "; did you mean to "; |
| 179 | |
| 180 | if (IsComparison) |
| 181 | OS << "compare the result of calling " << SuggestedApi; |
| 182 | else |
| 183 | OS << "call " << SuggestedApi; |
| 184 | |
| 185 | if (!IsPedanticMatch) |
| 186 | OS << "?"; |
| 187 | |
| 188 | BR.EmitBasicReport( |
| 189 | ADC->getDecl(), C, "Suspicious number object conversion", "Logic error", |
| 190 | OS.str(), |
| 191 | PathDiagnosticLocation::createBegin(Obj, BR.getSourceManager(), ADC), |
| 192 | Conv->getSourceRange()); |
| 193 | } |
| 194 | |
| 195 | void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D, |
| 196 | AnalysisManager &AM, |
| 197 | BugReporter &BR) const { |
| 198 | |
| 199 | auto CSuspiciousNumberObjectExprM = |
| 200 | expr(ignoringParenImpCasts( |
| 201 | expr(hasType( |
| 202 | typedefType(hasDeclaration(anyOf( |
| 203 | typedefDecl(hasName("CFNumberRef")), |
| 204 | typedefDecl(hasName("CFBooleanRef"))))))) |
| 205 | .bind("c_object"))); |
| 206 | |
| 207 | |
| 208 | auto CppSuspiciousNumberObjectExprM = |
| 209 | expr(ignoringParenImpCasts( |
| 210 | expr(hasType(hasCanonicalType( |
| 211 | pointerType(pointee(hasCanonicalType( |
| 212 | recordType(hasDeclaration( |
| 213 | anyOf( |
| 214 | cxxRecordDecl(hasName("OSBoolean")), |
| 215 | cxxRecordDecl(hasName("OSNumber")) |
| 216 | .bind("osnumber")))))))))) |
| 217 | .bind("cpp_object"))); |
| 218 | |
| 219 | |
| 220 | auto ObjCSuspiciousNumberObjectExprM = |
| 221 | expr(ignoringParenImpCasts( |
| 222 | expr(hasType(hasCanonicalType( |
| 223 | objcObjectPointerType(pointee( |
| 224 | qualType(hasCanonicalType( |
| 225 | qualType(hasDeclaration( |
| 226 | objcInterfaceDecl(hasName("NSNumber"))))))))))) |
| 227 | .bind("objc_object"))); |
| 228 | |
| 229 | auto SuspiciousNumberObjectExprM = anyOf( |
| 230 | CSuspiciousNumberObjectExprM, |
| 231 | CppSuspiciousNumberObjectExprM, |
| 232 | ObjCSuspiciousNumberObjectExprM); |
| 233 | |
| 234 | |
| 235 | auto AnotherSuspiciousNumberObjectExprM = |
| 236 | expr(anyOf( |
| 237 | equalsBoundNode("c_object"), |
| 238 | equalsBoundNode("objc_object"), |
| 239 | equalsBoundNode("cpp_object"))); |
| 240 | |
| 241 | |
| 242 | auto ObjCSuspiciousScalarBooleanTypeM = |
| 243 | qualType(typedefType(hasDeclaration( |
| 244 | typedefDecl(hasName("BOOL"))))).bind("objc_bool_type"); |
| 245 | |
| 246 | |
| 247 | auto SuspiciousScalarBooleanTypeM = |
| 248 | qualType(anyOf(qualType(booleanType()).bind("cpp_bool_type"), |
| 249 | ObjCSuspiciousScalarBooleanTypeM)); |
| 250 | |
| 251 | |
| 252 | |
| 253 | |
| 254 | auto SuspiciousScalarNumberTypeM = |
| 255 | qualType(hasCanonicalType(isInteger()), |
| 256 | unless(typedefType(hasDeclaration( |
| 257 | typedefDecl(matchesName("^::u?intptr_t$")))))) |
| 258 | .bind("int_type"); |
| 259 | |
| 260 | auto SuspiciousScalarTypeM = |
| 261 | qualType(anyOf(SuspiciousScalarBooleanTypeM, |
| 262 | SuspiciousScalarNumberTypeM)); |
| 263 | |
| 264 | auto SuspiciousScalarExprM = |
| 265 | expr(ignoringParenImpCasts(expr(hasType(SuspiciousScalarTypeM)))); |
| 266 | |
| 267 | auto ConversionThroughAssignmentM = |
| 268 | binaryOperator(allOf(hasOperatorName("="), |
| 269 | hasLHS(SuspiciousScalarExprM), |
| 270 | hasRHS(SuspiciousNumberObjectExprM))); |
| 271 | |
| 272 | auto ConversionThroughBranchingM = |
| 273 | ifStmt(allOf( |
| 274 | hasCondition(SuspiciousNumberObjectExprM), |
| 275 | unless(hasConditionVariableStatement(declStmt()) |
| 276 | ))).bind("pedantic"); |
| 277 | |
| 278 | auto ConversionThroughCallM = |
| 279 | callExpr(hasAnyArgument(allOf(hasType(SuspiciousScalarTypeM), |
| 280 | ignoringParenImpCasts( |
| 281 | SuspiciousNumberObjectExprM)))); |
| 282 | |
| 283 | |
| 284 | |
| 285 | |
| 286 | auto ConversionThroughEquivalenceM = |
| 287 | binaryOperator(allOf(anyOf(hasOperatorName("=="), hasOperatorName("!=")), |
| 288 | hasEitherOperand(SuspiciousNumberObjectExprM), |
| 289 | hasEitherOperand(SuspiciousScalarExprM |
| 290 | .bind("check_if_null")))) |
| 291 | .bind("comparison"); |
| 292 | |
| 293 | auto ConversionThroughComparisonM = |
| 294 | binaryOperator(allOf(anyOf(hasOperatorName(">="), hasOperatorName(">"), |
| 295 | hasOperatorName("<="), hasOperatorName("<")), |
| 296 | hasEitherOperand(SuspiciousNumberObjectExprM), |
| 297 | hasEitherOperand(SuspiciousScalarExprM))) |
| 298 | .bind("comparison"); |
| 299 | |
| 300 | auto ConversionThroughConditionalOperatorM = |
| 301 | conditionalOperator(allOf( |
| 302 | hasCondition(SuspiciousNumberObjectExprM), |
| 303 | unless(hasTrueExpression( |
| 304 | hasDescendant(AnotherSuspiciousNumberObjectExprM))), |
| 305 | unless(hasFalseExpression( |
| 306 | hasDescendant(AnotherSuspiciousNumberObjectExprM))))) |
| 307 | .bind("pedantic"); |
| 308 | |
| 309 | auto ConversionThroughExclamationMarkM = |
| 310 | unaryOperator(allOf(hasOperatorName("!"), |
| 311 | has(expr(SuspiciousNumberObjectExprM)))) |
| 312 | .bind("pedantic"); |
| 313 | |
| 314 | auto ConversionThroughExplicitBooleanCastM = |
| 315 | explicitCastExpr(allOf(hasType(SuspiciousScalarBooleanTypeM), |
| 316 | has(expr(SuspiciousNumberObjectExprM)))); |
| 317 | |
| 318 | auto ConversionThroughExplicitNumberCastM = |
| 319 | explicitCastExpr(allOf(hasType(SuspiciousScalarNumberTypeM), |
| 320 | has(expr(SuspiciousNumberObjectExprM)))); |
| 321 | |
| 322 | auto ConversionThroughInitializerM = |
| 323 | declStmt(hasSingleDecl( |
| 324 | varDecl(hasType(SuspiciousScalarTypeM), |
| 325 | hasInitializer(SuspiciousNumberObjectExprM)))); |
| 326 | |
| 327 | auto FinalM = stmt(anyOf(ConversionThroughAssignmentM, |
| 328 | ConversionThroughBranchingM, |
| 329 | ConversionThroughCallM, |
| 330 | ConversionThroughComparisonM, |
| 331 | ConversionThroughConditionalOperatorM, |
| 332 | ConversionThroughEquivalenceM, |
| 333 | ConversionThroughExclamationMarkM, |
| 334 | ConversionThroughExplicitBooleanCastM, |
| 335 | ConversionThroughExplicitNumberCastM, |
| 336 | ConversionThroughInitializerM)).bind("conv"); |
| 337 | |
| 338 | MatchFinder F; |
| 339 | Callback CB(this, BR, AM.getAnalysisDeclContext(D)); |
| 340 | |
| 341 | F.addMatcher(stmt(forEachDescendant(FinalM)), &CB); |
| 342 | F.match(*D->getBody(), AM.getASTContext()); |
| 343 | } |
| 344 | |
| 345 | void ento::registerNumberObjectConversionChecker(CheckerManager &Mgr) { |
| 346 | NumberObjectConversionChecker *Chk = |
| 347 | Mgr.registerChecker<NumberObjectConversionChecker>(); |
| 348 | Chk->Pedantic = |
| 349 | Mgr.getAnalyzerOptions().getCheckerBooleanOption(Chk, "Pedantic", false); |
| 350 | } |
| 351 | |
| 352 | bool ento::shouldRegisterNumberObjectConversionChecker(const LangOptions &LO) { |
| 353 | return true; |
| 354 | } |
| 355 | |