balazske created this revision. Herald added subscribers: cfe-commits, gamesh411, Szelethus, dkrupp, mgorny. Herald added a project: clang.
Currently work-in-progress. This should check if the return values from certain C API functions are checked for error. The list of functions is included. A check for zero (or non-zero) error return value is used for every function. The code is to be extended to work with other types of error conditions and better bug report construction (at least indicate the place of the function). This check should implement SEI CERT C Coding Standard rule "ERR33-C. Detect and handle standard library errors". Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D71510 Files: clang/docs/analyzer/checkers.rst clang/include/clang/StaticAnalyzer/Checkers/Checkers.td clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt clang/lib/StaticAnalyzer/Checkers/ErrorReturnChecker.cpp clang/test/Analysis/Inputs/system-header-simulator.h clang/test/Analysis/error-return.c
Index: clang/test/Analysis/error-return.c =================================================================== --- /dev/null +++ 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}} Index: clang/test/Analysis/Inputs/system-header-simulator.h =================================================================== --- clang/test/Analysis/Inputs/system-header-simulator.h +++ 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); Index: clang/lib/StaticAnalyzer/Checkers/ErrorReturnChecker.cpp =================================================================== --- /dev/null +++ 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<check::PostCall, check::BranchCondition, check::DeadSymbols > { + mutable std::unique_ptr<BuiltinBug> 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<PathSensitiveBugReport>( + *BT_Unchecked, BT_Unchecked->getDescription(), N)); + } + } + + ValueErrorResultChecker ZeroErrorResultChecker; + + CallDescriptionMap<FnInfo> 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<FunctionDecl>(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<FunctionCalledMap>(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<FunctionCalledMap>(); + llvm::SmallSet<SymbolRef, 10> SymbolsToRemove; + for (const auto &I: Map) { + SymbolRef Sym = I.first; + auto Val = SVB.makeSymbolVal(Sym).getAs<DefinedOrUnknownSVal>(); + if (Val) { + bool IsCompleteErrorCheck = I.second.Checker->checkBranchCondition(C, State, *Val); + if (IsCompleteErrorCheck) + SymbolsToRemove.insert(Sym); + } + } + for (const auto I: SymbolsToRemove) { + State = State->remove<FunctionCalledMap>(I); + } + C.addTransition(State); +} + +void ErrorReturnChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + llvm::SmallSet<SymbolRef, 10> BugSymbols; + const FunctionCalledMapTy &Map = State->get<FunctionCalledMap>(); + 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<FunctionCalledMap>(I); + } + ExplodedNode *N = C.generateNonFatalErrorNode(State); + for (const auto I: BugSymbols) { + emitUnchecked(C, N); + (void)I; + } +} + +void ento::registerErrorReturnChecker(CheckerManager &mgr) { + mgr.registerChecker<ErrorReturnChecker>(); +} + +bool ento::shouldRegisterErrorReturnChecker(const LangOptions &LO) { + return true; +} Index: clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt =================================================================== --- clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -35,6 +35,7 @@ DynamicTypePropagation.cpp DynamicTypeChecker.cpp EnumCastOutOfRangeChecker.cpp + ErrorReturnChecker.cpp ExprInspectionChecker.cpp FixedAddressChecker.cpp GCDAntipatternChecker.cpp Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -427,6 +427,10 @@ HelpText<"Check improper use of chroot">, Documentation<HasAlphaDocumentation>; +def ErrorReturnChecker : Checker<"ErrorReturn">, + HelpText<"Check for unchecked error return values">, + Documentation<HasAlphaDocumentation>; + def PthreadLockChecker : Checker<"PthreadLock">, HelpText<"Simple lock -> unlock checker">, Documentation<HasAlphaDocumentation>; Index: clang/docs/analyzer/checkers.rst =================================================================== --- clang/docs/analyzer/checkers.rst +++ 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)
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits