clang: lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
24#include "llvm/ADT/STLExtras.h"
25#include "llvm/ADT/SmallString.h"
26#include "llvm/ADT/StringExtras.h"
27#include "llvm/Support/raw_ostream.h"
28#include
29
30using namespace clang;
31using namespace ento;
32
34
35
37
38
39
41};
42
43namespace {
44
45class UnixAPIMisuseChecker
46 : public Checker<check::PreCall, check::ASTDecl> {
48 const BugType BT_getline{this, "Improper use of getdelim",
50 const BugType BT_pthreadOnce{this, "Improper use of 'pthread_once'",
53 mutable std::optional<uint64_t> Val_O_CREAT;
54
58 std::optional<std::reference_wrapper> BT =
59 std::nullopt) const;
60
62 SVal LinePtrPtrSVal, SVal SizePtrSVal, const Expr *LinePtrPtrExpr,
64
65public:
68
70
75
78
81};
82
83class UnixAPIPortabilityChecker : public Checker< check::PreStmt > {
84public:
86
87private:
88 const BugType BT_mallocZero{
89 this, "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)",
91
99
102 const Expr *arg,
103 const char *fn_name) const;
106 const unsigned numArgs,
107 const unsigned sizeArg,
108 const char *fn) const;
109};
110
111}
112
115 const StringRef PtrDescr,
116 std::optional<std::reference_wrapper> BT) const {
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(
125 BT.value_or(std::cref(BT_ArgumentNull)),
126 (PtrDescr + " pointer might be NULL.").str(), N);
127 if (PtrExpr)
129 C.emitReport(std::move(R));
130 }
131 return nullptr;
132 }
133
134 return PtrNotNull;
135}
136
140
141
143
144 if (!Val_O_CREAT) {
146 llvm::Triple::Apple)
147 Val_O_CREAT = 0x0200;
148 }
149}
150
151
152
153
154
155void UnixAPIMisuseChecker::checkPreCall(const CallEvent &Call,
157 const FunctionDecl *FD = dyn_cast_if_present(Call.getDecl());
158 if (!FD || FD->getKind() != Decl::Function)
159 return;
160
161
162
164 if (isa_and_nonnull(NamespaceCtx))
165 return;
166
167 StringRef FName = C.getCalleeName(FD);
168 if (FName.empty())
169 return;
170
171 if (FName == "open")
173
174 else if (FName == "openat")
176
177 else if (FName == "pthread_once")
178 CheckPthreadOnce(C, Call);
179
180 else if (is_contained({"getdelim", "getline"}, FName))
182}
183void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C,
185 const char *Msg,
188 if (!N)
189 return;
190
191 auto Report = std::make_unique(BT_open, Msg, N);
192 Report->addRange(SR);
193 C.emitReport(std::move(Report));
194}
195
199}
200
204}
205
206void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
209
210
211 unsigned int FlagsArgIndex;
212 const char *VariantName;
213 switch (Variant) {
215 FlagsArgIndex = 1;
216 VariantName = "open";
217 break;
219 FlagsArgIndex = 2;
220 VariantName = "openat";
221 break;
222 };
223
224
225 unsigned int MinArgCount = FlagsArgIndex + 1;
226
227
228
229 unsigned int CreateModeArgIndex = FlagsArgIndex + 1;
230
231
232 unsigned int MaxArgCount = CreateModeArgIndex + 1;
233
235
236 if (Call.getNumArgs() < MinArgCount) {
237
238 return;
239 } else if (Call.getNumArgs() == MaxArgCount) {
240 const Expr *Arg = Call.getArgExpr(CreateModeArgIndex);
244 llvm::raw_svector_ostream OS(SBuf);
245 OS << "The " << CreateModeArgIndex + 1
246 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
247 << " argument to '" << VariantName << "' is not an integer";
248
249 ReportOpenBug(C, state,
250 SBuf.c_str(),
252 return;
253 }
254 } else if (Call.getNumArgs() > MaxArgCount) {
256 llvm::raw_svector_ostream OS(SBuf);
257 OS << "Call to '" << VariantName << "' with more than " << MaxArgCount
258 << " arguments";
259
260 ReportOpenBug(C, state, SBuf.c_str(),
261 Call.getArgExpr(MaxArgCount)->getSourceRange());
262 return;
263 }
264
265 if (!Val_O_CREAT) {
266 return;
267 }
268
269
270 const Expr *oflagsEx = Call.getArgExpr(FlagsArgIndex);
271 const SVal V = Call.getArgSVal(FlagsArgIndex);
272 if (!isa(V)) {
273
274
275 return;
276 }
278 NonLoc ocreateFlag = C.getSValBuilder()
279 .makeIntVal(*Val_O_CREAT, oflagsEx->getType())
280 .castAs();
281 SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
282 oflags, ocreateFlag,
285 return;
287
288
290 std::tie(trueState, falseState) = state->assume(maskedFlags);
291
292
293
294 if (!(trueState && !falseState))
295 return;
296
297 if (Call.getNumArgs() < MaxArgCount) {
299 llvm::raw_svector_ostream OS(SBuf);
300 OS << "Call to '" << VariantName << "' requires a "
301 << CreateModeArgIndex + 1
302 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
303 << " argument when the 'O_CREAT' flag is set";
304 ReportOpenBug(C, trueState,
305 SBuf.c_str(),
307 }
308}
309
310
311
312
313
314ProgramStateRef UnixAPIMisuseChecker::EnsureGetdelimBufferAndSizeCorrect(
315 SVal LinePtrPtrSVal, SVal SizePtrSVal, const Expr *LinePtrPtrExpr,
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](
326 if (ExplodedNode *N = C.generateErrorNode(BugState)) {
327 auto R = std::make_unique(BT_getline, ErrMsg, N);
330 C.emitReport(std::move(R));
331 }
332 };
333
334
335
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
347 EmitBugReport(LinePtrNotNull, SizeUndef);
348 return nullptr;
349 }
350
351
352
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())
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
371void UnixAPIMisuseChecker::CheckGetDelim(CheckerContext &C,
374
375
376 SVal SizePtrSval = Call.getArgSVal(1);
377 State = EnsurePtrNotNull(SizePtrSval, Call.getArgExpr(1), C, State, "Size");
378 if (!State)
379 return;
380
381
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
397
398
399
400
401void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,
403
404
405
406
407 if (Call.getNumArgs() < 1)
408 return;
409
410
411
413 const MemRegion *R = Call.getArgSVal(0).getAsRegion();
414 if (!R || !isa(R->getMemorySpace()))
415 return;
416
418 if (!N)
419 return;
420
422 llvm::raw_svector_ostream os(S);
423 os << "Call to 'pthread_once' uses";
424 if (const VarRegion *VR = dyn_cast(R))
425 os << " the local variable '" << VR->getDecl()->getName() << '\'';
426 else
427 os << " stack allocated memory";
428 os << " for the \"control\" value. Using such transient memory for "
429 "the control value is potentially dangerous.";
430 if (isa(R) && isa(R->getMemorySpace()))
431 os << " Perhaps you intended to declare the variable as 'static'?";
432
433 auto report =
434 std::make_unique(BT_pthreadOnce, os.str(), N);
435 report->addRange(Call.getArgExpr(0)->getSourceRange());
436 C.emitReport(std::move(report));
437}
438
439
440
441
442
443
444
445
446
447
448
450 const SVal argVal,
453 std::tie(*trueState, *falseState) =
455
456 return (*falseState && !*trueState);
457}
458
459
460
461
462bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(
465 const Expr *arg,
466 const char *fn_name) const {
467 ExplodedNode *N = C.generateErrorNode(falseState);
468 if (!N)
469 return false;
470
472 llvm::raw_svector_ostream os(S);
473 os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
474 auto report =
475 std::make_unique(BT_mallocZero, os.str(), N);
476
477 report->addRange(arg->getSourceRange());
479 C.emitReport(std::move(report));
480
481 return true;
482}
483
484
485
486void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C,
488 const unsigned numArgs,
489 const unsigned sizeArg,
490 const char *fn) const {
491
493 return;
494
495
497 ProgramStateRef trueState = nullptr, falseState = nullptr;
499 SVal argVal = C.getSVal(arg);
500
502 return;
503
504
506 (void) ReportZeroByteAllocation(C, falseState, arg, fn);
507 return;
508 }
509
510 assert(trueState);
511 if (trueState != state)
512 C.addTransition(trueState);
513}
514
515void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C,
517 unsigned int nArgs = CE->getNumArgs();
518 if (nArgs != 2)
519 return;
520
522 ProgramStateRef trueState = nullptr, falseState = nullptr;
523
524 unsigned int i;
525 for (i = 0; i < nArgs; i++) {
527 SVal argVal = C.getSVal(arg);
529 if (i == 0)
530 continue;
531 else
532 return;
533 }
534
536 if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
537 return;
538 else if (i == 0)
539 continue;
540 else
541 return;
542 }
543 }
544
545
546 assert(trueState);
547 if (trueState != state)
548 C.addTransition(trueState);
549}
550
551void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C,
553 BasicAllocationCheck(C, CE, 1, 0, "malloc");
554}
555
556void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C,
558 BasicAllocationCheck(C, CE, 2, 1, "realloc");
559}
560
561void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C,
563 BasicAllocationCheck(C, CE, 2, 1, "reallocf");
564}
565
566void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C,
568 BasicAllocationCheck(C, CE, 1, 0, "alloca");
569}
570
571void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero(
574 BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align");
575}
576
577void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C,
579 BasicAllocationCheck(C, CE, 1, 0, "valloc");
580}
581
582void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,
585 if (!FD || FD->getKind() != Decl::Function)
586 return;
587
588
589
591 if (isa_and_nonnull(NamespaceCtx))
592 return;
593
594 StringRef FName = C.getCalleeName(FD);
595 if (FName.empty())
596 return;
597
598 if (FName == "calloc")
599 CheckCallocZero(C, CE);
600
601 else if (FName == "malloc")
602 CheckMallocZero(C, CE);
603
604 else if (FName == "realloc")
605 CheckReallocZero(C, CE);
606
607 else if (FName == "reallocf")
608 CheckReallocfZero(C, CE);
609
610 else if (FName == "alloca" || FName == "__builtin_alloca")
611 CheckAllocaZero(C, CE);
612
613 else if (FName == "__builtin_alloca_with_align")
614 CheckAllocaWithAlignZero(C, CE);
615
616 else if (FName == "valloc")
617 CheckVallocZero(C, CE);
618}
619
620
621
622
623
624#define REGISTER_CHECKER(CHECKERNAME) \
625 void ento::register##CHECKERNAME(CheckerManager &mgr) { \
626 mgr.registerChecker(); \
627 } \
628 \
629 bool ento::shouldRegister##CHECKERNAME(const CheckerManager &mgr) { \
630 return true; \
631 }
632
static bool IsZeroByteAllocation(ProgramStateRef state, const SVal argVal, ProgramStateRef *trueState, ProgramStateRef *falseState)
@ OpenAt
The variant taking a directory file descriptor and a relative path: int openat(int fd,...
@ Open
The standard open() call: int open(const char *path, int oflag, ...);.
#define REGISTER_CHECKER(CHECKERNAME)
const TargetInfo & getTargetInfo() const
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
DeclContext * getEnclosingNamespaceContext()
Retrieve the nearest enclosing namespace context.
This represents one expression.
Represents a function declaration or definition.
A (possibly-)qualified type.
A trivial tuple used to represent a source range.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
The top declaration context.
ASTContext & getASTContext() const
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Preprocessor & getPreprocessor() override
BugReporter is a utility class for generating PathDiagnostics for analysis.
Represents an abstract call to a function or method along a particular path.
bool isUndef() const =delete
bool isUnknown() const =delete
MemRegion - The root abstract class for all memory regions.
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemSpaceRegion * getMemorySpace() const
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
bool isUnknownOrUndef() const
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Defines the clang::TargetInfo interface.
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
const char *const UnixAPI
DefinedOrUnknownSVal getDynamicExtent(ProgramStateRef State, const MemRegion *MR, SValBuilder &SVB)
std::optional< SVal > getPointeeVal(SVal PtrSVal, ProgramStateRef State)
std::optional< int > tryExpandAsInteger(StringRef Macro, const Preprocessor &PP)
Try to parse the value of a defined preprocessor macro.
The JSON file list parser is used to communicate input to InstallAPI.
__DEVICE__ _Tp arg(const std::complex< _Tp > &__c)