diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -429,6 +429,10 @@ HelpText<"Check improper use of chroot">, Documentation; +def ErrorReturnChecker : Checker<"ErrorReturn">, + HelpText<"Check for unchecked error return values">, + Documentation; + def PthreadLockChecker : Checker<"PthreadLock">, HelpText<"Simple lock -> unlock checker">, Documentation; diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -37,6 +37,7 @@ DynamicTypePropagation.cpp DynamicTypeChecker.cpp EnumCastOutOfRangeChecker.cpp + ErrorReturnChecker.cpp ExprInspectionChecker.cpp FixedAddressChecker.cpp FuchsiaHandleChecker.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/ErrorReturnChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ErrorReturnChecker.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/ErrorReturnChecker.cpp @@ -0,0 +1,324 @@ +//===-- ErrorReturnChecker.cpp ------------------------------------*- C++ -*--// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines ErrorReturnChecker, a builtin checker that checks for +// error checking of certain C API function return values. +// This check corresponds to SEI CERT ERR33-C. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Expr.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/Stmt.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include + +using namespace clang; +using namespace ento; +using namespace std::placeholders; + +/// Store a saved argument value for an API function call to check. +REGISTER_MAP_WITH_PROGRAMSTATE(ParmValMap, SymbolRef, llvm::APSInt) + +namespace { + +/// Defines the way of checking a specific kind of function call. +class CheckForErrorResultChecker { +public: + // Test if in the current state the error-checked-condition is true. + // The function should check that it is not possible to assume the error + // condition but it is possible to assume the success condition on the return + // value of the checked API function call. If this condition is true a branch + // is found where the error check is done. The check should work if the code + // checks for a smaller subset of success return values. + // + // 'Sym' is the symbol for the function call to check (key in GDM). + // 'Value' is the current value of the symbol. + // 'RetTy' is the return type of the function (obtained from the AST). + // Return true if correct error checking was found. + virtual bool testErrorCheckedCondition(CheckerContext &C, + ProgramStateRef State, SymbolRef Sym, + DefinedOrUnknownSVal Value, + const QualType &RetTy) const = 0; +}; + +/// Check functions whose error return is a NULL value. +/// Should work with any type of null value. +class NullErrorResultChecker : public CheckForErrorResultChecker { +public: + bool testErrorCheckedCondition(CheckerContext &C, ProgramStateRef State, + SymbolRef Sym, DefinedOrUnknownSVal Value, + const QualType &RetTy) const override { + ConditionTruthVal Nullness = State->isNull(Value); + return Nullness.isConstrainedFalse(); + } +}; + +using FnFilter = std::function; + +/// Data about an API function to check. +struct FnInfo { + /// Minimal argument count, only for variadic functions. + Optional MinArgCount; + + /// Checker for the function. + CheckForErrorResultChecker *Checker; + + /// Index of argument whose value at call is to be saved in ParmValMap. + Optional ParmI; + + /// Possibility to exclude a function from the check. + FnFilter Filter; + + /// Return type of the function (initialized at runtime). + mutable QualType RetTy; + + FnInfo(CheckForErrorResultChecker *Checker, Optional ParmI = {}, + FnFilter Filter = {}) + : Checker(Checker), ParmI(ParmI), Filter(Filter) { + ; + } + FnInfo(unsigned int MinArgCount, CheckForErrorResultChecker *Checker, + Optional ParmI = {}, FnFilter Filter = {}) + : MinArgCount(MinArgCount), Checker(Checker), ParmI(ParmI), + Filter(Filter) { + ; + } +}; + +class ErrorReturnChecker + : public Checker { + mutable std::unique_ptr BT_Unchecked; + +public: + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkBranchCondition(const Stmt *S, CheckerContext &C) const; + void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, + ExprEngine &Eng) const; + +private: + NullErrorResultChecker CheckNullErrorResult; + + CallDescriptionMap CheckedFunctions = { + {{"aligned_alloc", 2}, FnInfo{&CheckNullErrorResult}}, + {{"bsearch", 5}, FnInfo{&CheckNullErrorResult}}, + {{"bsearch_s", 6}, FnInfo{&CheckNullErrorResult}}, + {{"calloc", 2}, FnInfo{&CheckNullErrorResult}}, + {{"fgets", 3}, FnInfo{&CheckNullErrorResult}}, + {{"fopen", 2}, FnInfo{&CheckNullErrorResult}}, + {{"freopen", 3}, FnInfo{&CheckNullErrorResult}}, + {{"getenv", 1}, FnInfo{&CheckNullErrorResult}}, + {{"getenv_s", 4}, FnInfo{&CheckNullErrorResult}}, + {{"gets_s", 2}, FnInfo{&CheckNullErrorResult}}, + {{"gmtime", 1}, FnInfo{&CheckNullErrorResult}}, + {{"gmtime_s", 2}, FnInfo{&CheckNullErrorResult}}, + {{"localtime", 1}, FnInfo{&CheckNullErrorResult}}, + {{"localtime_s", 2}, FnInfo{&CheckNullErrorResult}}, + {{"malloc", 1}, FnInfo{&CheckNullErrorResult}}, + {{"memchr", 3}, FnInfo{&CheckNullErrorResult}}, + {{"realloc", 2}, FnInfo{&CheckNullErrorResult}}, + {{"setlocale", 2}, FnInfo{&CheckNullErrorResult}}, + {{"strchr", 2}, FnInfo{&CheckNullErrorResult}}, + {{"strpbrk", 2}, FnInfo{&CheckNullErrorResult}}, + {{"strrchr", 2}, FnInfo{&CheckNullErrorResult}}, + {{"strstr", 2}, FnInfo{&CheckNullErrorResult}}, + {{"strtok", 2}, FnInfo{&CheckNullErrorResult}}, + {{"strtok_s", 4}, FnInfo{&CheckNullErrorResult}}, + {{"tmpfile", 0}, FnInfo{&CheckNullErrorResult}}, + {{"tmpnam", 1}, FnInfo{&CheckNullErrorResult}}, + {{"wcschr", 2}, FnInfo{&CheckNullErrorResult}}, + {{"wcspbrk", 2}, FnInfo{&CheckNullErrorResult}}, + {{"wcsrchr", 2}, FnInfo{&CheckNullErrorResult}}, + {{"wcsstr", 2}, FnInfo{&CheckNullErrorResult}}, + {{"wcstok", 3}, FnInfo{&CheckNullErrorResult}}, + {{"wcstok_s", 4}, FnInfo{&CheckNullErrorResult}}, + {{"wmemchr", 3}, FnInfo{&CheckNullErrorResult}}, + }; +}; + +struct CalledFunctionData { + const FnInfo *Info; + SourceRange CallLocation; + bool ErrorCheckedBranchFound; + + CalledFunctionData(const CalledFunctionData &CFD) + : Info(CFD.Info), + CallLocation(CFD.CallLocation), ErrorCheckedBranchFound{ + CFD.ErrorCheckedBranchFound} {} + CalledFunctionData(const FnInfo *Info, const SourceRange &CallLocation, + bool ErrorCheckedBranchFound) + : Info{Info}, CallLocation{CallLocation}, ErrorCheckedBranchFound{ + ErrorCheckedBranchFound} {} + + CalledFunctionData &operator=(const CalledFunctionData &CFD) { + Info = CFD.Info; + CallLocation = CFD.CallLocation; + ErrorCheckedBranchFound = CFD.ErrorCheckedBranchFound; + return *this; + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddPointer(Info); + ID.AddInteger(CallLocation.getBegin().getRawEncoding()); + ID.AddBoolean(ErrorCheckedBranchFound); + } + + bool operator==(const CalledFunctionData &CFD) const { + return Info == CFD.Info && CallLocation == CFD.CallLocation && + ErrorCheckedBranchFound == CFD.ErrorCheckedBranchFound; + } +}; + +} // end anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(CalledFunctionDataMap, SymbolRef, + CalledFunctionData) + +void ErrorReturnChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + const auto *FD = dyn_cast_or_null(Call.getDecl()); + ProgramStateRef State = C.getState(); + const ParentMap &PM = C.getLocationContext()->getParentMap(); + + if (!FD || FD->getKind() != Decl::Function) + return; + + if (!Call.isGlobalCFunction() || !Call.isInSystemHeader()) + return; + + const FnInfo *Fn = CheckedFunctions.lookup(Call); + if (!Fn) + return; + + // For variadic functions check for minimal argument count. + if (Fn->MinArgCount && Call.getNumArgs() < *(Fn->MinArgCount)) + return; + + const Stmt *S = PM.getParent(Call.getOriginExpr()); + + // Check for explicit cast to void. + if (auto *Cast = dyn_cast(S)) { + if (Cast->getTypeAsWritten().getTypePtr()->isVoidType()) + return; + } + + // Use a function-specific rule to omit specific calls. + if (Fn->Filter && Fn->Filter(Call, C)) + return; + + // The call should have a symbolic return value to analyze it. + SVal RetSV = Call.getReturnValue(); + if (RetSV.isUnknownOrUndef()) + return; + SymbolRef RetSym = RetSV.getAsSymbol(); + if (!RetSym) + return; + + // Lazy-init the return type when the function is found. + if (Fn->RetTy.isNull()) + Fn->RetTy = FD->getReturnType(); + + CalledFunctionData CFD{Fn, Call.getSourceRange(), false}; + State = State->set(RetSym, CFD); + + // Try to compute value of specific argument at the time of call (if needed). + if (Fn->ParmI) { + const llvm::APSInt *ParmVal = + C.getSValBuilder().getKnownValue(State, Call.getArgSVal(*Fn->ParmI)); + if (ParmVal) + State = State->set(RetSym, *ParmVal); + } + + C.addTransition(State); +} + +void ErrorReturnChecker::checkBranchCondition(const Stmt *S, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SValBuilder &SVB = C.getSValBuilder(); + + // For all previously encountered function calls, check if the + // error-checked-condition is true here. + const CalledFunctionDataMapTy &Map = State->get(); + ProgramStateRef OldState = State; + for (const auto &I : Map) { + SymbolRef Sym = I.first; + if (I.second.ErrorCheckedBranchFound) + continue; + auto Val = SVB.makeSymbolVal(Sym).getAs(); + if (Val) { + bool ErrorCheckedCondition = + I.second.Info->Checker->testErrorCheckedCondition( + C, State, I.first, *Val, I.second.Info->RetTy); + if (ErrorCheckedCondition) + State = State->set( + Sym, + CalledFunctionData{I.second.Info, I.second.CallLocation, true}); + } + } + if (OldState != State) + C.addTransition(State); +} + +void ErrorReturnChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, + ExprEngine &Eng) const { + llvm::DenseMap FoundFunctions; + // Examine each end-of-path to collect the function calls and search for a + // path where the error-checked-condition was satisfied. If one such path is + // found the function is assumed to have correct error checking in the code. + for (ExplodedGraph::eop_iterator I = G.eop_begin(), E = G.eop_end(); I != E; + ++I) { + const ExplodedNode *N = *I; + const ProgramStateRef State = N->getState(); + const CalledFunctionDataMapTy &Map = State->get(); + for (const auto &F : Map) { + auto InsertResult = FoundFunctions.insert(F); + if (!InsertResult.second) { + CalledFunctionData &FoundCFD = InsertResult.first->second; + assert((F.second.CallLocation == FoundCFD.CallLocation) && + "Function data mistatch?"); + FoundCFD.ErrorCheckedBranchFound = FoundCFD.ErrorCheckedBranchFound || + F.second.ErrorCheckedBranchFound; + } + } + } + + // Report the error for calls without found error check. + for (const auto &F : FoundFunctions) { + const CalledFunctionData &CFD = F.second; + if (!CFD.ErrorCheckedBranchFound) { + if (!BT_Unchecked) + BT_Unchecked.reset(new BuiltinBug( + this, "Unchecked return value", + "Missing or incomplete error check of return value")); + auto Report = std::make_unique( + *BT_Unchecked, BT_Unchecked->getDescription(), + PathDiagnosticLocation{CFD.CallLocation.getBegin(), + BR.getSourceManager()}); + Report->addRange(CFD.CallLocation); + BR.emitReport(std::move(Report)); + } + } +} + +void ento::registerErrorReturnChecker(CheckerManager &mgr) { + mgr.registerChecker(); +} + +bool ento::shouldRegisterErrorReturnChecker(const LangOptions &LO) { + return true; +} diff --git a/clang/test/Analysis/Inputs/system-header-simulator.h b/clang/test/Analysis/Inputs/system-header-simulator.h --- a/clang/test/Analysis/Inputs/system-header-simulator.h +++ b/clang/test/Analysis/Inputs/system-header-simulator.h @@ -112,4 +112,8 @@ #define NULL __DARWIN_NULL #endif -#define offsetof(t, d) __builtin_offsetof(t, d) \ No newline at end of file +#define offsetof(t, d) __builtin_offsetof(t, d) + +// Some more functions. + +void *aligned_alloc(size_t alignment, size_t size); diff --git a/clang/test/Analysis/error-return.c b/clang/test/Analysis/error-return.c new file mode 100644 --- /dev/null +++ b/clang/test/Analysis/error-return.c @@ -0,0 +1,342 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.unix.ErrorReturn -verify %s + +#include "Inputs/system-header-simulator.h" + +/* +Functions from CERT ERR33-C that should be checked for error: +https://wiki.sei.cmu.edu/confluence/display/c/ERR33-C.+Detect+and+handle+standard+library+errors + +void *aligned_alloc( size_t alignment, size_t size ); +errno_t asctime_s(char *buf, rsize_t bufsz, const struct tm *time_ptr); +int at_quick_exit( void (*func)(void) ); +int atexit( void (*func)(void) ); +void* bsearch( const void *key, const void *ptr, size_t count, size_t size, + int (*comp)(const void*, const void*) ); +void* bsearch_s( const void *key, const void *ptr, rsize_t count, rsize_t size, + int (*comp)(const void *, const void *, void *), + void *context ); +wint_t btowc( int c ); +size_t c16rtomb( char * restrict s, char16_t c16, mbstate_t * restrict ps ); +size_t c32rtomb( char * restrict s, char32_t c32, mbstate_t * restrict ps ); +void* calloc( size_t num, size_t size ); +clock_t clock(void); +int cnd_broadcast( cnd_t *cond ); +int cnd_init( cnd_t* cond ); +int cnd_signal( cnd_t *cond ); +int cnd_timedwait( cnd_t* restrict cond, mtx_t* restrict mutex, + const struct timespec* restrict time_point ); +int cnd_wait( cnd_t* cond, mtx_t* mutex ); +errno_t ctime_s(char *buffer, rsize_t bufsz, const time_t *time); +int fclose( FILE *stream ); +int fflush( FILE *stream ); +int fgetc( FILE *stream ); +int fgetpos( FILE *restrict stream, fpos_t *restrict pos ); +char *fgets( char *restrict str, int count, FILE *restrict stream ); +wint_t fgetwc( FILE *stream ); +FILE *fopen( const char *restrict filename, const char *restrict mode ); +errno_t fopen_s(FILE *restrict *restrict streamptr, + const char *restrict filename, + const char *restrict mode); +int fprintf( FILE *restrict stream, const char *restrict format, ... ); +int fprintf_s(FILE *restrict stream, const char *restrict format, ...); +int fputc( int ch, FILE *stream ); +int fputs( const char *restrict str, FILE *restrict stream ); +wint_t fputwc( wchar_t ch, FILE *stream ); +int fputws( const wchar_t * restrict str, FILE * restrict stream ); +size_t fread( void *restrict buffer, size_t size, size_t count, + FILE *restrict stream ); +FILE *freopen( const char *restrict filename, const char *restrict mode, + FILE *restrict stream ); +errno_t freopen_s(FILE *restrict *restrict newstreamptr, + const char *restrict filename, const char *restrict mode, + FILE *restrict stream); +int fscanf( FILE *restrict stream, const char *restrict format, ... ); +int fscanf_s(FILE *restrict stream, const char *restrict format, ...); +int fseek( FILE *stream, long offset, int origin ); +int fsetpos( FILE *stream, const fpos_t *pos ); +long ftell( FILE *stream ); +int fwprintf( FILE *restrict stream, + const wchar_t *restrict format, ... ); +int fwprintf_s( FILE *restrict stream, + const wchar_t *restrict format, ...); +size_t fwrite( const void *restrict buffer, size_t size, size_t count, + FILE *restrict stream ); // more exact error return: < count +int fwscanf( FILE *restrict stream, + const wchar_t *restrict format, ... ); +int fwscanf_s( FILE *restrict stream, + const wchar_t *restrict format, ...); +int getc( FILE *stream ); +int getchar(void); +char *getenv( const char *name ); +errno_t getenv_s( size_t *restrict len, char *restrict value, + rsize_t valuesz, const char *restrict name ); +char *gets_s( char *str, rsize_t n ); +wint_t getwc( FILE *stream ); +wint_t getwchar(void); +struct tm *gmtime( const time_t *time ); +struct tm *gmtime_s(const time_t *restrict time, struct tm *restrict result); +struct tm *localtime( const time_t *time ); +struct tm *localtime_s(const time_t *restrict time, struct tm *restrict result); +void* malloc( size_t size ); +int mblen( const char* s, size_t n ); +size_t mbrlen( const char *restrict s, size_t n, mbstate_t *restrict ps ); +size_t mbrtoc16( char16_t * restrict pc16, const char * restrict s, + size_t n, mbstate_t * restrict ps ); +size_t mbrtoc32( char32_t restrict * pc32, const char * restrict s, + size_t n, mbstate_t * restrict ps ); +size_t mbrtowc( wchar_t *restrict pwc, const char *restrict s, size_t n, + mbstate_t *restrict ps ); +size_t mbsrtowcs( wchar_t *restrict dst, const char **restrict src, size_t len, + mbstate_t *restrict ps); +errno_t mbsrtowcs_s( size_t *restrict retval, + wchar_t *restrict dst, rsize_t dstsz, + const char **restrict src, rsize_t len, + mbstate_t *restrict ps); +size_t mbstowcs( wchar_t *restrict dst, const char *restrict src, size_t len); +errno_t mbstowcs_s(size_t *restrict retval, wchar_t *restrict dst, + rsize_t dstsz, const char *restrict src, rsize_t len); +int mbtowc( wchar_t *restrict pwc, const char *restrict s, size_t n ); +void* memchr( const void* ptr, int ch, size_t count ); +time_t mktime( struct tm *time ); +int mtx_init( mtx_t* mutex, int type ); +int mtx_lock( mtx_t* mutex ); +int mtx_timedlock( mtx_t *restrict mutex, + const struct timespec *restrict time_point ); +int mtx_trylock( mtx_t *mutex ); +int mtx_unlock( mtx_t *mutex ); +int printf_s(const char *restrict format, ...); +int putc( int ch, FILE *stream ); +wint_t putwc( wchar_t ch, FILE *stream ); +int raise( int sig ); +void *realloc( void *ptr, size_t new_size ); +int remove( const char *fname ); +int rename( const char *old_filename, const char *new_filename ); +char* setlocale( int category, const char* locale); +int setvbuf( FILE *restrict stream, char *restrict buffer, + int mode, size_t size ); +int scanf( const char *restrict format, ... ); +int scanf_s(const char *restrict format, ...); +void (*signal( int sig, void (*handler) (int))) (int); +int snprintf( char *restrict buffer, size_t bufsz, + const char *restrict format, ... ); +int snprintf_s(char *restrict buffer, rsize_t bufsz, + const char *restrict format, ...); +int snwprintf_s( wchar_t * restrict s, rsize_t n, + const wchar_t * restrict format, ...); // missing from CERT list +int sprintf( char *restrict buffer, const char *restrict format, ... ); +int sprintf_s(char *restrict buffer, rsize_t bufsz, + const char *restrict format, ...); +int sscanf( const char *restrict buffer, const char *restrict format, ... ); +int sscanf_s(const char *restrict buffer, const char *restrict format, ...); +char *strchr( const char *str, int ch ); +errno_t strerror_s( char *buf, rsize_t bufsz, errno_t errnum ); +size_t strftime( char *restrict str, size_t count, + const char *restrict format, const struct tm *restrict time ); +char* strpbrk( const char* dest, const char* breakset ); +char *strrchr( const char *str, int ch ); +char *strstr( const char* str, const char* substr ); +double strtod( const char *restrict str, char **restrict str_end ); +float strtof( const char *restrict str, char **restrict str_end ); +intmax_t strtoimax( const char *restrict nptr, + char **restrict endptr, int base ); +char *strtok( char *restrict str, const char *restrict delim ); +char *strtok_s(char *restrict str, rsize_t *restrict strmax, + const char *restrict delim, char **restrict ptr); +long strtol( const char *restrict str, char **restrict str_end, int base ); +long double strtold( const char *restrict str, char **restrict str_end ); +long long strtoll( const char *restrict str, char **restrict str_end, int base ); +uintmax_t strtoumax( const char *restrict nptr, + char **restrict endptr, int base ); +unsigned long strtoul( const char *restrict str, char **restrict str_end, + int base ); +unsigned long long strtoull( const char *restrict str, char **restrict str_end, + int base ); +size_t strxfrm( char *restrict dest, const char *restrict src, + size_t count ); +int swprintf( wchar_t *restrict buffer, size_t bufsz, + const wchar_t *restrict format, ... ); +int swprintf_s( wchar_t *restrict buffer, rsize_t bufsz, + const wchar_t* restrict format, ...); +int swscanf( const wchar_t *restrict buffer, + const wchar_t *restrict format, ... ); +int swscanf_s( const wchar_t *restrict s, + const wchar_t *restrict format, ...); +int thrd_create( thrd_t *thr, thrd_start_t func, void *arg ); +int thrd_detach( thrd_t thr ); +int thrd_join( thrd_t thr, int *res ); +int thrd_sleep( const struct timespec* duration, + struct timespec* remaining ); +time_t time( time_t *arg ); +int timespec_get( struct timespec *ts, int base); +FILE *tmpfile(void); +errno_t tmpfile_s(FILE * restrict * restrict streamptr); +char *tmpnam( char *filename ); +errno_t tmpnam_s(char *filename_s, rsize_t maxsize); +int tss_create( tss_t* tss_key, tss_dtor_t destructor ); +void *tss_get( tss_t tss_key ); +int tss_set( tss_t tss_id, void *val ); +int ungetc( int ch, FILE *stream ); +wint_t ungetwc( wint_t ch, FILE *stream ); +int vfprintf( FILE *restrict stream, const char *restrict format, + va_list vlist ); +int vfprintf_s( FILE *restrict stream, const char *restrict format, + va_list arg); +int vfscanf( FILE *restrict stream, const char *restrict format, + va_list vlist ); +int vfscanf_s( FILE *restrict stream, const char *restrict format, + va_list vlist); +int vfwprintf( FILE *restrict stream, + const wchar_t *restrict format, va_list vlist ); +int vfwprintf_s( FILE * restrict stream, + const wchar_t *restrict format, va_list vlist); +int vfwscanf( FILE *restrict stream, + const wchar_t *restrict format, va_list vlist ); +int vfwscanf_s( FILE *restrict stream, + const wchar_t *restrict format, va_list vlist ); +int vprintf( const char *restrict format, va_list vlist ); // missing from CERT list +int vprintf_s( const char *restrict format, va_list arg); +int vscanf( const char *restrict format, va_list vlist ); +int vscanf_s(const char *restrict format, va_list vlist); +int vsnprintf( char *restrict buffer, size_t bufsz, + const char *restrict format, va_list vlist ); +int vsnprintf_s(char *restrict buffer, rsize_t bufsz, + const char *restrict format, va_list arg); +int vsnwprintf_s( wchar_t *restrict buffer, rsize_t bufsz, + const wchar_t *restrict format, va_list vlist); // missing from CERT list +int vsprintf( char *restrict buffer, const char *restrict format, + va_list vlist ); +int vsprintf_s( char *restrict buffer, rsize_t bufsz, + const char *restrict format, va_list arg); +int vsscanf( const char *restrict buffer, const char *restrict format, + va_list vlist ); +int vsscanf_s( const char *restrict buffer, const char *restrict format, + va_list vlist); +int vswprintf( wchar_t *restrict buffer, size_t bufsz, + const wchar_t *restrict format, va_list vlist ); +int vswprintf_s( wchar_t *restrict buffer, rsize_t bufsz, + const wchar_t * restrict format, va_list vlist); +int vswscanf( const wchar_t *restrict buffer, + const wchar_t *restrict format, va_list vlist ); +int vswscanf_s( const wchar_t *restrict buffer, + const wchar_t *restrict format, va_list vlist ); +int vwprintf_s( const wchar_t *restrict format, va_list vlist); +int vwscanf( const wchar_t *restrict format, va_list vlist ); +int vwscanf_s( const wchar_t *restrict format, va_list vlist ); +size_t wcrtomb( char *restrict s, wchar_t wc, mbstate_t *restrict ps); +errno_t wcrtomb_s(size_t *restrict retval, char *restrict s, rsize_t ssz, + wchar_t wc, mbstate_t *restrict ps); // missing from CERT list +wchar_t* wcschr( const wchar_t* str, wchar_t ch ); +size_t wcsftime( wchar_t* str, size_t count, const wchar_t* format, tm* time ); +wchar_t* wcspbrk( const wchar_t* dest, const wchar_t* str ); +wchar_t* wcsrchr( const wchar_t* str, wchar_t ch ); +size_t wcsrtombs( char *restrict dst, const wchar_t **restrict src, size_t len, + mbstate_t *restrict ps); +errno_t wcsrtombs_s( size_t *restrict retval, char *restrict dst, rsize_t dstsz, + const wchar_t **restrict src, rsize_t len, + mbstate_t *restrict ps); +wchar_t* wcsstr( const wchar_t* dest, const wchar_t* src ); +double wcstod( const wchar_t * restrict str, wchar_t ** restrict str_end ); +float wcstof( const wchar_t * restrict str, wchar_t ** restrict str_end ); +intmax_t wcstoimax( const wchar_t *restrict nptr, + wchar_t **restrict endptr, int base ); +wchar_t *wcstok(wchar_t * restrict str, const wchar_t * restrict delim, + wchar_t **restrict ptr); +wchar_t *wcstok_s( wchar_t *restrict str, rsize_t *restrict strmax, + const wchar_t *restrict delim, wchar_t **restrict ptr); +long wcstol( const wchar_t * str, wchar_t ** restrict str_end, + int base ); +long double wcstold( const wchar_t * restrict str, wchar_t ** restrict str_end ); +long long wcstoll( const wchar_t * restrict str, wchar_t ** restrict str_end, + int base ); +size_t wcstombs( char *restrict dst, const wchar_t *restrict src, size_t len ); +errno_t wcstombs_s( size_t *restrict retval, char *restrict dst, rsize_t dstsz, + const wchar_t *restrict src, rsize_t len ); +uintmax_t wcstoumax( const wchar_t *restrict nptr, + wchar_t **restrict endptr, int base ); +unsigned long wcstoul( const wchar_t * restrict str, + wchar_t ** restrict str_end, int base ); +unsigned long long wcstoull( const wchar_t * restrict str, + wchar_t ** restrict str_end, int base ); +size_t wcsxfrm( wchar_t* restrict dest, const wchar_t* restrict src, size_t count ); +int wctob( wint_t c ); +int wctomb( char *s, wchar_t wc ); +errno_t wctomb_s(int *restrict status, char *restrict s, rsize_t ssz, wchar_t wc); // CERT list contains wrong description? +wctrans_t wctrans( const char* str ); +wctype_t wctype( const char* str ); +wchar_t *wmemchr( const wchar_t *ptr, wchar_t ch, size_t count ); +int wprintf_s( const wchar_t *restrict format, ...); +int wscanf( const wchar_t *restrict format, ... ); +int wscanf_s( const wchar_t *restrict format, ...); + +These are OK if not checked: +int putchar( int ch ); +wint_t putwchar( wchar_t ch ); +int puts( const char *str ); +int printf( const char *restrict format, ... ); +int vprintf( const char *restrict format, va_list vlist ); +int wprintf( const wchar_t *restrict format, ... ); +int vwprintf( const wchar_t *restrict format, va_list vlist ); + +The following functions are OK if not checked. +These have no error return value therefore can not be handled by this checker: +kill_dependency() +memcpy(), wmemcpy() +memmove(), wmemmove() +strcpy(), wcscpy() +strncpy(), wcsncpy() +strcat(), wcscat() +strncat(), wcsncat() +memset(), wmemset() +*/ + +void test_NullCorrectCheck1() { + // FIXME: false positive + void *P = aligned_alloc(4, 8); // expected-warning{{Missing or incomplete error check}} + if (P) { + } +} + +void test_NullCorrectCheck2() { + void *P = aligned_alloc(4, 8); + if (!P) { + } +} + +void test_NullCorrectCheck3() { + void *P = aligned_alloc(4, 8); + if (P == NULL) { + } +} + +void test_NullCorrectCheck4() { + if (aligned_alloc(4, 8) == NULL) { + } +} + +int test_NullCorrectCheck5() { + // FIXME: false positive + return aligned_alloc(4, 8) == NULL; +} + +void test_NullCorrectCheck6() { + // FIXME: false positive + int R = (aligned_alloc(4, 8) == NULL); +} + +void test_NullCorrectCheck7() { + (void)aligned_alloc(4, 8); +} + +void test_NullBadCheck1() { + void *P = aligned_alloc(4, 8); // expected-warning{{Missing or incomplete error check}} +} + +void test_NullBadCheck2() { + aligned_alloc(4, 8); // expected-warning{{Missing or incomplete error check}} +} + +void test_NullBadCheck3() { + // FIXME: no warning + if (aligned_alloc(4, 8) == (void*)1) { // expected-warning{{Missing or incomplete error check}} + } +}