20
20
#include " clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
21
21
#include " clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22
22
#include " clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
23
+ #include " clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
23
24
#include " llvm/ADT/STLExtras.h"
24
25
#include " llvm/ADT/SmallString.h"
25
26
#include " llvm/ADT/StringExtras.h"
@@ -44,10 +45,23 @@ namespace {
44
45
class UnixAPIMisuseChecker
45
46
: public Checker<check::PreCall, check::ASTDecl<TranslationUnitDecl>> {
46
47
const BugType BT_open{this , " Improper use of 'open'" , categories::UnixAPI};
48
+ const BugType BT_getline{this , " Improper use of getdelim" ,
49
+ categories::UnixAPI};
47
50
const BugType BT_pthreadOnce{this , " Improper use of 'pthread_once'" ,
48
51
categories::UnixAPI};
52
+ const BugType BT_ArgumentNull{this , " NULL pointer" , categories::UnixAPI};
49
53
mutable std::optional<uint64_t > Val_O_CREAT;
50
54
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
+
51
65
public:
52
66
void checkASTDecl (const TranslationUnitDecl *TU, AnalysisManager &Mgr,
53
67
BugReporter &BR) const ;
@@ -56,6 +70,7 @@ class UnixAPIMisuseChecker
56
70
57
71
void CheckOpen (CheckerContext &C, const CallEvent &Call) const ;
58
72
void CheckOpenAt (CheckerContext &C, const CallEvent &Call) const ;
73
+ void CheckGetDelim (CheckerContext &C, const CallEvent &Call) const ;
59
74
void CheckPthreadOnce (CheckerContext &C, const CallEvent &Call) const ;
60
75
61
76
void CheckOpenVariant (CheckerContext &C, const CallEvent &Call,
@@ -95,6 +110,30 @@ class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > {
95
110
96
111
} // end anonymous namespace
97
112
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
+
98
137
void UnixAPIMisuseChecker::checkASTDecl (const TranslationUnitDecl *TU,
99
138
AnalysisManager &Mgr,
100
139
BugReporter &) const {
@@ -137,6 +176,9 @@ void UnixAPIMisuseChecker::checkPreCall(const CallEvent &Call,
137
176
138
177
else if (FName == " pthread_once" )
139
178
CheckPthreadOnce (C, Call);
179
+
180
+ else if (is_contained ({" getdelim" , " getline" }, FName))
181
+ CheckGetDelim (C, Call);
140
182
}
141
183
void UnixAPIMisuseChecker::ReportOpenBug (CheckerContext &C,
142
184
ProgramStateRef State,
@@ -215,8 +257,7 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
215
257
OS << " Call to '" << VariantName << " ' with more than " << MaxArgCount
216
258
<< " arguments" ;
217
259
218
- ReportOpenBug (C, state,
219
- SBuf.c_str (),
260
+ ReportOpenBug (C, state, SBuf.c_str (),
220
261
Call.getArgExpr (MaxArgCount)->getSourceRange ());
221
262
return ;
222
263
}
@@ -266,6 +307,93 @@ void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
266
307
}
267
308
}
268
309
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
+
269
397
// ===----------------------------------------------------------------------===//
270
398
// pthread_once
271
399
// ===----------------------------------------------------------------------===//
0 commit comments