2020#include " clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
2121#include " clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
2222#include " clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
23+ #include " clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
2324#include " llvm/ADT/STLExtras.h"
2425#include " llvm/ADT/SmallString.h"
2526#include " llvm/ADT/StringExtras.h"
@@ -44,10 +45,23 @@ namespace {
4445class UnixAPIMisuseChecker
4546 : public Checker<check::PreCall, check::ASTDecl<TranslationUnitDecl>> {
4647 const BugType BT_open{this , " Improper use of 'open'" , categories::UnixAPI};
48+ const BugType BT_getline{this , " Improper use of getdelim" ,
49+ categories::UnixAPI};
4750 const BugType BT_pthreadOnce{this , " Improper use of 'pthread_once'" ,
4851 categories::UnixAPI};
52+ const BugType BT_ArgumentNull{this , " NULL pointer" , categories::UnixAPI};
4953 mutable std::optional<uint64_t > Val_O_CREAT;
5054
55+ ProgramStateRef
56+ EnsurePtrNotNull (SVal PtrVal, const Expr *PtrExpr, CheckerContext &C,
57+ ProgramStateRef State, const StringRef PtrDescr,
58+ std::optional<std::reference_wrapper<const BugType>> BT =
59+ std::nullopt ) const ;
60+
61+ ProgramStateRef EnsureGetdelimBufferAndSizeCorrect (
62+ SVal LinePtrPtrSVal, SVal SizePtrSVal, const Expr *LinePtrPtrExpr,
63+ const Expr *SizePtrExpr, CheckerContext &C, ProgramStateRef State) const ;
64+
5165public:
5266 void checkASTDecl (const TranslationUnitDecl *TU, AnalysisManager &Mgr,
5367 BugReporter &BR) const ;
@@ -56,6 +70,7 @@ class UnixAPIMisuseChecker
5670
5771 void CheckOpen (CheckerContext &C, const CallEvent &Call) const ;
5872 void CheckOpenAt (CheckerContext &C, const CallEvent &Call) const ;
73+ void CheckGetDelim (CheckerContext &C, const CallEvent &Call) const ;
5974 void CheckPthreadOnce (CheckerContext &C, const CallEvent &Call) const ;
6075
6176 void CheckOpenVariant (CheckerContext &C, const CallEvent &Call,
@@ -95,6 +110,30 @@ class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > {
95110
96111} // end anonymous namespace
97112
113+ ProgramStateRef UnixAPIMisuseChecker::EnsurePtrNotNull (
114+ SVal PtrVal, const Expr *PtrExpr, CheckerContext &C, ProgramStateRef State,
115+ const StringRef PtrDescr,
116+ std::optional<std::reference_wrapper<const BugType>> BT) const {
117+ const auto Ptr = PtrVal.getAs <DefinedSVal>();
118+ if (!Ptr)
119+ return State;
120+
121+ const auto [PtrNotNull, PtrNull] = State->assume (*Ptr);
122+ if (!PtrNotNull && PtrNull) {
123+ if (ExplodedNode *N = C.generateErrorNode (PtrNull)) {
124+ auto R = std::make_unique<PathSensitiveBugReport>(
125+ BT.value_or (std::cref (BT_ArgumentNull)),
126+ (PtrDescr + " pointer might be NULL." ).str (), N);
127+ if (PtrExpr)
128+ bugreporter::trackExpressionValue (N, PtrExpr, *R);
129+ C.emitReport (std::move (R));
130+ }
131+ return nullptr ;
132+ }
133+
134+ return PtrNotNull;
135+ }
136+
98137void UnixAPIMisuseChecker::checkASTDecl (const TranslationUnitDecl *TU,
99138 AnalysisManager &Mgr,
100139 BugReporter &) const {
@@ -137,6 +176,9 @@ void UnixAPIMisuseChecker::checkPreCall(const CallEvent &Call,
137176
138177 else if (FName == " pthread_once" )
139178 CheckPthreadOnce (C, Call);
179+
180+ else if (is_contained ({" getdelim" , " getline" }, FName))
181+ CheckGetDelim (C, Call);
140182}
141183void UnixAPIMisuseChecker::ReportOpenBug (CheckerContext &C,
142184 ProgramStateRef State,
@@ -215,8 +257,7 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
215257 OS << " Call to '" << VariantName << " ' with more than " << MaxArgCount
216258 << " arguments" ;
217259
218- ReportOpenBug (C, state,
219- SBuf.c_str (),
260+ ReportOpenBug (C, state, SBuf.c_str (),
220261 Call.getArgExpr (MaxArgCount)->getSourceRange ());
221262 return ;
222263 }
@@ -266,6 +307,93 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
266307 }
267308}
268309
310+ // ===----------------------------------------------------------------------===//
311+ // getdelim and getline
312+ // ===----------------------------------------------------------------------===//
313+
314+ ProgramStateRef UnixAPIMisuseChecker::EnsureGetdelimBufferAndSizeCorrect (
315+ SVal LinePtrPtrSVal, SVal SizePtrSVal, const Expr *LinePtrPtrExpr,
316+ const Expr *SizePtrExpr, CheckerContext &C, ProgramStateRef State) const {
317+ static constexpr llvm::StringLiteral SizeGreaterThanBufferSize =
318+ " The buffer from the first argument is smaller than the size "
319+ " specified by the second parameter" ;
320+ static constexpr llvm::StringLiteral SizeUndef =
321+ " The buffer from the first argument is not NULL, but the size specified "
322+ " by the second parameter is undefined." ;
323+
324+ auto EmitBugReport = [this , &C, SizePtrExpr, LinePtrPtrExpr](
325+ ProgramStateRef BugState, StringRef ErrMsg) {
326+ if (ExplodedNode *N = C.generateErrorNode (BugState)) {
327+ auto R = std::make_unique<PathSensitiveBugReport>(BT_getline, ErrMsg, N);
328+ bugreporter::trackExpressionValue (N, SizePtrExpr, *R);
329+ bugreporter::trackExpressionValue (N, LinePtrPtrExpr, *R);
330+ C.emitReport (std::move (R));
331+ }
332+ };
333+
334+ // We have a pointer to a pointer to the buffer, and a pointer to the size.
335+ // We want what they point at.
336+ auto LinePtrSVal = getPointeeVal (LinePtrPtrSVal, State)->getAs <DefinedSVal>();
337+ auto NSVal = getPointeeVal (SizePtrSVal, State);
338+ if (!LinePtrSVal || !NSVal || NSVal->isUnknown ())
339+ return nullptr ;
340+
341+ assert (LinePtrPtrExpr && SizePtrExpr);
342+
343+ const auto [LinePtrNotNull, LinePtrNull] = State->assume (*LinePtrSVal);
344+ if (LinePtrNotNull && !LinePtrNull) {
345+ // If `*lineptr` is not null, but `*n` is undefined, there is UB.
346+ if (NSVal->isUndef ()) {
347+ EmitBugReport (LinePtrNotNull, SizeUndef);
348+ return nullptr ;
349+ }
350+
351+ // If it is defined, and known, its size must be less than or equal to
352+ // the buffer size.
353+ auto NDefSVal = NSVal->getAs <DefinedSVal>();
354+ auto &SVB = C.getSValBuilder ();
355+ auto LineBufSize =
356+ getDynamicExtent (LinePtrNotNull, LinePtrSVal->getAsRegion (), SVB);
357+ auto LineBufSizeGtN = SVB.evalBinOp (LinePtrNotNull, BO_GE, LineBufSize,
358+ *NDefSVal, SVB.getConditionType ())
359+ .getAs <DefinedOrUnknownSVal>();
360+ if (!LineBufSizeGtN)
361+ return LinePtrNotNull;
362+ if (auto LineBufSizeOk = LinePtrNotNull->assume (*LineBufSizeGtN, true ))
363+ return LineBufSizeOk;
364+
365+ EmitBugReport (LinePtrNotNull, SizeGreaterThanBufferSize);
366+ return nullptr ;
367+ }
368+ return State;
369+ }
370+
371+ void UnixAPIMisuseChecker::CheckGetDelim (CheckerContext &C,
372+ const CallEvent &Call) const {
373+ ProgramStateRef State = C.getState ();
374+
375+ // The parameter `n` must not be NULL.
376+ SVal SizePtrSval = Call.getArgSVal (1 );
377+ State = EnsurePtrNotNull (SizePtrSval, Call.getArgExpr (1 ), C, State, " Size" );
378+ if (!State)
379+ return ;
380+
381+ // The parameter `lineptr` must not be NULL.
382+ SVal LinePtrPtrSVal = Call.getArgSVal (0 );
383+ State =
384+ EnsurePtrNotNull (LinePtrPtrSVal, Call.getArgExpr (0 ), C, State, " Line" );
385+ if (!State)
386+ return ;
387+
388+ State = EnsureGetdelimBufferAndSizeCorrect (LinePtrPtrSVal, SizePtrSval,
389+ Call.getArgExpr (0 ),
390+ Call.getArgExpr (1 ), C, State);
391+ if (!State)
392+ return ;
393+
394+ C.addTransition (State);
395+ }
396+
269397// ===----------------------------------------------------------------------===//
270398// pthread_once
271399// ===----------------------------------------------------------------------===//
0 commit comments