24#include "llvm/ADT/STLExtras.h"
25#include "llvm/ADT/StringExtras.h"
26#include "llvm/Support/raw_ostream.h"
45 if (MacroVal.has_value())
56class UnixAPIMisuseChecker :
public Checker<check::PreCall> {
58 const BugType BT_getline{
this,
"Improper use of getdelim",
60 const BugType BT_pthreadOnce{
this,
"Improper use of 'pthread_once'",
63 const std::optional<int> Val_O_CREAT;
66 EnsurePtrNotNull(SVal PtrVal,
const Expr *PtrExpr, CheckerContext &
C,
68 std::optional<std::reference_wrapper<const BugType>> BT =
72 SVal LinePtrPtrSVal, SVal SizePtrSVal,
const Expr *LinePtrPtrExpr,
76 UnixAPIMisuseChecker(
const ASTContext &Ctx,
const Preprocessor &PP)
79 void checkASTDecl(
const TranslationUnitDecl *TU, AnalysisManager &Mgr,
80 BugReporter &BR)
const;
82 void checkPreCall(
const CallEvent &
Call, CheckerContext &
C)
const;
84 void CheckOpen(CheckerContext &
C,
const CallEvent &
Call)
const;
85 void CheckOpenAt(CheckerContext &
C,
const CallEvent &
Call)
const;
86 void CheckGetDelimOrGetline(CheckerContext &
C,
const CallEvent &
Call)
const;
87 void CheckPthreadOnce(CheckerContext &
C,
const CallEvent &
Call)
const;
89 void CheckOpenVariant(CheckerContext &
C,
const CallEvent &
Call,
92 void ReportOpenBug(CheckerContext &
C,
ProgramStateRef State,
const char *Msg,
93 SourceRange SR)
const;
96class UnixAPIPortabilityChecker :
public Checker< check::PreStmt<CallExpr> > {
98 void checkPreStmt(
const CallExpr *CE, CheckerContext &
C)
const;
101 const BugType BT_mallocZero{
102 this,
"Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)",
105 void CheckCallocZero(CheckerContext &
C,
const CallExpr *CE)
const;
106 void CheckMallocZero(CheckerContext &
C,
const CallExpr *CE)
const;
107 void CheckReallocZero(CheckerContext &
C,
const CallExpr *CE)
const;
108 void CheckReallocfZero(CheckerContext &
C,
const CallExpr *CE)
const;
109 void CheckAllocaZero(CheckerContext &
C,
const CallExpr *CE)
const;
110 void CheckAllocaWithAlignZero(CheckerContext &
C,
const CallExpr *CE)
const;
111 void CheckVallocZero(CheckerContext &
C,
const CallExpr *CE)
const;
113 bool ReportZeroByteAllocation(CheckerContext &
C,
116 const char *fn_name)
const;
117 void BasicAllocationCheck(CheckerContext &
C,
119 const unsigned numArgs,
120 const unsigned sizeArg,
121 const char *fn)
const;
128 const StringRef PtrDescr,
129 std::optional<std::reference_wrapper<const BugType>> BT)
const {
130 const auto Ptr = PtrVal.
getAs<DefinedSVal>();
134 const auto [PtrNotNull, PtrNull] = State->assume(*Ptr);
135 if (!PtrNotNull && PtrNull) {
136 if (ExplodedNode *N =
C.generateErrorNode(PtrNull)) {
137 auto R = std::make_unique<PathSensitiveBugReport>(
138 BT.value_or(std::cref(BT_ArgumentNull)),
139 (PtrDescr +
" pointer might be NULL.").str(), N);
141 C.emitReport(std::move(R));
153void UnixAPIMisuseChecker::checkPreCall(
const CallEvent &
Call,
154 CheckerContext &
C)
const {
155 const FunctionDecl *FD = dyn_cast_if_present<FunctionDecl>(
Call.getDecl());
156 if (!FD || FD->
getKind() != Decl::Function)
162 if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
165 StringRef FName =
C.getCalleeName(FD);
172 else if (FName ==
"openat")
173 CheckOpenAt(
C,
Call);
175 else if (FName ==
"pthread_once")
176 CheckPthreadOnce(
C,
Call);
178 else if (is_contained({
"getdelim",
"getline"}, FName))
179 CheckGetDelimOrGetline(
C,
Call);
181void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &
C,
184 SourceRange SR)
const {
185 ExplodedNode *N =
C.generateErrorNode(State);
189 auto Report = std::make_unique<PathSensitiveBugReport>(BT_open, Msg, N);
191 C.emitReport(std::move(
Report));
194void UnixAPIMisuseChecker::CheckOpen(CheckerContext &
C,
195 const CallEvent &
Call)
const {
199void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &
C,
200 const CallEvent &
Call)
const {
204void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &
C,
205 const CallEvent &
Call,
209 unsigned int FlagsArgIndex;
210 const char *VariantName;
214 VariantName =
"open";
218 VariantName =
"openat";
223 unsigned int MinArgCount = FlagsArgIndex + 1;
226 if (
Call.getNumArgs() < MinArgCount)
231 unsigned int CreateModeArgIndex = FlagsArgIndex + 1;
234 unsigned int MaxArgCount = CreateModeArgIndex + 1;
237 if (
Call.getNumArgs() == MaxArgCount) {
238 const Expr *Arg =
Call.getArgExpr(CreateModeArgIndex);
241 SmallString<256> SBuf;
242 llvm::raw_svector_ostream
OS(SBuf);
243 OS <<
"The " << CreateModeArgIndex + 1
244 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
245 <<
" argument to '" << VariantName <<
"' is not an integer";
247 ReportOpenBug(
C, state,
252 }
else if (
Call.getNumArgs() > MaxArgCount) {
253 SmallString<256> SBuf;
254 llvm::raw_svector_ostream
OS(SBuf);
255 OS <<
"Call to '" << VariantName <<
"' with more than " << MaxArgCount
258 ReportOpenBug(
C, state, SBuf.c_str(),
259 Call.getArgExpr(MaxArgCount)->getSourceRange());
263 if (!Val_O_CREAT.has_value()) {
268 const Expr *oflagsEx =
Call.getArgExpr(FlagsArgIndex);
269 const SVal
V =
Call.getArgSVal(FlagsArgIndex);
275 NonLoc oflags =
V.castAs<NonLoc>();
276 NonLoc ocreateFlag =
C.getSValBuilder()
277 .makeIntVal(Val_O_CREAT.value(), oflagsEx->
getType())
279 SVal maskedFlagsUC =
C.getSValBuilder().evalBinOpNN(state, BO_And,
282 if (maskedFlagsUC.isUnknownOrUndef())
284 DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>();
288 std::tie(trueState, falseState) = state->assume(maskedFlags);
292 if (!(trueState && !falseState))
295 if (
Call.getNumArgs() < MaxArgCount) {
296 SmallString<256> SBuf;
297 llvm::raw_svector_ostream
OS(SBuf);
298 OS <<
"Call to '" << VariantName <<
"' requires a "
299 << CreateModeArgIndex + 1
300 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
301 <<
" argument when the 'O_CREAT' flag is set";
302 ReportOpenBug(
C, trueState,
312ProgramStateRef UnixAPIMisuseChecker::EnsureGetdelimBufferAndSizeCorrect(
313 SVal LinePtrPtrSVal, SVal SizePtrSVal,
const Expr *LinePtrPtrExpr,
315 static constexpr llvm::StringLiteral SizeGreaterThanBufferSize =
316 "The buffer from the first argument is smaller than the size "
317 "specified by the second parameter";
318 static constexpr llvm::StringLiteral SizeUndef =
319 "The buffer from the first argument is not NULL, but the size specified "
320 "by the second parameter is undefined.";
322 auto EmitBugReport = [
this, &
C, SizePtrExpr, LinePtrPtrExpr](
324 if (ExplodedNode *N =
C.generateErrorNode(BugState)) {
325 auto R = std::make_unique<PathSensitiveBugReport>(BT_getline, ErrMsg, N);
328 C.emitReport(std::move(R));
334 const auto LinePtrValOpt =
getPointeeVal(LinePtrPtrSVal, State);
338 const auto LinePtrSVal = LinePtrValOpt->getAs<DefinedSVal>();
340 if (!LinePtrSVal || !NSVal || NSVal->
isUnknown())
343 assert(LinePtrPtrExpr && SizePtrExpr);
345 const auto [LinePtrNotNull, LinePtrNull] = State->assume(*LinePtrSVal);
346 if (LinePtrNotNull && !LinePtrNull) {
349 EmitBugReport(LinePtrNotNull, SizeUndef);
355 auto NDefSVal = NSVal->
getAs<DefinedSVal>();
357 return LinePtrNotNull;
359 auto &SVB =
C.getSValBuilder();
361 const MemRegion *LinePtrRegion = LinePtrSVal->getAsRegion();
363 return LinePtrNotNull;
366 auto LineBufSizeGtN = SVB.evalBinOp(LinePtrNotNull, BO_GE, LineBufSize,
367 *NDefSVal, SVB.getConditionType())
368 .getAs<DefinedOrUnknownSVal>();
370 return LinePtrNotNull;
371 if (
auto LineBufSizeOk = LinePtrNotNull->assume(*LineBufSizeGtN,
true))
372 return LineBufSizeOk;
374 EmitBugReport(LinePtrNotNull, SizeGreaterThanBufferSize);
380void UnixAPIMisuseChecker::CheckGetDelimOrGetline(CheckerContext &
C,
381 const CallEvent &
Call)
const {
382 if (
Call.getNumArgs() < 2)
388 SVal SizePtrSval =
Call.getArgSVal(1);
389 State = EnsurePtrNotNull(SizePtrSval,
Call.getArgExpr(1),
C, State,
"Size");
394 SVal LinePtrPtrSVal =
Call.getArgSVal(0);
396 EnsurePtrNotNull(LinePtrPtrSVal,
Call.getArgExpr(0),
C, State,
"Line");
400 State = EnsureGetdelimBufferAndSizeCorrect(LinePtrPtrSVal, SizePtrSval,
402 Call.getArgExpr(1),
C, State);
406 C.addTransition(State);
413void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &
C,
414 const CallEvent &
Call)
const {
419 if (
Call.getNumArgs() < 1)
425 const MemRegion *R =
Call.getArgSVal(0).getAsRegion();
429 ExplodedNode *N =
C.generateErrorNode(state);
434 llvm::raw_svector_ostream os(S);
435 os <<
"Call to 'pthread_once' uses";
436 if (
const VarRegion *VR = dyn_cast<VarRegion>(R))
437 os <<
" the local variable '" << VR->getDecl()->getName() <<
'\'';
439 os <<
" stack allocated memory";
440 os <<
" for the \"control\" value. Using such transient memory for "
441 "the control value is potentially dangerous.";
443 os <<
" Perhaps you intended to declare the variable as 'static'?";
446 std::make_unique<PathSensitiveBugReport>(BT_pthreadOnce, os.str(), N);
447 report->addRange(
Call.getArgExpr(0)->getSourceRange());
448 C.emitReport(std::move(report));
465 std::tie(*trueState, *falseState) =
468 return (*falseState && !*trueState);
474bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(
478 const char *fn_name)
const {
479 ExplodedNode *N =
C.generateErrorNode(falseState);
484 llvm::raw_svector_ostream os(S);
485 os <<
"Call to '" << fn_name <<
"' has an allocation size of 0 bytes";
487 std::make_unique<PathSensitiveBugReport>(BT_mallocZero, os.str(), N);
489 report->addRange(
arg->getSourceRange());
491 C.emitReport(std::move(report));
498void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &
C,
500 const unsigned numArgs,
501 const unsigned sizeArg,
502 const char *fn)
const {
511 SVal argVal =
C.getSVal(arg);
518 (void) ReportZeroByteAllocation(
C, falseState, arg, fn);
523 if (trueState != state)
524 C.addTransition(trueState);
527void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &
C,
528 const CallExpr *CE)
const {
537 for (i = 0; i < nArgs; i++) {
539 SVal argVal =
C.getSVal(arg);
547 if (ReportZeroByteAllocation(
C, falseState, arg,
"calloc"))
557 if (trueState != state)
558 C.addTransition(trueState);
561void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &
C,
562 const CallExpr *CE)
const {
563 BasicAllocationCheck(
C, CE, 1, 0,
"malloc");
566void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &
C,
567 const CallExpr *CE)
const {
568 BasicAllocationCheck(
C, CE, 2, 1,
"realloc");
571void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &
C,
572 const CallExpr *CE)
const {
573 BasicAllocationCheck(
C, CE, 2, 1,
"reallocf");
576void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &
C,
577 const CallExpr *CE)
const {
578 BasicAllocationCheck(
C, CE, 1, 0,
"alloca");
581void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero(
583 const CallExpr *CE)
const {
584 BasicAllocationCheck(
C, CE, 2, 0,
"__builtin_alloca_with_align");
587void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &
C,
588 const CallExpr *CE)
const {
589 BasicAllocationCheck(
C, CE, 1, 0,
"valloc");
592void UnixAPIPortabilityChecker::checkPreStmt(
const CallExpr *CE,
593 CheckerContext &
C)
const {
594 const FunctionDecl *FD =
C.getCalleeDecl(CE);
595 if (!FD || FD->
getKind() != Decl::Function)
601 if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
604 StringRef FName =
C.getCalleeName(FD);
608 if (FName ==
"calloc")
609 CheckCallocZero(
C, CE);
611 else if (FName ==
"malloc")
612 CheckMallocZero(
C, CE);
614 else if (FName ==
"realloc")
615 CheckReallocZero(
C, CE);
617 else if (FName ==
"reallocf")
618 CheckReallocfZero(
C, CE);
620 else if (FName ==
"alloca" || FName ==
"__builtin_alloca")
621 CheckAllocaZero(
C, CE);
623 else if (FName ==
"__builtin_alloca_with_align")
624 CheckAllocaWithAlignZero(
C, CE);
626 else if (FName ==
"valloc")
627 CheckVallocZero(
C, CE);
634void ento::registerUnixAPIMisuseChecker(CheckerManager &Mgr) {
638bool ento::shouldRegisterUnixAPIMisuseChecker(
const CheckerManager &Mgr) {
642void ento::registerUnixAPIPortabilityChecker(CheckerManager &Mgr) {
645bool 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
bool hasMemorySpace(ProgramStateRef State) 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
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)