diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst --- a/clang/docs/analyzer/checkers.rst +++ b/clang/docs/analyzer/checkers.rst @@ -2083,6 +2083,17 @@ f(); // warn: no call of chdir("/") immediately after chroot } +.. _alpha-unix-ErrorReturn: + +alpha.unix.ErrorReturn (C) +"""""""""""""""""""""" +Check for unchecked return value from API functions: ``xxx, yyy,`` +``zzz``. + +.. code-block:: c + + // warn: ?????????? + .. _alpha-unix-PthreadLock: alpha.unix.PthreadLock (C) 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 @@ -427,6 +427,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 @@ -35,6 +35,7 @@ DynamicTypePropagation.cpp DynamicTypeChecker.cpp EnumCastOutOfRangeChecker.cpp + ErrorReturnChecker.cpp ExprInspectionChecker.cpp FixedAddressChecker.cpp GCDAntipatternChecker.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,364 @@ +//===-- 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 +// +//===----------------------------------------------------------------------===// +// +// Check that return values from certain C API functions are checked for error. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ParentMap.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" + +using namespace clang; +using namespace ento; + +namespace { + +class CheckForErrorResultChecker { +public: + // See if the result value from the system function (to check) is checked for error after a branch condition. + // 'Value' contains the (mostly conjured) symbolic value of the function call. + // Return if the state after this branch condition is a correct check for error return condition. + virtual bool checkBranchCondition(CheckerContext &C, ProgramStateRef State, DefinedOrUnknownSVal FunctionCall) const = 0; +}; + +// Error return is a fixed value (default zero). +class ValueErrorResultChecker : public CheckForErrorResultChecker { +public: + ValueErrorResultChecker(int ErrorResultValue = 0) : ErrorResultValue(ErrorResultValue) {;} + + bool checkBranchCondition(CheckerContext &C, ProgramStateRef State, DefinedOrUnknownSVal Value) const override { + SValBuilder &SVB = C.getSValBuilder(); + // Test if the return value equals a fixed error code. + DefinedOrUnknownSVal Eval = SVB.evalEQ(State, Value, SVB.makeIntVal(ErrorResultValue, C.getASTContext().getIntMaxType())); + auto Assumed = State->assume(Eval); + // Try to get the exact return value (in the current state). + //const llvm::APSInt *KV = SVB.getKnownValue(State, Value); + //if (KV) + // llvm::errs() << "Known value:" << *KV << "\n"; + //else + // llvm::errs() << "Value not known\n"; + // The error check is correct if we know that the error or the non-error condition exactly is satisfied. + return ((Assumed.first && !Assumed.second) || (!Assumed.first && Assumed.second)); + } +private: + int ErrorResultValue; +}; + +struct FnInfo { + CheckForErrorResultChecker *Checker; + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddPointer(Checker); + } + + bool operator==(const FnInfo &FnI) const { + return Checker == FnI.Checker; + } +}; + +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 checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + +private: + void emitUnchecked(CheckerContext &C, ExplodedNode *N) const { + if (N) { + if (!BT_Unchecked) + BT_Unchecked.reset(new BuiltinBug(this, "Unchecked return value", + "Missing or incomplete error check of return value")); + C.emitReport(std::make_unique( + *BT_Unchecked, BT_Unchecked->getDescription(), N)); + } + } + + ValueErrorResultChecker ZeroErrorResultChecker; + + CallDescriptionMap CheckedFunctions = { +{{"aligned_alloc", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"asctime_s", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"at_quick_exit", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"atexit", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"bsearch", 5}, FnInfo{&ZeroErrorResultChecker}}, +{{"bsearch_s", 6}, FnInfo{&ZeroErrorResultChecker}}, +{{"btowc", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"c16rtomb", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"c32rtomb", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"calloc", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"clock", 0}, FnInfo{&ZeroErrorResultChecker}}, +{{"cnd_broadcast", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"cnd_init", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"cnd_signal", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"cnd_timedwait", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"cnd_wait", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"ctime_s", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"fclose", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"fflush", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"fgetc", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"fgetpos", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"fgets", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"fgetwc", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"fopen", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"fopen_s", 3}, FnInfo{&ZeroErrorResultChecker}}, +//{{"fprintf", 2+}, FnInfo{&ZeroErrorResultChecker}}, +//{{"fprintf_s", 2+}, FnInfo{&ZeroErrorResultChecker}}, +{{"fputc", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"fputs", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"fputwc", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"fputws", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"fread", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"freopen", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"freopen_s", 4}, FnInfo{&ZeroErrorResultChecker}}, +//{{"fscanf", 2+}, FnInfo{&ZeroErrorResultChecker}}, +//{{"fscanf_s", 2+}, FnInfo{&ZeroErrorResultChecker}}, +{{"fseek", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"fsetpos", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"ftell", 1}, FnInfo{&ZeroErrorResultChecker}}, +//{{"fwprintf", 2+}, FnInfo{&ZeroErrorResultChecker}}, +//{{"fwprintf_s", 2+}, FnInfo{&ZeroErrorResultChecker}}, +{{"fwrite", 4}, FnInfo{&ZeroErrorResultChecker}}, +//{{"fwscanf", 2+}, FnInfo{&ZeroErrorResultChecker}}, +//{{"fwscanf_s", 2+}, FnInfo{&ZeroErrorResultChecker}}, +{{"getc", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"getchar", 0}, FnInfo{&ZeroErrorResultChecker}}, +{{"getenv", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"getenv_s", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"gets_s", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"getwc", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"getwchar", 0}, FnInfo{&ZeroErrorResultChecker}}, +{{"gmtime", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"gmtime_s", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"localtime", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"localtime_s", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"malloc", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"mblen", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"mbrlen", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"mbrtoc16", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"mbrtoc32", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"mbrtowc", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"mbsrtowcs", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"mbsrtowcs_s", 6}, FnInfo{&ZeroErrorResultChecker}}, +{{"mbtowc", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"memchr", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"mktime", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"mtx_init", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"mtx_lock", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"mtx_timedlock", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"mtx_trylock", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"mtx_unlock", 1}, FnInfo{&ZeroErrorResultChecker}}, +//{{"printf_s", 1+}, FnInfo{&ZeroErrorResultChecker}}, +{{"putc", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"putwc", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"raise", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"realloc", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"remove", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"rename", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"setlocale", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"setvbuf", 4}, FnInfo{&ZeroErrorResultChecker}}, +//{{"scanf", 1+}, FnInfo{&ZeroErrorResultChecker}}, +//{{"scanf_s", 1+}, FnInfo{&ZeroErrorResultChecker}}, +{{"signal", 2}, FnInfo{&ZeroErrorResultChecker}}, +//{{"snprintf", 3+}, FnInfo{&ZeroErrorResultChecker}}, +//{{"snprintf_s", 3+}, FnInfo{&ZeroErrorResultChecker}}, +//{{"snwprintf_s", 3+}, FnInfo{&ZeroErrorResultChecker}}, +//{{"sprintf", 2+}, FnInfo{&ZeroErrorResultChecker}}, +//{{"sprintf_s", 3+}, FnInfo{&ZeroErrorResultChecker}}, +//{{"sscanf", 2+}, FnInfo{&ZeroErrorResultChecker}}, +//{{"sscanf_s", 2+}, FnInfo{&ZeroErrorResultChecker}}, +{{"strchr", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"strerror_s", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"strftime", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"strpbrk", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"strrchr", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"strstr", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"strtod", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"strtof", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"strtoimax", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"strtok", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"strtok_s", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"strtol", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"strtold", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"strtoll", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"strtoumax", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"strtoul", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"strtoull", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"strxfrm", 3}, FnInfo{&ZeroErrorResultChecker}}, +//{{"swprintf", 3+}, FnInfo{&ZeroErrorResultChecker}}, +//{{"swprintf_s", 3+}, FnInfo{&ZeroErrorResultChecker}}, +//{{"swscanf", 2+}, FnInfo{&ZeroErrorResultChecker}}, +//{{"swscanf_s", 2+}, FnInfo{&ZeroErrorResultChecker}}, +{{"thrd_create", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"thrd_detach", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"thrd_join", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"thrd_sleep", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"time", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"timespec_get", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"tmpfile", 0}, FnInfo{&ZeroErrorResultChecker}}, +{{"tmpfile_s", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"tmpnam", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"tmpnam_s", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"tss_create", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"tss_get", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"tss_set", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"ungetc", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"ungetwc", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"vprintf", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"vfprintf", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"vfprintf_s", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"vfscanf", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"vfscanf_s", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"vfwprintf", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"vfwprintf_s", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"vfwscanf", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"vfwscanf_s", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"vprintf_s", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"vscanf", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"vscanf_s", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"vsnprintf", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"vsnprintf_s", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"vsnwprintf_s", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"vsprintf", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"vsprintf_s", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"vsscanf", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"vsscanf_s", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"vswprintf", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"vswprintf_s", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"vswscanf", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"vswscanf_s", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"vwprintf_s", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"vwscanf", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"vwscanf_s", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcrtomb", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcrtomb_s", 5}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcschr", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcsftime", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcspbrk", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcsrchr", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcsrtombs", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcsrtombs_s", 5}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcsstr", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcstod", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcstof", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcstoimax", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcstok", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcstok_s", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcstol", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcstold", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcstoll", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcstombs", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcstombs_s", 5}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcstoumax", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcstoul", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcstoull", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"wcsxfrm", 3}, FnInfo{&ZeroErrorResultChecker}}, +{{"wctob", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"wctomb", 2}, FnInfo{&ZeroErrorResultChecker}}, +{{"wctomb_s", 4}, FnInfo{&ZeroErrorResultChecker}}, +{{"wctrans", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"wctype", 1}, FnInfo{&ZeroErrorResultChecker}}, +{{"wmemchr", 3}, FnInfo{&ZeroErrorResultChecker}}, +//{{"wprintf_s", 1+}, FnInfo{&ZeroErrorResultChecker}}, +//{{"wscanf", 1+}, FnInfo{&ZeroErrorResultChecker}}, +//{{"wscanf_s", 1+}, FnInfo{&ZeroErrorResultChecker}}, + }; +}; + +} // end anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(FunctionCalledMap, SymbolRef, FnInfo) + +void ErrorReturnChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { + const auto *FD = dyn_cast_or_null(Call.getDecl()); + if (!FD || FD->getKind() != Decl::Function) + return; + + if (!Call.isGlobalCFunction()) + return; + + const FnInfo *Fn = CheckedFunctions.lookup(Call); + if (!Fn) + return; + + SVal RetSV = Call.getReturnValue(); + if (RetSV.isUnknownOrUndef()) + return; + SymbolRef RetSym = RetSV.getAsSymbol(); + if (!RetSym) + return; + + ProgramStateRef State = C.getState(); + State = State->set(RetSym, *Fn); + C.addTransition(State); +} + +void ErrorReturnChecker::checkBranchCondition(const Stmt *S, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SValBuilder &SVB = C.getSValBuilder(); + + const FunctionCalledMapTy &Map = State->get(); + llvm::SmallSet SymbolsToRemove; + for (const auto &I: Map) { + SymbolRef Sym = I.first; + auto Val = SVB.makeSymbolVal(Sym).getAs(); + if (Val) { + bool IsCompleteErrorCheck = I.second.Checker->checkBranchCondition(C, State, *Val); + if (IsCompleteErrorCheck) + SymbolsToRemove.insert(Sym); + } + } + for (const auto I: SymbolsToRemove) { + State = State->remove(I); + } + C.addTransition(State); +} + +void ErrorReturnChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + llvm::SmallSet BugSymbols; + const FunctionCalledMapTy &Map = State->get(); + for (const auto &I: Map) { + SymbolRef Sym = I.first; + if (!SymReaper.isDead(Sym)) + continue; + + BugSymbols.insert(Sym); + } + + if (BugSymbols.empty()) + return; + + for (const auto I: BugSymbols) { + State = State->remove(I); + } + ExplodedNode *N = C.generateNonFatalErrorNode(State); + for (const auto I: BugSymbols) { + emitUnchecked(C, N); + (void)I; + } +} + +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,11 @@ #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) + + + +typedef __typeof(errno) errno_t; +typedef size_t rsize_t; + +errno_t asctime_s(char *buf, rsize_t bufsz, const struct tm *time_ptr); 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,364 @@ +// 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: + +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); +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 vprintf( const char *restrict format, va_list vlist );​ +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_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); +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 ); +*/ + +void CheckError(errno_t e) { + if (e == 0) { + } +} + +void test_CorrectCheck1() { + char buf[1]; + errno_t err = asctime_s(buf, 1, NULL); + if (err == 0) { + } +} + +void test_CorrectCheck2() { + char buf[1]; + errno_t err = asctime_s(buf, 1, NULL); + if (err != 0) { + } +} + +void test_CorrectCheck3() { + char buf[1]; + errno_t err = asctime_s(buf, 1, NULL); + if (err > 0) { + } else if (err < 0) { + } +} + +void test_CorrectCheck4() { + char buf[1]; + errno_t err = asctime_s(buf, 1, NULL); + CheckError(err); +} + +void test_CorrectCheck5() { + char buf[1]; + errno_t err = asctime_s(buf, 1, NULL); + if (err + 1 == 1) { + } +} + +void test_CorrectCheck6() { + char buf[1]; + errno_t err = asctime_s(buf, 1, NULL); + if (err) { // FIXME: this does not work + } +} + +void test_BadCheck1() { + char buf[1]; + asctime_s(buf, 9, NULL); +} // expected-warning{{Missing or incomplete error check}} + +void test_BadCheck2() { + char buf[1]; + errno_t err = asctime_s(buf, 1, NULL); + if (err == 1) { + } +} // expected-warning{{Missing or incomplete error check}} + +void test_BadCheck3() { + char buf[1]; + errno_t err = asctime_s(buf, 1, NULL); + if (err < 0) { + } +} // expected-warning{{Missing or incomplete error check}} + +void test_BadCheck4() { + char buf[1]; + errno_t err = asctime_s(buf, 1, NULL); + if (err < 0 || err > 2) { + } +} // expected-warning{{Missing or incomplete error check}} + +void test_BadCheck5() { + char buf[1]; + errno_t err = asctime_s(buf, 1, NULL); + if (err > -2) { + } +} // expected-warning{{Missing or incomplete error check}} + +void test_BadCheck6() { + char buf[1]; + errno_t err = asctime_s(buf, 1, NULL); + err = asctime_s(buf, 1, NULL); + if (err == 0) { + } +} // expected-warning{{Missing or incomplete error check}}