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"
45class UnixAPIMisuseChecker
46 :
public Checker<check::PreCall, check::ASTDecl<TranslationUnitDecl>> {
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;
58 std::optional<std::reference_wrapper<const BugType>> BT =
62 SVal LinePtrPtrSVal,
SVal SizePtrSVal,
const Expr *LinePtrPtrExpr,
83class UnixAPIPortabilityChecker :
public Checker< check::PreStmt<CallExpr> > {
89 this,
"Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)",
103 const char *fn_name)
const;
106 const unsigned numArgs,
107 const unsigned sizeArg,
108 const char *fn)
const;
115 const StringRef PtrDescr,
116 std::optional<std::reference_wrapper<const BugType>> BT)
const {
121 const auto [PtrNotNull, PtrNull] = State->assume(*Ptr);
122 if (!PtrNotNull && PtrNull) {
124 auto R = std::make_unique<PathSensitiveBugReport>(
125 BT.value_or(std::cref(BT_ArgumentNull)),
126 (PtrDescr +
" pointer might be NULL.").str(), N);
129 C.emitReport(std::move(R));
147 Val_O_CREAT = 0x0200;
155void UnixAPIMisuseChecker::checkPreCall(
const CallEvent &
Call,
158 if (!FD || FD->
getKind() != Decl::Function)
164 if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
167 StringRef FName =
C.getCalleeName(FD);
174 else if (FName ==
"openat")
175 CheckOpenAt(
C,
Call);
177 else if (FName ==
"pthread_once")
178 CheckPthreadOnce(
C,
Call);
180 else if (is_contained({
"getdelim",
"getline"}, FName))
181 CheckGetDelim(
C,
Call);
191 auto Report = std::make_unique<PathSensitiveBugReport>(BT_open, Msg, N);
192 Report->addRange(SR);
193 C.emitReport(std::move(Report));
211 unsigned int FlagsArgIndex;
212 const char *VariantName;
216 VariantName =
"open";
220 VariantName =
"openat";
225 unsigned int MinArgCount = FlagsArgIndex + 1;
229 unsigned int CreateModeArgIndex = FlagsArgIndex + 1;
232 unsigned int MaxArgCount = CreateModeArgIndex + 1;
236 if (
Call.getNumArgs() < MinArgCount) {
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";
249 ReportOpenBug(
C, state,
254 }
else if (
Call.getNumArgs() > MaxArgCount) {
256 llvm::raw_svector_ostream OS(SBuf);
257 OS <<
"Call to '" << VariantName <<
"' with more than " << MaxArgCount
260 ReportOpenBug(
C, state, SBuf.c_str(),
261 Call.getArgExpr(MaxArgCount)->getSourceRange());
270 const Expr *oflagsEx =
Call.getArgExpr(FlagsArgIndex);
271 const SVal V =
Call.getArgSVal(FlagsArgIndex);
272 if (!isa<NonLoc>(
V)) {
278 NonLoc ocreateFlag =
C.getSValBuilder()
279 .makeIntVal(*Val_O_CREAT, oflagsEx->
getType())
281 SVal maskedFlagsUC =
C.getSValBuilder().evalBinOpNN(state, BO_And,
290 std::tie(trueState, falseState) = state->assume(maskedFlags);
294 if (!(trueState && !falseState))
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,
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.";
324 auto EmitBugReport = [
this, &
C, SizePtrExpr, LinePtrPtrExpr](
327 auto R = std::make_unique<PathSensitiveBugReport>(BT_getline, ErrMsg, N);
330 C.emitReport(std::move(R));
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);
354 auto &SVB =
C.getSValBuilder();
357 auto LineBufSizeGtN = SVB.evalBinOp(LinePtrNotNull, BO_GE, LineBufSize,
358 *NDefSVal, SVB.getConditionType())
361 return LinePtrNotNull;
362 if (
auto LineBufSizeOk = LinePtrNotNull->assume(*LineBufSizeGtN,
true))
363 return LineBufSizeOk;
365 EmitBugReport(LinePtrNotNull, SizeGreaterThanBufferSize);
376 SVal SizePtrSval =
Call.getArgSVal(1);
377 State = EnsurePtrNotNull(SizePtrSval,
Call.getArgExpr(1),
C, State,
"Size");
382 SVal LinePtrPtrSVal =
Call.getArgSVal(0);
384 EnsurePtrNotNull(LinePtrPtrSVal,
Call.getArgExpr(0),
C, State,
"Line");
388 State = EnsureGetdelimBufferAndSizeCorrect(LinePtrPtrSVal, SizePtrSval,
390 Call.getArgExpr(1),
C, State);
394 C.addTransition(State);
407 if (
Call.getNumArgs() < 1)
422 llvm::raw_svector_ostream os(S);
423 os <<
"Call to 'pthread_once' uses";
424 if (
const VarRegion *VR = dyn_cast<VarRegion>(R))
425 os <<
" the local variable '" << VR->getDecl()->getName() <<
'\'';
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<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->
getMemorySpace()))
431 os <<
" Perhaps you intended to declare the variable as 'static'?";
434 std::make_unique<PathSensitiveBugReport>(BT_pthreadOnce, os.str(), N);
435 report->addRange(
Call.getArgExpr(0)->getSourceRange());
436 C.emitReport(std::move(report));
453 std::tie(*trueState, *falseState) =
456 return (*falseState && !*trueState);
462bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(
466 const char *fn_name)
const {
472 llvm::raw_svector_ostream os(S);
473 os <<
"Call to '" << fn_name <<
"' has an allocation size of 0 bytes";
475 std::make_unique<PathSensitiveBugReport>(BT_mallocZero, os.str(), N);
477 report->addRange(
arg->getSourceRange());
479 C.emitReport(std::move(report));
488 const unsigned numArgs,
489 const unsigned sizeArg,
490 const char *fn)
const {
499 SVal argVal =
C.getSVal(arg);
506 (void) ReportZeroByteAllocation(
C, falseState, arg, fn);
511 if (trueState != state)
512 C.addTransition(trueState);
525 for (i = 0; i < nArgs; i++) {
527 SVal argVal =
C.getSVal(arg);
536 if (ReportZeroByteAllocation(
C, falseState, arg,
"calloc"))
547 if (trueState != state)
548 C.addTransition(trueState);
553 BasicAllocationCheck(
C, CE, 1, 0,
"malloc");
558 BasicAllocationCheck(
C, CE, 2, 1,
"realloc");
563 BasicAllocationCheck(
C, CE, 2, 1,
"reallocf");
568 BasicAllocationCheck(
C, CE, 1, 0,
"alloca");
571void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero(
574 BasicAllocationCheck(
C, CE, 2, 0,
"__builtin_alloca_with_align");
579 BasicAllocationCheck(
C, CE, 1, 0,
"valloc");
582void UnixAPIPortabilityChecker::checkPreStmt(
const CallExpr *CE,
585 if (!FD || FD->
getKind() != Decl::Function)
591 if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
594 StringRef FName =
C.getCalleeName(FD);
598 if (FName ==
"calloc")
599 CheckCallocZero(
C, CE);
601 else if (FName ==
"malloc")
602 CheckMallocZero(
C, CE);
604 else if (FName ==
"realloc")
605 CheckReallocZero(
C, CE);
607 else if (FName ==
"reallocf")
608 CheckReallocfZero(
C, CE);
610 else if (FName ==
"alloca" || FName ==
"__builtin_alloca")
611 CheckAllocaZero(
C, CE);
613 else if (FName ==
"__builtin_alloca_with_align")
614 CheckAllocaWithAlignZero(
C, CE);
616 else if (FName ==
"valloc")
617 CheckVallocZero(
C, CE);
624#define REGISTER_CHECKER(CHECKERNAME) \
625 void ento::register##CHECKERNAME(CheckerManager &mgr) { \
626 mgr.registerChecker<CHECKERNAME>(); \
629 bool ento::shouldRegister##CHECKERNAME(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, ...);.
#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)