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 checkPreCall(
const CallEvent &
Call, CheckerContext &
C)
const;
79 void CheckOpen(CheckerContext &
C,
const CallEvent &
Call)
const;
80 void CheckOpenAt(CheckerContext &
C,
const CallEvent &
Call)
const;
81 void CheckGetDelimOrGetline(CheckerContext &
C,
const CallEvent &
Call)
const;
82 void CheckPthreadOnce(CheckerContext &
C,
const CallEvent &
Call)
const;
84 void CheckOpenVariant(CheckerContext &
C,
const CallEvent &
Call,
87 void ReportOpenBug(CheckerContext &
C,
ProgramStateRef State,
const char *Msg,
88 SourceRange SR)
const;
91class UnixAPIPortabilityChecker :
public Checker< check::PreStmt<CallExpr> > {
93 void checkPreStmt(
const CallExpr *CE, CheckerContext &
C)
const;
96 const BugType BT_mallocZero{
97 this,
"Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)",
100 void CheckCallocZero(CheckerContext &
C,
const CallExpr *CE)
const;
101 void CheckMallocZero(CheckerContext &
C,
const CallExpr *CE)
const;
102 void CheckReallocZero(CheckerContext &
C,
const CallExpr *CE)
const;
103 void CheckReallocfZero(CheckerContext &
C,
const CallExpr *CE)
const;
104 void CheckAllocaZero(CheckerContext &
C,
const CallExpr *CE)
const;
105 void CheckAllocaWithAlignZero(CheckerContext &
C,
const CallExpr *CE)
const;
106 void CheckVallocZero(CheckerContext &
C,
const CallExpr *CE)
const;
108 bool ReportZeroByteAllocation(CheckerContext &
C,
111 const char *fn_name)
const;
112 void BasicAllocationCheck(CheckerContext &
C,
114 const unsigned numArgs,
115 const unsigned sizeArg,
116 const char *fn)
const;
123 const StringRef PtrDescr,
124 std::optional<std::reference_wrapper<const BugType>> BT)
const {
125 const auto Ptr = PtrVal.
getAs<DefinedSVal>();
129 const auto [PtrNotNull, PtrNull] = State->assume(*Ptr);
130 if (!PtrNotNull && PtrNull) {
131 if (ExplodedNode *N =
C.addSink(PtrNull)) {
132 auto R = std::make_unique<PathSensitiveBugReport>(
133 BT.value_or(std::cref(BT_ArgumentNull)),
134 (PtrDescr +
" pointer might be NULL.").str(), N);
136 C.emitReport(std::move(R));
148void UnixAPIMisuseChecker::checkPreCall(
const CallEvent &
Call,
149 CheckerContext &
C)
const {
150 const FunctionDecl *FD = dyn_cast_if_present<FunctionDecl>(
Call.getDecl());
151 if (!FD || FD->
getKind() != Decl::Function)
157 if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
160 StringRef FName =
C.getCalleeName(FD);
167 else if (FName ==
"openat")
168 CheckOpenAt(
C,
Call);
170 else if (FName ==
"pthread_once")
171 CheckPthreadOnce(
C,
Call);
173 else if (is_contained({
"getdelim",
"getline"}, FName))
174 CheckGetDelimOrGetline(
C,
Call);
176void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &
C,
179 SourceRange SR)
const {
180 ExplodedNode *N =
C.addSink(State);
184 auto Report = std::make_unique<PathSensitiveBugReport>(BT_open, Msg, N);
186 C.emitReport(std::move(
Report));
189void UnixAPIMisuseChecker::CheckOpen(CheckerContext &
C,
190 const CallEvent &
Call)
const {
194void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &
C,
195 const CallEvent &
Call)
const {
199void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &
C,
200 const CallEvent &
Call,
204 unsigned int FlagsArgIndex;
205 const char *VariantName;
209 VariantName =
"open";
213 VariantName =
"openat";
218 unsigned int MinArgCount = FlagsArgIndex + 1;
221 if (
Call.getNumArgs() < MinArgCount)
226 unsigned int CreateModeArgIndex = FlagsArgIndex + 1;
229 unsigned int MaxArgCount = CreateModeArgIndex + 1;
232 if (
Call.getNumArgs() == MaxArgCount) {
233 const Expr *Arg =
Call.getArgExpr(CreateModeArgIndex);
236 SmallString<256> SBuf;
237 llvm::raw_svector_ostream
OS(SBuf);
238 OS <<
"The " << CreateModeArgIndex + 1
239 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
240 <<
" argument to '" << VariantName <<
"' is not an integer";
242 ReportOpenBug(
C, state,
247 }
else if (
Call.getNumArgs() > MaxArgCount) {
248 SmallString<256> SBuf;
249 llvm::raw_svector_ostream
OS(SBuf);
250 OS <<
"Call to '" << VariantName <<
"' with more than " << MaxArgCount
253 ReportOpenBug(
C, state, SBuf.c_str(),
254 Call.getArgExpr(MaxArgCount)->getSourceRange());
258 if (!Val_O_CREAT.has_value()) {
263 const Expr *oflagsEx =
Call.getArgExpr(FlagsArgIndex);
264 const SVal
V =
Call.getArgSVal(FlagsArgIndex);
270 NonLoc oflags =
V.castAs<NonLoc>();
271 NonLoc ocreateFlag =
C.getSValBuilder()
272 .makeIntVal(Val_O_CREAT.value(), oflagsEx->
getType())
274 SVal maskedFlagsUC =
C.getSValBuilder().evalBinOpNN(state, BO_And,
277 if (maskedFlagsUC.isUnknownOrUndef())
279 DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>();
283 std::tie(trueState, falseState) = state->assume(maskedFlags);
287 if (!(trueState && !falseState))
290 if (
Call.getNumArgs() < MaxArgCount) {
291 SmallString<256> SBuf;
292 llvm::raw_svector_ostream
OS(SBuf);
293 OS <<
"Call to '" << VariantName <<
"' requires a "
294 << CreateModeArgIndex + 1
295 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
296 <<
" argument when the 'O_CREAT' flag is set";
297 ReportOpenBug(
C, trueState,
307ProgramStateRef UnixAPIMisuseChecker::EnsureGetdelimBufferAndSizeCorrect(
308 SVal LinePtrPtrSVal, SVal SizePtrSVal,
const Expr *LinePtrPtrExpr,
310 static constexpr llvm::StringLiteral SizeGreaterThanBufferSize =
311 "The buffer from the first argument is smaller than the size "
312 "specified by the second parameter";
313 static constexpr llvm::StringLiteral SizeUndef =
314 "The buffer from the first argument is not NULL, but the size specified "
315 "by the second parameter is undefined.";
317 auto EmitBugReport = [
this, &
C, SizePtrExpr, LinePtrPtrExpr](
319 if (ExplodedNode *N =
C.addSink(BugState)) {
320 auto R = std::make_unique<PathSensitiveBugReport>(BT_getline, ErrMsg, N);
323 C.emitReport(std::move(R));
329 const auto LinePtrValOpt =
getPointeeVal(LinePtrPtrSVal, State);
333 const auto LinePtrSVal = LinePtrValOpt->getAs<DefinedSVal>();
335 if (!LinePtrSVal || !NSVal || NSVal->
isUnknown())
338 assert(LinePtrPtrExpr && SizePtrExpr);
340 const auto [LinePtrNotNull, LinePtrNull] = State->assume(*LinePtrSVal);
341 if (LinePtrNotNull && !LinePtrNull) {
344 EmitBugReport(LinePtrNotNull, SizeUndef);
350 auto NDefSVal = NSVal->
getAs<DefinedSVal>();
352 return LinePtrNotNull;
354 auto &SVB =
C.getSValBuilder();
356 const MemRegion *LinePtrRegion = LinePtrSVal->getAsRegion();
358 return LinePtrNotNull;
361 auto LineBufSizeGtN = SVB.evalBinOp(LinePtrNotNull, BO_GE, LineBufSize,
362 *NDefSVal, SVB.getConditionType())
363 .getAs<DefinedOrUnknownSVal>();
365 return LinePtrNotNull;
366 if (
auto LineBufSizeOk = LinePtrNotNull->assume(*LineBufSizeGtN,
true))
367 return LineBufSizeOk;
369 EmitBugReport(LinePtrNotNull, SizeGreaterThanBufferSize);
375void UnixAPIMisuseChecker::CheckGetDelimOrGetline(CheckerContext &
C,
376 const CallEvent &
Call)
const {
377 if (
Call.getNumArgs() < 2)
383 SVal SizePtrSval =
Call.getArgSVal(1);
384 State = EnsurePtrNotNull(SizePtrSval,
Call.getArgExpr(1),
C, State,
"Size");
389 SVal LinePtrPtrSVal =
Call.getArgSVal(0);
391 EnsurePtrNotNull(LinePtrPtrSVal,
Call.getArgExpr(0),
C, State,
"Line");
395 State = EnsureGetdelimBufferAndSizeCorrect(LinePtrPtrSVal, SizePtrSval,
397 Call.getArgExpr(1),
C, State);
401 C.addTransition(State);
408void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &
C,
409 const CallEvent &
Call)
const {
414 if (
Call.getNumArgs() < 1)
420 const MemRegion *
R =
Call.getArgSVal(0).getAsRegion();
421 if (!R || !
R->hasMemorySpace<StackSpaceRegion>(state))
424 ExplodedNode *N =
C.addSink(state);
429 llvm::raw_svector_ostream os(S);
430 os <<
"Call to 'pthread_once' uses";
431 if (
const VarRegion *VR = dyn_cast<VarRegion>(R))
432 os <<
" the local variable '" << VR->getDecl()->getName() <<
'\'';
434 os <<
" stack allocated memory";
435 os <<
" for the \"control\" value; using such transient memory for "
436 "the control value is potentially dangerous";
437 if (
isa<VarRegion>(R) &&
R->hasMemorySpace<StackLocalsSpaceRegion>(state))
438 os <<
"; perhaps you intended to declare the variable as 'static'?";
441 std::make_unique<PathSensitiveBugReport>(BT_pthreadOnce, os.str(), N);
442 report->addRange(
Call.getArgExpr(0)->getSourceRange());
443 C.emitReport(std::move(report));
460 std::tie(*trueState, *falseState) =
463 return (*falseState && !*trueState);
469bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(
473 const char *fn_name)
const {
474 ExplodedNode *N =
C.addSink(falseState);
478 auto report = std::make_unique<PathSensitiveBugReport>(
480 "Call to '" + Twine(fn_name) +
"' has an allocation size of 0 bytes", N);
482 report->addRange(
arg->getSourceRange());
484 C.emitReport(std::move(report));
491void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &
C,
493 const unsigned numArgs,
494 const unsigned sizeArg,
495 const char *fn)
const {
504 SVal argVal =
C.getSVal(arg);
511 (void) ReportZeroByteAllocation(
C, falseState, arg, fn);
516 if (trueState != state)
517 C.addTransition(trueState);
520void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &
C,
521 const CallExpr *CE)
const {
530 for (i = 0; i < nArgs; i++) {
532 SVal argVal =
C.getSVal(arg);
540 if (ReportZeroByteAllocation(
C, falseState, arg,
"calloc"))
550 if (trueState != state)
551 C.addTransition(trueState);
554void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &
C,
555 const CallExpr *CE)
const {
556 BasicAllocationCheck(
C, CE, 1, 0,
"malloc");
559void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &
C,
560 const CallExpr *CE)
const {
561 BasicAllocationCheck(
C, CE, 2, 1,
"realloc");
564void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &
C,
565 const CallExpr *CE)
const {
566 BasicAllocationCheck(
C, CE, 2, 1,
"reallocf");
569void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &
C,
570 const CallExpr *CE)
const {
571 BasicAllocationCheck(
C, CE, 1, 0,
"alloca");
574void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero(
576 const CallExpr *CE)
const {
577 BasicAllocationCheck(
C, CE, 2, 0,
"__builtin_alloca_with_align");
580void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &
C,
581 const CallExpr *CE)
const {
582 BasicAllocationCheck(
C, CE, 1, 0,
"valloc");
585void UnixAPIPortabilityChecker::checkPreStmt(
const CallExpr *CE,
586 CheckerContext &
C)
const {
587 const FunctionDecl *FD =
C.getCalleeDecl(CE);
588 if (!FD || FD->
getKind() != Decl::Function)
594 if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
597 StringRef FName =
C.getCalleeName(FD);
601 if (FName ==
"calloc")
602 CheckCallocZero(
C, CE);
604 else if (FName ==
"malloc")
605 CheckMallocZero(
C, CE);
607 else if (FName ==
"realloc")
608 CheckReallocZero(
C, CE);
610 else if (FName ==
"reallocf")
611 CheckReallocfZero(
C, CE);
613 else if (FName ==
"alloca" || FName ==
"__builtin_alloca")
614 CheckAllocaZero(
C, CE);
616 else if (FName ==
"__builtin_alloca_with_align")
617 CheckAllocaWithAlignZero(
C, CE);
619 else if (FName ==
"valloc")
620 CheckVallocZero(
C, CE);
627void ento::registerUnixAPIMisuseChecker(CheckerManager &Mgr) {
631bool ento::shouldRegisterUnixAPIMisuseChecker(
const CheckerManager &Mgr) {
635void ento::registerUnixAPIPortabilityChecker(CheckerManager &Mgr) {
638bool 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.
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
__DEVICE__ _Tp arg(const std::complex< _Tp > &__c)