1 //== ValistChecker.cpp - stdarg.h macro usage checker -----------*- C++ -*--==// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This defines checkers which detect usage of uninitialized va_list values 11 // and va_start calls with no matching va_end. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "ClangSACheckers.h" 16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 17 #include "clang/StaticAnalyzer/Core/Checker.h" 18 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 19 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 21 22 using namespace clang; 23 using namespace ento; 24 25 REGISTER_SET_WITH_PROGRAMSTATE(InitializedVALists, const MemRegion *) 26 27 namespace { 28 typedef SmallVector<const MemRegion *, 2> RegionVector; 29 30 class ValistChecker : public Checker<check::PreCall, check::PreStmt<VAArgExpr>, 31 check::DeadSymbols> { 32 mutable std::unique_ptr<BugType> BT_leakedvalist, BT_uninitaccess; 33 34 struct VAListAccepter { 35 CallDescription Func; 36 int VAListPos; 37 }; 38 static const SmallVector<VAListAccepter, 15> VAListAccepters; 39 static const CallDescription VaStart, VaEnd, VaCopy; 40 41 public: 42 enum CheckKind { 43 CK_Uninitialized, 44 CK_Unterminated, 45 CK_CopyToSelf, 46 CK_NumCheckKinds 47 }; 48 49 DefaultBool ChecksEnabled[CK_NumCheckKinds]; 50 CheckName CheckNames[CK_NumCheckKinds]; 51 52 void checkPreStmt(const VAArgExpr *VAA, CheckerContext &C) const; 53 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 54 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; 55 56 private: 57 const MemRegion *getVAListAsRegion(SVal SV, const Expr *VAExpr, 58 bool &IsSymbolic, CheckerContext &C) const; 59 const ExplodedNode *getStartCallSite(const ExplodedNode *N, 60 const MemRegion *Reg) const; 61 62 void reportUninitializedAccess(const MemRegion *VAList, StringRef Msg, 63 CheckerContext &C) const; 64 void reportLeakedVALists(const RegionVector &LeakedVALists, StringRef Msg1, 65 StringRef Msg2, CheckerContext &C, ExplodedNode *N, 66 bool ReportUninit = false) const; 67 68 void checkVAListStartCall(const CallEvent &Call, CheckerContext &C, 69 bool IsCopy) const; 70 void checkVAListEndCall(const CallEvent &Call, CheckerContext &C) const; 71 72 class ValistBugVisitor : public BugReporterVisitor { 73 public: 74 ValistBugVisitor(const MemRegion *Reg, bool IsLeak = false) 75 : Reg(Reg), IsLeak(IsLeak) {} 76 void Profile(llvm::FoldingSetNodeID &ID) const override { 77 static int X = 0; 78 ID.AddPointer(&X); 79 ID.AddPointer(Reg); 80 } 81 std::shared_ptr<PathDiagnosticPiece> 82 getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode, 83 BugReport &BR) override { 84 if (!IsLeak) 85 return nullptr; 86 87 PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath( 88 EndPathNode, BRC.getSourceManager()); 89 // Do not add the statement itself as a range in case of leak. 90 return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(), false); 91 } 92 std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, 93 BugReporterContext &BRC, 94 BugReport &BR) override; 95 96 private: 97 const MemRegion *Reg; 98 bool IsLeak; 99 }; 100 }; 101 102 const SmallVector<ValistChecker::VAListAccepter, 15> 103 ValistChecker::VAListAccepters = { 104 {{"vfprintf", 3}, 2}, 105 {{"vfscanf", 3}, 2}, 106 {{"vprintf", 2}, 1}, 107 {{"vscanf", 2}, 1}, 108 {{"vsnprintf", 4}, 3}, 109 {{"vsprintf", 3}, 2}, 110 {{"vsscanf", 3}, 2}, 111 {{"vfwprintf", 3}, 2}, 112 {{"vfwscanf", 3}, 2}, 113 {{"vwprintf", 2}, 1}, 114 {{"vwscanf", 2}, 1}, 115 {{"vswprintf", 4}, 3}, 116 // vswprintf is the wide version of vsnprintf, 117 // vsprintf has no wide version 118 {{"vswscanf", 3}, 2}}; 119 const CallDescription ValistChecker::VaStart("__builtin_va_start", 2), 120 ValistChecker::VaCopy("__builtin_va_copy", 2), 121 ValistChecker::VaEnd("__builtin_va_end", 1); 122 } // end anonymous namespace 123 124 void ValistChecker::checkPreCall(const CallEvent &Call, 125 CheckerContext &C) const { 126 if (!Call.isGlobalCFunction()) 127 return; 128 if (Call.isCalled(VaStart)) 129 checkVAListStartCall(Call, C, false); 130 else if (Call.isCalled(VaCopy)) 131 checkVAListStartCall(Call, C, true); 132 else if (Call.isCalled(VaEnd)) 133 checkVAListEndCall(Call, C); 134 else { 135 for (auto FuncInfo : VAListAccepters) { 136 if (!Call.isCalled(FuncInfo.Func)) 137 continue; 138 bool Symbolic; 139 const MemRegion *VAList = 140 getVAListAsRegion(Call.getArgSVal(FuncInfo.VAListPos), 141 Call.getArgExpr(FuncInfo.VAListPos), Symbolic, C); 142 if (!VAList) 143 return; 144 145 if (C.getState()->contains<InitializedVALists>(VAList)) 146 return; 147 148 // We did not see va_start call, but the source of the region is unknown. 149 // Be conservative and assume the best. 150 if (Symbolic) 151 return; 152 153 SmallString<80> Errmsg("Function '"); 154 Errmsg += FuncInfo.Func.getFunctionName(); 155 Errmsg += "' is called with an uninitialized va_list argument"; 156 reportUninitializedAccess(VAList, Errmsg.c_str(), C); 157 break; 158 } 159 } 160 } 161 162 const MemRegion *ValistChecker::getVAListAsRegion(SVal SV, const Expr *E, 163 bool &IsSymbolic, 164 CheckerContext &C) const { 165 const MemRegion *Reg = SV.getAsRegion(); 166 if (!Reg) 167 return nullptr; 168 // TODO: In the future this should be abstracted away by the analyzer. 169 bool VaListModelledAsArray = false; 170 if (const auto *Cast = dyn_cast<CastExpr>(E)) { 171 QualType Ty = Cast->getType(); 172 VaListModelledAsArray = 173 Ty->isPointerType() && Ty->getPointeeType()->isRecordType(); 174 } 175 if (const auto *DeclReg = Reg->getAs<DeclRegion>()) { 176 if (isa<ParmVarDecl>(DeclReg->getDecl())) 177 Reg = C.getState()->getSVal(SV.castAs<Loc>()).getAsRegion(); 178 } 179 IsSymbolic = Reg && Reg->getAs<SymbolicRegion>(); 180 // Some VarRegion based VA lists reach here as ElementRegions. 181 const auto *EReg = dyn_cast_or_null<ElementRegion>(Reg); 182 return (EReg && VaListModelledAsArray) ? EReg->getSuperRegion() : Reg; 183 } 184 185 void ValistChecker::checkPreStmt(const VAArgExpr *VAA, 186 CheckerContext &C) const { 187 ProgramStateRef State = C.getState(); 188 const Expr *VASubExpr = VAA->getSubExpr(); 189 SVal VAListSVal = C.getSVal(VASubExpr); 190 bool Symbolic; 191 const MemRegion *VAList = 192 getVAListAsRegion(VAListSVal, VASubExpr, Symbolic, C); 193 if (!VAList) 194 return; 195 if (Symbolic) 196 return; 197 if (!State->contains<InitializedVALists>(VAList)) 198 reportUninitializedAccess( 199 VAList, "va_arg() is called on an uninitialized va_list", C); 200 } 201 202 void ValistChecker::checkDeadSymbols(SymbolReaper &SR, 203 CheckerContext &C) const { 204 ProgramStateRef State = C.getState(); 205 InitializedVAListsTy TrackedVALists = State->get<InitializedVALists>(); 206 RegionVector LeakedVALists; 207 for (auto Reg : TrackedVALists) { 208 if (SR.isLiveRegion(Reg)) 209 continue; 210 LeakedVALists.push_back(Reg); 211 State = State->remove<InitializedVALists>(Reg); 212 } 213 if (ExplodedNode *N = C.addTransition(State)) 214 reportLeakedVALists(LeakedVALists, "Initialized va_list", " is leaked", C, 215 N); 216 } 217 218 // This function traverses the exploded graph backwards and finds the node where 219 // the va_list is initialized. That node is used for uniquing the bug paths. 220 // It is not likely that there are several different va_lists that belongs to 221 // different stack frames, so that case is not yet handled. 222 const ExplodedNode * 223 ValistChecker::getStartCallSite(const ExplodedNode *N, 224 const MemRegion *Reg) const { 225 const LocationContext *LeakContext = N->getLocationContext(); 226 const ExplodedNode *StartCallNode = N; 227 228 bool FoundInitializedState = false; 229 230 while (N) { 231 ProgramStateRef State = N->getState(); 232 if (!State->contains<InitializedVALists>(Reg)) { 233 if (FoundInitializedState) 234 break; 235 } else { 236 FoundInitializedState = true; 237 } 238 const LocationContext *NContext = N->getLocationContext(); 239 if (NContext == LeakContext || NContext->isParentOf(LeakContext)) 240 StartCallNode = N; 241 N = N->pred_empty() ? nullptr : *(N->pred_begin()); 242 } 243 244 return StartCallNode; 245 } 246 247 void ValistChecker::reportUninitializedAccess(const MemRegion *VAList, 248 StringRef Msg, 249 CheckerContext &C) const { 250 if (!ChecksEnabled[CK_Uninitialized]) 251 return; 252 if (ExplodedNode *N = C.generateErrorNode()) { 253 if (!BT_uninitaccess) 254 BT_uninitaccess.reset(new BugType(CheckNames[CK_Uninitialized], 255 "Uninitialized va_list", 256 categories::MemoryError)); 257 auto R = llvm::make_unique<BugReport>(*BT_uninitaccess, Msg, N); 258 R->markInteresting(VAList); 259 R->addVisitor(llvm::make_unique<ValistBugVisitor>(VAList)); 260 C.emitReport(std::move(R)); 261 } 262 } 263 264 void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists, 265 StringRef Msg1, StringRef Msg2, 266 CheckerContext &C, ExplodedNode *N, 267 bool ReportUninit) const { 268 if (!(ChecksEnabled[CK_Unterminated] || 269 (ChecksEnabled[CK_Uninitialized] && ReportUninit))) 270 return; 271 for (auto Reg : LeakedVALists) { 272 if (!BT_leakedvalist) { 273 // FIXME: maybe creating a new check name for this type of bug is a better 274 // solution. 275 BT_leakedvalist.reset( 276 new BugType(CheckNames[CK_Unterminated].getName().empty() 277 ? CheckNames[CK_Uninitialized] 278 : CheckNames[CK_Unterminated], 279 "Leaked va_list", categories::MemoryError)); 280 BT_leakedvalist->setSuppressOnSink(true); 281 } 282 283 const ExplodedNode *StartNode = getStartCallSite(N, Reg); 284 PathDiagnosticLocation LocUsedForUniqueing; 285 286 if (const Stmt *StartCallStmt = PathDiagnosticLocation::getStmt(StartNode)) 287 LocUsedForUniqueing = PathDiagnosticLocation::createBegin( 288 StartCallStmt, C.getSourceManager(), StartNode->getLocationContext()); 289 290 SmallString<100> Buf; 291 llvm::raw_svector_ostream OS(Buf); 292 OS << Msg1; 293 std::string VariableName = Reg->getDescriptiveName(); 294 if (!VariableName.empty()) 295 OS << " " << VariableName; 296 OS << Msg2; 297 298 auto R = llvm::make_unique<BugReport>( 299 *BT_leakedvalist, OS.str(), N, LocUsedForUniqueing, 300 StartNode->getLocationContext()->getDecl()); 301 R->markInteresting(Reg); 302 R->addVisitor(llvm::make_unique<ValistBugVisitor>(Reg, true)); 303 C.emitReport(std::move(R)); 304 } 305 } 306 307 void ValistChecker::checkVAListStartCall(const CallEvent &Call, 308 CheckerContext &C, bool IsCopy) const { 309 bool Symbolic; 310 const MemRegion *VAList = 311 getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), Symbolic, C); 312 if (!VAList) 313 return; 314 315 ProgramStateRef State = C.getState(); 316 317 if (IsCopy) { 318 const MemRegion *Arg2 = 319 getVAListAsRegion(Call.getArgSVal(1), Call.getArgExpr(1), Symbolic, C); 320 if (Arg2) { 321 if (ChecksEnabled[CK_CopyToSelf] && VAList == Arg2) { 322 RegionVector LeakedVALists{VAList}; 323 if (ExplodedNode *N = C.addTransition(State)) 324 reportLeakedVALists(LeakedVALists, "va_list", 325 " is copied onto itself", C, N, true); 326 return; 327 } else if (!State->contains<InitializedVALists>(Arg2) && !Symbolic) { 328 if (State->contains<InitializedVALists>(VAList)) { 329 State = State->remove<InitializedVALists>(VAList); 330 RegionVector LeakedVALists{VAList}; 331 if (ExplodedNode *N = C.addTransition(State)) 332 reportLeakedVALists(LeakedVALists, "Initialized va_list", 333 " is overwritten by an uninitialized one", C, N, 334 true); 335 } else { 336 reportUninitializedAccess(Arg2, "Uninitialized va_list is copied", C); 337 } 338 return; 339 } 340 } 341 } 342 if (State->contains<InitializedVALists>(VAList)) { 343 RegionVector LeakedVALists{VAList}; 344 if (ExplodedNode *N = C.addTransition(State)) 345 reportLeakedVALists(LeakedVALists, "Initialized va_list", 346 " is initialized again", C, N); 347 return; 348 } 349 350 State = State->add<InitializedVALists>(VAList); 351 C.addTransition(State); 352 } 353 354 void ValistChecker::checkVAListEndCall(const CallEvent &Call, 355 CheckerContext &C) const { 356 bool Symbolic; 357 const MemRegion *VAList = 358 getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), Symbolic, C); 359 if (!VAList) 360 return; 361 362 // We did not see va_start call, but the source of the region is unknown. 363 // Be conservative and assume the best. 364 if (Symbolic) 365 return; 366 367 if (!C.getState()->contains<InitializedVALists>(VAList)) { 368 reportUninitializedAccess( 369 VAList, "va_end() is called on an uninitialized va_list", C); 370 return; 371 } 372 ProgramStateRef State = C.getState(); 373 State = State->remove<InitializedVALists>(VAList); 374 C.addTransition(State); 375 } 376 377 std::shared_ptr<PathDiagnosticPiece> ValistChecker::ValistBugVisitor::VisitNode( 378 const ExplodedNode *N, BugReporterContext &BRC, 379 BugReport &) { 380 ProgramStateRef State = N->getState(); 381 ProgramStateRef StatePrev = N->getFirstPred()->getState(); 382 383 const Stmt *S = PathDiagnosticLocation::getStmt(N); 384 if (!S) 385 return nullptr; 386 387 StringRef Msg; 388 if (State->contains<InitializedVALists>(Reg) && 389 !StatePrev->contains<InitializedVALists>(Reg)) 390 Msg = "Initialized va_list"; 391 else if (!State->contains<InitializedVALists>(Reg) && 392 StatePrev->contains<InitializedVALists>(Reg)) 393 Msg = "Ended va_list"; 394 395 if (Msg.empty()) 396 return nullptr; 397 398 PathDiagnosticLocation Pos(S, BRC.getSourceManager(), 399 N->getLocationContext()); 400 return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true); 401 } 402 403 #define REGISTER_CHECKER(name) \ 404 void ento::register##name##Checker(CheckerManager &mgr) { \ 405 ValistChecker *checker = mgr.registerChecker<ValistChecker>(); \ 406 checker->ChecksEnabled[ValistChecker::CK_##name] = true; \ 407 checker->CheckNames[ValistChecker::CK_##name] = mgr.getCurrentCheckName(); \ 408 } 409 410 REGISTER_CHECKER(Uninitialized) 411 REGISTER_CHECKER(Unterminated) 412 REGISTER_CHECKER(CopyToSelf) 413