Index: include/clang/StaticAnalyzer/Core/BugReporter/BugType.h =================================================================== --- include/clang/StaticAnalyzer/Core/BugReporter/BugType.h +++ include/clang/StaticAnalyzer/Core/BugReporter/BugType.h @@ -32,15 +32,19 @@ const CheckName Check; const std::string Name; const std::string Category; - bool SuppressonSink; + bool SuppressOnSink; virtual void anchor(); public: - BugType(class CheckName check, StringRef name, StringRef cat) - : Check(check), Name(name), Category(cat), SuppressonSink(false) {} - BugType(const CheckerBase *checker, StringRef name, StringRef cat) - : Check(checker->getCheckName()), Name(name), Category(cat), - SuppressonSink(false) {} + BugType(class CheckName Check, StringRef Name, StringRef Cat) + : Check(Check), Name(Name), Category(Cat), SuppressOnSink(false) { + assert(!getCheckName().empty() && "Check name is not set properly."); + } + BugType(const CheckerBase *Checker, StringRef Name, StringRef Cat) + : Check(Checker->getCheckName()), Name(Name), Category(Cat), + SuppressOnSink(false) { + assert(!getCheckName().empty() && "Check name is not set properly."); + } virtual ~BugType() {} // FIXME: Should these be made strings as well? @@ -51,8 +55,8 @@ /// isSuppressOnSink - Returns true if bug reports associated with this bug /// type should be suppressed if the end node of the report is post-dominated /// by a sink node. - bool isSuppressOnSink() const { return SuppressonSink; } - void setSuppressOnSink(bool x) { SuppressonSink = x; } + bool isSuppressOnSink() const { return SuppressOnSink; } + void setSuppressOnSink(bool x) { SuppressOnSink = x; } virtual void FlushReports(BugReporter& BR); }; @@ -74,7 +78,7 @@ StringRef getDescription() const { return desc; } }; -} // end GR namespace +} // end ento namespace } // end clang namespace #endif Index: lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp +++ lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp @@ -26,14 +26,13 @@ namespace { -class BlockInCriticalSectionChecker : public Checker { +class BlockInCriticalSectionChecker : public Checker { CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn, PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn, MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock; - std::unique_ptr BlockInCritSectionBugType; + mutable std::unique_ptr BlockInCritSectionBugType; void reportBlockInCritSection(SymbolRef FileDescSym, const CallEvent &call, @@ -46,8 +45,6 @@ bool isLockFunction(const CallEvent &Call) const; bool isUnlockFunction(const CallEvent &Call) const; - void checkPreCall(const CallEvent &Call, CheckerContext &C) const; - /// Process unlock. /// Process lock. /// Process blocking functions (sleep, getc, fgets, read, recv) @@ -64,16 +61,9 @@ FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"), PthreadLockFn("pthread_mutex_lock"), PthreadTryLockFn("pthread_mutex_trylock"), - PthreadUnlockFn("pthread_mutex_unlock"), - MtxLock("mtx_lock"), - MtxTimedLock("mtx_timedlock"), - MtxTryLock("mtx_trylock"), - MtxUnlock("mtx_unlock") { - // Initialize the bug type. - BlockInCritSectionBugType.reset( - new BugType(this, "Call to blocking function in critical section", - "Blocking Error")); -} + PthreadUnlockFn("pthread_mutex_unlock"), MtxLock("mtx_lock"), + MtxTimedLock("mtx_timedlock"), MtxTryLock("mtx_trylock"), + MtxUnlock("mtx_unlock") {} bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const { if (Call.isCalled(SleepFn) @@ -107,10 +97,6 @@ return false; } -void BlockInCriticalSectionChecker::checkPreCall(const CallEvent &Call, - CheckerContext &C) const { -} - void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { if (!isBlockingFunction(Call) @@ -142,7 +128,13 @@ llvm::raw_string_ostream os(msg); os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName() << "' inside of critical section"; - auto R = llvm::make_unique(*BlockInCritSectionBugType, os.str(), ErrNode); + if (!BlockInCritSectionBugType) { + BlockInCritSectionBugType.reset( + new BugType(this, "Call to blocking function in critical section", + "Blocking Error")); + } + auto R = llvm::make_unique(*BlockInCritSectionBugType, os.str(), + ErrNode); R->addRange(Call.getSourceRange()); R->markInteresting(BlockDescSym); C.emitReport(std::move(R)); Index: lib/StaticAnalyzer/Checkers/CStringChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -305,6 +305,9 @@ ProgramStateRef StInBound = state->assumeInBound(Idx, Size, true); ProgramStateRef StOutBound = state->assumeInBound(Idx, Size, false); if (StOutBound && !StInBound) { + if (!Filter.CheckCStringOutOfBounds) + return state; + ExplodedNode *N = C.generateErrorNode(StOutBound); if (!N) return nullptr; Index: lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -104,9 +104,9 @@ mutable Selector DeallocSel, ReleaseSel; - std::unique_ptr MissingReleaseBugType; - std::unique_ptr ExtraReleaseBugType; - std::unique_ptr MistakenDeallocBugType; + mutable std::unique_ptr MissingReleaseBugType; + mutable std::unique_ptr ExtraReleaseBugType; + mutable std::unique_ptr MistakenDeallocBugType; public: ObjCDeallocChecker(); @@ -585,6 +585,11 @@ OS << " by a synthesized property but not released" " before '[super dealloc]'"; + if (!MissingReleaseBugType) + MissingReleaseBugType.reset( + new BugType(this, "Missing ivar release (leak)", + categories::MemoryCoreFoundationObjectiveC)); + std::unique_ptr BR( new BugReport(*MissingReleaseBugType, OS.str(), ErrNode)); @@ -708,6 +713,11 @@ OS << " property but was released in 'dealloc'"; } + if (!ExtraReleaseBugType) + ExtraReleaseBugType.reset( + new BugType(this, "Extra ivar release", + categories::MemoryCoreFoundationObjectiveC)); + std::unique_ptr BR( new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode)); BR->addRange(M.getOriginExpr()->getSourceRange()); @@ -746,6 +756,10 @@ OS << "'" << *PropImpl->getPropertyIvarDecl() << "' should be released rather than deallocated"; + if (!MistakenDeallocBugType) + MistakenDeallocBugType.reset(new BugType( + this, "Mistaken dealloc", categories::MemoryCoreFoundationObjectiveC)); + std::unique_ptr BR( new BugReport(*MistakenDeallocBugType, OS.str(), ErrNode)); BR->addRange(M.getOriginExpr()->getSourceRange()); @@ -757,20 +771,7 @@ ObjCDeallocChecker::ObjCDeallocChecker() : NSObjectII(nullptr), SenTestCaseII(nullptr), XCTestCaseII(nullptr), - CIFilterII(nullptr) { - - MissingReleaseBugType.reset( - new BugType(this, "Missing ivar release (leak)", - categories::MemoryCoreFoundationObjectiveC)); - - ExtraReleaseBugType.reset( - new BugType(this, "Extra ivar release", - categories::MemoryCoreFoundationObjectiveC)); - - MistakenDeallocBugType.reset( - new BugType(this, "Mistaken dealloc", - categories::MemoryCoreFoundationObjectiveC)); -} + CIFilterII(nullptr) {} void ObjCDeallocChecker::initIdentifierInfoAndSelectors( ASTContext &Ctx) const { Index: lib/StaticAnalyzer/Checkers/IteratorChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/IteratorChecker.cpp +++ lib/StaticAnalyzer/Checkers/IteratorChecker.cpp @@ -171,7 +171,7 @@ check::DeadSymbols, eval::Assume> { - std::unique_ptr OutOfRangeBugType; + mutable std::unique_ptr OutOfRangeBugType; void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal, const SVal &RVal, OverloadedOperatorKind Op) const; @@ -184,8 +184,6 @@ CheckerContext &C, ExplodedNode *ErrNode) const; public: - IteratorChecker(); - enum CheckKind { CK_IteratorRangeChecker, CK_NumCheckKinds @@ -257,12 +255,6 @@ bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos); } // namespace -IteratorChecker::IteratorChecker() { - OutOfRangeBugType.reset( - new BugType(this, "Iterator out of range", "Misuse of STL APIs")); - OutOfRangeBugType->setSuppressOnSink(true); -} - void IteratorChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { // Check for out of range access @@ -522,6 +514,12 @@ void IteratorChecker::reportOutOfRangeBug(const StringRef &Message, const SVal &Val, CheckerContext &C, ExplodedNode *ErrNode) const { + + if (!OutOfRangeBugType) { + OutOfRangeBugType.reset( + new BugType(this, "Iterator out of range", "Misuse of STL APIs")); + OutOfRangeBugType->setSuppressOnSink(true); + } auto R = llvm::make_unique(*OutOfRangeBugType, Message, ErrNode); R->markInteresting(Val); C.emitReport(std::move(R)); Index: lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp +++ lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp @@ -88,8 +88,6 @@ Selector S) const; public: - NonLocalizedStringChecker(); - // When this parameter is set to true, the checker assumes all // methods that return NSStrings are unlocalized. Thus, more false // positives will be reported. @@ -107,11 +105,6 @@ REGISTER_MAP_WITH_PROGRAMSTATE(LocalizedMemMap, const MemRegion *, LocalizedState) -NonLocalizedStringChecker::NonLocalizedStringChecker() { - BT.reset(new BugType(this, "Unlocalizable string", - "Localizability Issue (Apple)")); -} - namespace { class NonLocalizedStringBRVisitor final : public BugReporterVisitorImpl { @@ -764,6 +757,10 @@ if (!ErrNode) return; + if (!BT) + BT.reset(new BugType(this, "Unlocalizable string", + "Localizability Issue (Apple)")); + // Generate the bug report. std::unique_ptr R(new BugReport( *BT, "User-facing text should use localized string macro", ErrNode)); Index: lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h =================================================================== --- lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h +++ lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h @@ -25,13 +25,6 @@ class MPIBugReporter { public: - MPIBugReporter(const CheckerBase &CB) { - UnmatchedWaitBugType.reset(new BugType(&CB, "Unmatched wait", MPIError)); - DoubleNonblockingBugType.reset( - new BugType(&CB, "Double nonblocking", MPIError)); - MissingWaitBugType.reset(new BugType(&CB, "Missing wait", MPIError)); - } - /// Report duplicate request use by nonblocking calls without intermediate /// wait. /// @@ -44,7 +37,8 @@ const Request &Req, const MemRegion *const RequestRegion, const ExplodedNode *const ExplNode, - BugReporter &BReporter) const; + BugReporter &BReporter, + const CheckerBase &CB) const; /// Report a missing wait for a nonblocking call. /// @@ -55,7 +49,7 @@ void reportMissingWait(const Request &Req, const MemRegion *const RequestRegion, const ExplodedNode *const ExplNode, - BugReporter &BReporter) const; + BugReporter &BReporter, const CheckerBase &CB) const; /// Report a wait on a request that has not been used at all before. /// @@ -66,15 +60,15 @@ void reportUnmatchedWait(const CallEvent &CE, const MemRegion *const RequestRegion, const ExplodedNode *const ExplNode, - BugReporter &BReporter) const; + BugReporter &BReporter, const CheckerBase &CB) const; private: const std::string MPIError = "MPI Error"; - // path-sensitive bug types - std::unique_ptr UnmatchedWaitBugType; - std::unique_ptr MissingWaitBugType; - std::unique_ptr DoubleNonblockingBugType; + // Path-sensitive bug types. + mutable std::unique_ptr UnmatchedWaitBugType; + mutable std::unique_ptr MissingWaitBugType; + mutable std::unique_ptr DoubleNonblockingBugType; /// Bug visitor class to find the node where the request region was previously /// used in order to include it into the BugReport path. Index: lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp +++ lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp @@ -23,9 +23,11 @@ void MPIBugReporter::reportDoubleNonblocking( const CallEvent &MPICallEvent, const ento::mpi::Request &Req, - const MemRegion *const RequestRegion, - const ExplodedNode *const ExplNode, - BugReporter &BReporter) const { + const MemRegion *const RequestRegion, const ExplodedNode *const ExplNode, + BugReporter &BReporter, const CheckerBase &CB) const { + if (!DoubleNonblockingBugType) + DoubleNonblockingBugType.reset( + new BugType(&CB, "Double nonblocking", MPIError)); std::string ErrorText; ErrorText = "Double nonblocking on request " + @@ -47,10 +49,14 @@ BReporter.emitReport(std::move(Report)); } -void MPIBugReporter::reportMissingWait( - const ento::mpi::Request &Req, const MemRegion *const RequestRegion, - const ExplodedNode *const ExplNode, - BugReporter &BReporter) const { +void MPIBugReporter::reportMissingWait(const ento::mpi::Request &Req, + const MemRegion *const RequestRegion, + const ExplodedNode *const ExplNode, + BugReporter &BReporter, + const CheckerBase &CB) const { + if (!MissingWaitBugType) + MissingWaitBugType.reset(new BugType(&CB, "Missing wait", MPIError)); + std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() + " has no matching wait. "}; @@ -69,8 +75,11 @@ void MPIBugReporter::reportUnmatchedWait( const CallEvent &CE, const clang::ento::MemRegion *const RequestRegion, - const ExplodedNode *const ExplNode, - BugReporter &BReporter) const { + const ExplodedNode *const ExplNode, BugReporter &BReporter, + const CheckerBase &CB) const { + if (!UnmatchedWaitBugType) + UnmatchedWaitBugType.reset(new BugType(&CB, "Unmatched wait", MPIError)); + std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() + " has no matching nonblocking call. "}; Index: lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h =================================================================== --- lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h +++ lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h @@ -30,9 +30,7 @@ class MPIChecker : public Checker { public: - MPIChecker() : BReporter(*this) {} - - // path-sensitive callbacks + // Path-sensitive callbacks. void checkPreCall(const CallEvent &CE, CheckerContext &Ctx) const { dynamicInit(Ctx); checkUnmatchedWaits(CE, Ctx); Index: lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp +++ lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp @@ -44,7 +44,7 @@ if (Req && Req->CurrentState == Request::State::Nonblocking) { ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode(); BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode, - Ctx.getBugReporter()); + Ctx.getBugReporter(), *this); Ctx.addTransition(ErrorNode->getState(), ErrorNode); } // no error @@ -87,7 +87,7 @@ } // A wait has no matching nonblocking call. BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode, - Ctx.getBugReporter()); + Ctx.getBugReporter(), *this); } } @@ -121,7 +121,7 @@ State = ErrorNode->getState(); } BReporter.reportMissingWait(Req.second, Req.first, ErrorNode, - Ctx.getBugReporter()); + Ctx.getBugReporter(), *this); } State = State->remove(Req.first); } Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -2900,8 +2900,13 @@ mgr.getCurrentCheckName(); // We currently treat NewDeleteLeaks checker as a subchecker of NewDelete // checker. - if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker]) + if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker]) { checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker] = true; + // FIXME: This does not set the correct name, but without this workaround + // no name will be set at all. + checker->CheckNames[MallocChecker::CK_NewDeleteChecker] = + mgr.getCurrentCheckName(); + } } #define REGISTER_CHECKER(name) \ Index: lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp +++ lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp @@ -31,7 +31,7 @@ mutable IdentifierInfo *IIdealloc, *IINSObject; mutable Selector SELdealloc; - std::unique_ptr DoubleSuperDeallocBugType; + mutable std::unique_ptr DoubleSuperDeallocBugType; void initIdentifierInfoAndSelectors(ASTContext &Ctx) const; @@ -193,6 +193,11 @@ if (Desc.empty()) Desc = "Use of 'self' after it has been deallocated"; + if (!DoubleSuperDeallocBugType) + DoubleSuperDeallocBugType.reset( + new BugType(this, "[super dealloc] should not be called more than once", + categories::CoreFoundationObjectiveC)); + // Generate the report. std::unique_ptr BR( new BugReport(*DoubleSuperDeallocBugType, Desc, ErrNode)); @@ -220,12 +225,7 @@ } ObjCSuperDeallocChecker::ObjCSuperDeallocChecker() - : IIdealloc(nullptr), IINSObject(nullptr) { - - DoubleSuperDeallocBugType.reset( - new BugType(this, "[super dealloc] should not be called more than once", - categories::CoreFoundationObjectiveC)); -} + : IIdealloc(nullptr), IINSObject(nullptr) {} void ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(ASTContext &Ctx) const { @@ -252,7 +252,7 @@ std::shared_ptr SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ, const ExplodedNode *Pred, - BugReporterContext &BRC, BugReport &BR) { + BugReporterContext &BRC, BugReport &) { if (Satisfied) return nullptr; Index: lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -54,8 +54,8 @@ check::PointerEscape> { CallDescription OpenFn, CloseFn; - std::unique_ptr DoubleCloseBugType; - std::unique_ptr LeakBugType; + mutable std::unique_ptr DoubleCloseBugType; + mutable std::unique_ptr LeakBugType; void reportDoubleClose(SymbolRef FileDescSym, const CallEvent &Call, @@ -104,16 +104,7 @@ } // end anonymous namespace SimpleStreamChecker::SimpleStreamChecker() - : OpenFn("fopen"), CloseFn("fclose", 1) { - // Initialize the bug types. - DoubleCloseBugType.reset( - new BugType(this, "Double fclose", "Unix Stream API Error")); - - LeakBugType.reset( - new BugType(this, "Resource Leak", "Unix Stream API Error")); - // Sinks are higher importance bugs as well as calls to assert() or exit(0). - LeakBugType->setSuppressOnSink(true); -} + : OpenFn("fopen"), CloseFn("fclose", 1) {} void SimpleStreamChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { @@ -206,6 +197,10 @@ if (!ErrNode) return; + if (!DoubleCloseBugType) + DoubleCloseBugType.reset( + new BugType(this, "Double fclose", "Unix Stream API Error")); + // Generate the report. auto R = llvm::make_unique(*DoubleCloseBugType, "Closing a previously closed file stream", ErrNode); @@ -217,6 +212,13 @@ void SimpleStreamChecker::reportLeaks(ArrayRef LeakedStreams, CheckerContext &C, ExplodedNode *ErrNode) const { + + if (!LeakBugType) { + LeakBugType.reset( + new BugType(this, "Resource Leak", "Unix Stream API Error")); + // Sinks are higher importance bugs as well as calls to assert() or exit(0). + LeakBugType->setSuppressOnSink(true); + } // Attach bug reports to the leak node. // TODO: Identify the leaked file descriptor. for (SymbolRef LeakedStream : LeakedStreams) { Index: lib/StaticAnalyzer/Checkers/ValistChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/ValistChecker.cpp +++ lib/StaticAnalyzer/Checkers/ValistChecker.cpp @@ -64,7 +64,7 @@ CheckerContext &C) const; void reportLeakedVALists(const RegionVector &LeakedVALists, StringRef Msg1, StringRef Msg2, CheckerContext &C, ExplodedNode *N, - bool ForceReport = false) const; + bool ReportUninit = false) const; void checkVAListStartCall(const CallEvent &Call, CheckerContext &C, bool IsCopy) const; @@ -267,15 +267,15 @@ void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists, StringRef Msg1, StringRef Msg2, CheckerContext &C, ExplodedNode *N, - bool ForceReport) const { + bool ReportUninit) const { if (!(ChecksEnabled[CK_Unterminated] || - (ChecksEnabled[CK_Uninitialized] && ForceReport))) + (ChecksEnabled[CK_Uninitialized] && ReportUninit))) return; for (auto Reg : LeakedVALists) { if (!BT_leakedvalist) { - BT_leakedvalist.reset(new BugType(CheckNames[CK_Unterminated], - "Leaked va_list", - categories::MemoryError)); + BT_leakedvalist.reset(new BugType( + CheckNames[ReportUninit ? CK_Uninitialized : CK_Unterminated], + "Leaked va_list", categories::MemoryError)); BT_leakedvalist->setSuppressOnSink(true); } @@ -375,7 +375,7 @@ std::shared_ptr ValistChecker::ValistBugVisitor::VisitNode( const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, - BugReport &BR) { + BugReport &) { ProgramStateRef State = N->getState(); ProgramStateRef StatePrev = PrevN->getState(); Index: test/Analysis/malloc.c =================================================================== --- test/Analysis/malloc.c +++ test/Analysis/malloc.c @@ -1720,13 +1720,6 @@ } } -char *dupstrWarn(const char *s) { - const int len = strlen(s); - char *p = (char*) smallocWarn(len + 1); - strcpy(p, s); // expected-warning{{String copy function overflows destination buffer}} - return p; -} - int *radar15580979() { int *data = (int *)malloc(32); int *p = data ?: (int*)malloc(32); // no warning