24#include "llvm/ADT/STLExtras.h"
43 if (MacroVal.has_value())
54class UnixAPIMisuseChecker :
public Checker<check::PreCall> {
56 const BugType BT_getline{
this,
"Improper use of getdelim",
58 const BugType BT_pthreadOnce{
this,
"Improper use of 'pthread_once'",
61 const std::optional<int> Val_O_CREAT;
64 EnsurePtrNotNull(SVal PtrVal,
const Expr *PtrExpr, CheckerContext &
C,
66 std::optional<std::reference_wrapper<const BugType>> BT =
70 SVal LinePtrPtrSVal, SVal SizePtrSVal,
const Expr *LinePtrPtrExpr,
74 UnixAPIMisuseChecker(
const ASTContext &Ctx,
const Preprocessor &PP)
77 void checkASTDecl(
const TranslationUnitDecl *TU, AnalysisManager &Mgr,
78 BugReporter &BR)
const;
80 void checkPreCall(
const CallEvent &
Call, CheckerContext &
C)
const;
82 void CheckOpen(CheckerContext &
C,
const CallEvent &
Call)
const;
83 void CheckOpenAt(CheckerContext &
C,
const CallEvent &
Call)
const;
84 void CheckGetDelimOrGetline(CheckerContext &
C,
const CallEvent &
Call)
const;
85 void CheckPthreadOnce(CheckerContext &
C,
const CallEvent &
Call)
const;
87 void CheckOpenVariant(CheckerContext &
C,
const CallEvent &
Call,
90 void ReportOpenBug(CheckerContext &
C,
ProgramStateRef State,
const char *Msg,
91 SourceRange SR)
const;
94class UnixAPIPortabilityChecker :
public Checker< check::PreStmt<CallExpr> > {
96 void checkPreStmt(
const CallExpr *CE, CheckerContext &
C)
const;
99 const BugType BT_mallocZero{
100 this,
"Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)",
103 void CheckCallocZero(CheckerContext &
C,
const CallExpr *CE)
const;
104 void CheckMallocZero(CheckerContext &
C,
const CallExpr *CE)
const;
105 void CheckReallocZero(CheckerContext &
C,
const CallExpr *CE)
const;
106 void CheckReallocfZero(CheckerContext &
C,
const CallExpr *CE)
const;
107 void CheckAllocaZero(CheckerContext &
C,
const CallExpr *CE)
const;
108 void CheckAllocaWithAlignZero(CheckerContext &
C,
const CallExpr *CE)
const;
109 void CheckVallocZero(CheckerContext &
C,
const CallExpr *CE)
const;
111 bool ReportZeroByteAllocation(CheckerContext &
C,
114 const char *fn_name)
const;
115 void BasicAllocationCheck(CheckerContext &
C,
117 const unsigned numArgs,
118 const unsigned sizeArg,
119 const char *fn)
const;
126 const StringRef PtrDescr,
127 std::optional<std::reference_wrapper<const BugType>> BT)
const {
128 const auto Ptr = PtrVal.
getAs<DefinedSVal>();
132 const auto [PtrNotNull, PtrNull] = State->assume(*Ptr);
133 if (!PtrNotNull && PtrNull) {
134 if (ExplodedNode *N =
C.generateErrorNode(PtrNull)) {
135 auto R = std::make_unique<PathSensitiveBugReport>(
136 BT.value_or(std::cref(BT_ArgumentNull)),
137 (PtrDescr +
" pointer might be NULL.").str(), N);
139 C.emitReport(std::move(R));
151void UnixAPIMisuseChecker::checkPreCall(
const CallEvent &
Call,
152 CheckerContext &
C)
const {
153 const FunctionDecl *FD = dyn_cast_if_present<FunctionDecl>(
Call.getDecl());
154 if (!FD || FD->
getKind() != Decl::Function)
160 if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
163 StringRef FName =
C.getCalleeName(FD);
170 else if (FName ==
"openat")
171 CheckOpenAt(
C,
Call);
173 else if (FName ==
"pthread_once")
174 CheckPthreadOnce(
C,
Call);
176 else if (is_contained({
"getdelim",
"getline"}, FName))
177 CheckGetDelimOrGetline(
C,
Call);
179void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &
C,
182 SourceRange SR)
const {
183 ExplodedNode *N =
C.generateErrorNode(State);
187 auto Report = std::make_unique<PathSensitiveBugReport>(BT_open, Msg, N);
189 C.emitReport(std::move(
Report));
192void UnixAPIMisuseChecker::CheckOpen(CheckerContext &
C,
193 const CallEvent &
Call)
const {
197void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &
C,
198 const CallEvent &
Call)
const {
202void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &
C,
203 const CallEvent &
Call,
207 unsigned int FlagsArgIndex;
208 const char *VariantName;
212 VariantName =
"open";
216 VariantName =
"openat";
221 unsigned int MinArgCount = FlagsArgIndex + 1;
224 if (
Call.getNumArgs() < MinArgCount)
229 unsigned int CreateModeArgIndex = FlagsArgIndex + 1;
232 unsigned int MaxArgCount = CreateModeArgIndex + 1;
235 if (
Call.getNumArgs() == MaxArgCount) {
236 const Expr *Arg =
Call.getArgExpr(CreateModeArgIndex);
239 SmallString<256> SBuf;
240 llvm::raw_svector_ostream
OS(SBuf);
241 OS <<
"The " << CreateModeArgIndex + 1
242 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
243 <<
" argument to '" << VariantName <<
"' is not an integer";
245 ReportOpenBug(
C, state,
250 }
else if (
Call.getNumArgs() > MaxArgCount) {
251 SmallString<256> SBuf;
252 llvm::raw_svector_ostream
OS(SBuf);
253 OS <<
"Call to '" << VariantName <<
"' with more than " << MaxArgCount
256 ReportOpenBug(
C, state, SBuf.c_str(),
257 Call.getArgExpr(MaxArgCount)->getSourceRange());
261 if (!Val_O_CREAT.has_value()) {
266 const Expr *oflagsEx =
Call.getArgExpr(FlagsArgIndex);
267 const SVal
V =
Call.getArgSVal(FlagsArgIndex);
273 NonLoc oflags =
V.castAs<NonLoc>();
274 NonLoc ocreateFlag =
C.getSValBuilder()
275 .makeIntVal(Val_O_CREAT.value(), oflagsEx->
getType())
277 SVal maskedFlagsUC =
C.getSValBuilder().evalBinOpNN(state, BO_And,
280 if (maskedFlagsUC.isUnknownOrUndef())
282 DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>();
286 std::tie(trueState, falseState) = state->assume(maskedFlags);
290 if (!(trueState && !falseState))
293 if (
Call.getNumArgs() < MaxArgCount) {
294 SmallString<256> SBuf;
295 llvm::raw_svector_ostream
OS(SBuf);
296 OS <<
"Call to '" << VariantName <<
"' requires a "
297 << CreateModeArgIndex + 1
298 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
299 <<
" argument when the 'O_CREAT' flag is set";
300 ReportOpenBug(
C, trueState,
310ProgramStateRef UnixAPIMisuseChecker::EnsureGetdelimBufferAndSizeCorrect(
311 SVal LinePtrPtrSVal, SVal SizePtrSVal,
const Expr *LinePtrPtrExpr,
313 static constexpr llvm::StringLiteral SizeGreaterThanBufferSize =
314 "The buffer from the first argument is smaller than the size "
315 "specified by the second parameter";
316 static constexpr llvm::StringLiteral SizeUndef =
317 "The buffer from the first argument is not NULL, but the size specified "
318 "by the second parameter is undefined.";
320 auto EmitBugReport = [
this, &
C, SizePtrExpr, LinePtrPtrExpr](
322 if (ExplodedNode *N =
C.generateErrorNode(BugState)) {
323 auto R = std::make_unique<PathSensitiveBugReport>(BT_getline, ErrMsg, N);
326 C.emitReport(std::move(R));
332 const auto LinePtrValOpt =
getPointeeVal(LinePtrPtrSVal, State);
336 const auto LinePtrSVal = LinePtrValOpt->getAs<DefinedSVal>();
338 if (!LinePtrSVal || !NSVal || NSVal->
isUnknown())
341 assert(LinePtrPtrExpr && SizePtrExpr);
343 const auto [LinePtrNotNull, LinePtrNull] = State->assume(*LinePtrSVal);
344 if (LinePtrNotNull && !LinePtrNull) {
347 EmitBugReport(LinePtrNotNull, SizeUndef);
353 auto NDefSVal = NSVal->
getAs<DefinedSVal>();
355 return LinePtrNotNull;
357 auto &SVB =
C.getSValBuilder();
359 const MemRegion *LinePtrRegion = LinePtrSVal->getAsRegion();
361 return LinePtrNotNull;
364 auto LineBufSizeGtN = SVB.evalBinOp(LinePtrNotNull, BO_GE, LineBufSize,
365 *NDefSVal, SVB.getConditionType())
366 .getAs<DefinedOrUnknownSVal>();
368 return LinePtrNotNull;
369 if (
auto LineBufSizeOk = LinePtrNotNull->assume(*LineBufSizeGtN,
true))
370 return LineBufSizeOk;
372 EmitBugReport(LinePtrNotNull, SizeGreaterThanBufferSize);
378void UnixAPIMisuseChecker::CheckGetDelimOrGetline(CheckerContext &
C,
379 const CallEvent &
Call)
const {
380 if (
Call.getNumArgs() < 2)
386 SVal SizePtrSval =
Call.getArgSVal(1);
387 State = EnsurePtrNotNull(SizePtrSval,
Call.getArgExpr(1),
C, State,
"Size");
392 SVal LinePtrPtrSVal =
Call.getArgSVal(0);
394 EnsurePtrNotNull(LinePtrPtrSVal,
Call.getArgExpr(0),
C, State,
"Line");
398 State = EnsureGetdelimBufferAndSizeCorrect(LinePtrPtrSVal, SizePtrSval,
400 Call.getArgExpr(1),
C, State);
404 C.addTransition(State);
411void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &
C,
412 const CallEvent &
Call)
const {
417 if (
Call.getNumArgs() < 1)
423 const MemRegion *
R =
Call.getArgSVal(0).getAsRegion();
424 if (!R || !
R->hasMemorySpace<StackSpaceRegion>(state))
427 ExplodedNode *N =
C.generateErrorNode(state);
432 llvm::raw_svector_ostream os(S);
433 os <<
"Call to 'pthread_once' uses";
434 if (
const VarRegion *VR = dyn_cast<VarRegion>(R))
435 os <<
" the local variable '" << VR->getDecl()->getName() <<
'\'';
437 os <<
" stack allocated memory";
438 os <<
" for the \"control\" value. Using such transient memory for "
439 "the control value is potentially dangerous.";
440 if (
isa<VarRegion>(R) &&
R->hasMemorySpace<StackLocalsSpaceRegion>(state))
441 os <<
" Perhaps you intended to declare the variable as 'static'?";
444 std::make_unique<PathSensitiveBugReport>(BT_pthreadOnce, os.str(), N);
445 report->addRange(
Call.getArgExpr(0)->getSourceRange());
446 C.emitReport(std::move(report));
463 std::tie(*trueState, *falseState) =
466 return (*falseState && !*trueState);
472bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(
476 const char *fn_name)
const {
477 ExplodedNode *N =
C.generateErrorNode(falseState);
481 auto report = std::make_unique<PathSensitiveBugReport>(
483 "Call to '" + Twine(fn_name) +
"' has an allocation size of 0 bytes", N);
485 report->addRange(
arg->getSourceRange());
487 C.emitReport(std::move(report));
494void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &
C,
496 const unsigned numArgs,
497 const unsigned sizeArg,
498 const char *fn)
const {
507 SVal argVal =
C.getSVal(arg);
514 (void) ReportZeroByteAllocation(
C, falseState, arg, fn);
519 if (trueState != state)
520 C.addTransition(trueState);
523void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &
C,
524 const CallExpr *CE)
const {
533 for (i = 0; i < nArgs; i++) {
535 SVal argVal =
C.getSVal(arg);
543 if (ReportZeroByteAllocation(
C, falseState, arg,
"calloc"))
553 if (trueState != state)
554 C.addTransition(trueState);
557void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &
C,
558 const CallExpr *CE)
const {
559 BasicAllocationCheck(
C, CE, 1, 0,
"malloc");
562void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &
C,
563 const CallExpr *CE)
const {
564 BasicAllocationCheck(
C, CE, 2, 1,
"realloc");
567void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &
C,
568 const CallExpr *CE)
const {
569 BasicAllocationCheck(
C, CE, 2, 1,
"reallocf");
572void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &
C,
573 const CallExpr *CE)
const {
574 BasicAllocationCheck(
C, CE, 1, 0,
"alloca");
577void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero(
579 const CallExpr *CE)
const {
580 BasicAllocationCheck(
C, CE, 2, 0,
"__builtin_alloca_with_align");
583void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &
C,
584 const CallExpr *CE)
const {
585 BasicAllocationCheck(
C, CE, 1, 0,
"valloc");
588void UnixAPIPortabilityChecker::checkPreStmt(
const CallExpr *CE,
589 CheckerContext &
C)
const {
590 const FunctionDecl *FD =
C.getCalleeDecl(CE);
591 if (!FD || FD->
getKind() != Decl::Function)
597 if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
600 StringRef FName =
C.getCalleeName(FD);
604 if (FName ==
"calloc")
605 CheckCallocZero(
C, CE);
607 else if (FName ==
"malloc")
608 CheckMallocZero(
C, CE);
610 else if (FName ==
"realloc")
611 CheckReallocZero(
C, CE);
613 else if (FName ==
"reallocf")
614 CheckReallocfZero(
C, CE);
616 else if (FName ==
"alloca" || FName ==
"__builtin_alloca")
617 CheckAllocaZero(
C, CE);
619 else if (FName ==
"__builtin_alloca_with_align")
620 CheckAllocaWithAlignZero(
C, CE);
622 else if (FName ==
"valloc")
623 CheckVallocZero(
C, CE);
630void ento::registerUnixAPIMisuseChecker(CheckerManager &Mgr) {
634bool ento::shouldRegisterUnixAPIMisuseChecker(
const CheckerManager &Mgr) {
638void ento::registerUnixAPIPortabilityChecker(CheckerManager &Mgr) {
641bool ento::shouldRegisterUnixAPIPortabilityChecker(
const CheckerManager &Mgr) {
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, ...);.
static std::optional< int > getCreateFlagValue(const ASTContext &Ctx, const Preprocessor &PP)
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
const TargetInfo & getTargetInfo() const
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
DeclContext * getEnclosingNamespaceContext()
Retrieve the nearest enclosing namespace context.
This represents one expression.
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
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.
bool isPointerType() const
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
ASTContext & getASTContext() const
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
const Preprocessor & getPreprocessor() const
Simple checker classes that implement one frontend (i.e.
bool isUndef() const =delete
bool isUnknown() const =delete
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
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
DefinedOrUnknownSVal getDynamicExtent(ProgramStateRef State, const MemRegion *MR, SValBuilder &SVB)
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
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.
bool isa(CodeGen::Address addr)
__DEVICE__ _Tp arg(const std::complex< _Tp > &__c)