balazske updated this revision to Diff 234730.
balazske added a comment.

Adding a new diff over the previous one.
(The commit was amended accidentally.)


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D71510/new/

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,470 @@
+// 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_ZeroCorrectCheck1() {
+  char buf[1];
+  errno_t err = asctime_s(buf, 1, NULL);
+  if (err == 0) {
+  }
+}
+
+void test_ZeroCorrectCheck2() {
+  char buf[1];
+  errno_t err = asctime_s(buf, 1, NULL);
+  if (err != 0) {
+  }
+}
+
+void test_ZeroCorrectCheck3() {
+  char buf[1];
+  errno_t err = asctime_s(buf, 1, NULL);
+  if (err > 0) {
+  } else if (err < 0) {
+  }
+}
+
+void test_ZeroCorrectCheck4() {
+  char buf[1];
+  errno_t err = asctime_s(buf, 1, NULL);
+  CheckError(err);
+}
+
+void test_ZeroCorrectCheck5() {
+  char buf[1];
+  errno_t err = asctime_s(buf, 1, NULL);
+  if (err + 1 == 1) {
+  }
+}
+
+void test_ZeroCorrectCheck6() {
+  char buf[1];
+  errno_t err = asctime_s(buf, 1, NULL);
+  if (err) { // FIXME: this does not work
+  }
+}
+
+void test_ZeroCorrectCheck7() {
+  char buf[1];
+  errno_t err = asctime_s(buf, 1, NULL);
+  if (!err) {
+  }
+}
+
+void test_ZeroBadCheck1() {
+  char buf[1];
+  asctime_s(buf, 9, NULL);
+} // expected-warning{{Missing or incomplete error check}}
+
+void test_ZeroBadCheck2() {
+  char buf[1];
+  errno_t err = asctime_s(buf, 1, NULL);
+  if (err == 1) {
+  }
+} // expected-warning{{Missing or incomplete error check}}
+
+void test_ZeroBadCheck3() {
+  char buf[1];
+  errno_t err = asctime_s(buf, 1, NULL);
+  if (err < 0) {
+  }
+} // expected-warning{{Missing or incomplete error check}}
+
+void test_ZeroBadCheck4() {
+  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_ZeroBadCheck5() {
+  char buf[1];
+  errno_t err = asctime_s(buf, 1, NULL);
+  if (err > -2) {
+  }
+} // expected-warning{{Missing or incomplete error check}}
+
+void test_ZeroBadCheck6() {
+  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}}
+
+void test_NullCorrectCheck1() {
+  void *P = aligned_alloc(4, 8);
+  if (P) { // FIXME: this does not work
+  }
+}
+
+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_NullBadCheck1() {
+  void *P = aligned_alloc(4, 8);
+} // expected-warning{{Missing or incomplete error check}}
+
+void test_WEofCorrectCheck1() {
+  wint_t X = btowc(3);
+  if (X == WEOF) {
+  }
+}
+
+void test_WEofBadCheck1() {
+  btowc(3);
+} // expected-warning{{Missing or incomplete error check}}
+
+void test_ZeroOrEofCorrectCheck1() {
+  int X = fclose(NULL);
+  if (X == -1) {
+  }
+}
+
+void test_ZeroOrEofCorrectCheck2() {
+  int X = fclose(NULL);
+  if (X < 0) {
+  }
+}
+
+void test_ZeroOrEofCorrectCheck3() {
+  int X = fclose(NULL);
+  if (X == 0) {
+  }
+}
+
+void test_ZeroOrEofBadCheck1() {
+  int X = fclose(NULL);
+  if (X == -2) {
+  }
+} // expected-warning{{Missing or incomplete error check}}
+
+void test_ZeroOrEofBadCheck2() {
+  int X = fclose(NULL);
+  if (X < 1) {
+  }
+} // expected-warning{{Missing or incomplete error check}}
+
+void test_ZeroOrEofBadCheck3() {
+  int X = fclose(NULL);
+  if (X > 0) {
+  }
+} // expected-warning{{Missing or incomplete error check}}
+
+void test_LessThanParmValCorrectCheck1() {
+  int X = fread(NULL, 1, 2, NULL);
+  if (X == 2) {
+  }
+}
+
+void test_LessThanParmValCorrectCheck2() {
+  int X = fread(NULL, 1, 2, NULL);
+  if (X < 2) {
+  }
+}
+
+void test_LessThanParmValCorrectCheck3() {
+  int X = fread(NULL, 1, 2, NULL);
+  if (X >= 2) {
+  } else {
+  }
+}
+
+void test_LessThanParmValBadCheck1() {
+  int X = fread(NULL, 1, 2, NULL);
+  if (X == 1) {
+  }
+} // expected-warning{{Missing or incomplete error check}}
+
+void test_LessThanParmValBadCheck2() {
+  int X = fread(NULL, 1, 2, NULL);
+  if (X < 3) {
+  }
+} // 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,14 @@
 #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;
+typedef unsigned int wint_t;
+#define WEOF (0xffffffffu)
+
+void *aligned_alloc(size_t alignment, size_t size);
+errno_t asctime_s(char *buf, rsize_t bufsz, const struct tm *time_ptr);
+wint_t btowc(int c);
+size_t fread(void *restrict buffer, size_t size, size_t count, FILE *restrict stream);
Index: clang/lib/StaticAnalyzer/Checkers/ErrorReturnChecker.cpp
===================================================================
--- /dev/null
+++ clang/lib/StaticAnalyzer/Checkers/ErrorReturnChecker.cpp
@@ -0,0 +1,516 @@
+//===-- 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/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. 'RetTy' is the return type of the
+  // function (obtained from the AST). 'ParmVal' contains the computed value of
+  // a specific parameter at the time of the function call, if any. Index of the
+  // parameter is set in 'FnInfo' below. 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 QualType &RetTy,
+                                    const llvm::APSInt *ParmVal) const = 0;
+};
+
+// Error return is a fixed value (default zero).
+class ValueErrorResultChecker : public CheckForErrorResultChecker {
+public:
+  ValueErrorResultChecker(uint64_t ErrorResultValue = 0)
+      : ErrorResultValue(ErrorResultValue) {
+    ;
+  }
+
+  bool checkBranchCondition(CheckerContext &C, ProgramStateRef State,
+                            DefinedOrUnknownSVal Value, const QualType &RetTy,
+                            const llvm::APSInt *ParmVal) 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, RetTy));
+    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:
+  uint64_t ErrorResultValue;
+};
+
+// Error return is a NULL value.
+// This should work with any type of null value.
+// FIXME: Is this different from the ValueErrorResultChecker with 0 as value?
+class NullErrorResultChecker : public CheckForErrorResultChecker {
+public:
+  bool checkBranchCondition(CheckerContext &C, ProgramStateRef State,
+                            DefinedOrUnknownSVal Value, const QualType &RetTy,
+                            const llvm::APSInt *ParmVal) const override {
+    ConditionTruthVal Nullness = State->isNull(Value);
+    return Nullness.isConstrained();
+  }
+};
+
+// Error return is any negative value.
+// This should check for the "return value < 0" type of code.
+class NegativeErrorResultChecker : public CheckForErrorResultChecker {
+public:
+  bool checkBranchCondition(CheckerContext &C, ProgramStateRef State,
+                            DefinedOrUnknownSVal Value, const QualType &RetTy,
+                            const llvm::APSInt *ParmVal) const override {
+    SValBuilder &SVB = C.getSValBuilder();
+    // Test if the return value is negative.
+    SVal Eval = SVB.evalBinOp(State, clang::BO_LT, Value,
+                              SVB.makeZeroVal(RetTy), SVB.getConditionType());
+    auto DefEval = Eval.getAs<DefinedOrUnknownSVal>();
+    if (DefEval) {
+      auto Assumed = State->assume(*DefEval);
+      // 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));
+    }
+    return false; // FIXME: this means that every unknown condition check is
+                  // reported
+  }
+};
+
+// 0 is success, -1 (EOF) is error, no other values possible.
+class ZeroOrEofErrorResultChecker : public CheckForErrorResultChecker {
+public:
+  bool checkBranchCondition(CheckerContext &C, ProgramStateRef State,
+                            DefinedOrUnknownSVal Value, const QualType &RetTy,
+                            const llvm::APSInt *ParmVal) const override {
+    SValBuilder &SVB = C.getSValBuilder();
+    DefinedOrUnknownSVal Eval1 =
+        SVB.evalEQ(State, Value, SVB.makeIntVal(0, RetTy));
+    DefinedOrUnknownSVal Eval2 =
+        SVB.evalEQ(State, Value, SVB.makeIntVal(-1, RetTy));
+    auto Assumed1 = State->assume(Eval1);
+    auto Assumed2 = State->assume(Eval2);
+    return ((Assumed1.first && !Assumed1.second) ||
+            (!Assumed1.first && Assumed1.second)) ||
+           ((Assumed2.first && !Assumed2.second) ||
+            (!Assumed2.first && Assumed2.second));
+  }
+};
+
+// Error return value is something less than the value of an argument passed to
+// the function. Typical case: The 'fread' function.
+class LessThanParmValErrorResultChecker : public CheckForErrorResultChecker {
+public:
+  bool checkBranchCondition(CheckerContext &C, ProgramStateRef State,
+                            DefinedOrUnknownSVal Value, const QualType &RetTy,
+                            const llvm::APSInt *ParmVal) const override {
+    if (!ParmVal)
+      return true;
+    SValBuilder &SVB = C.getSValBuilder();
+    DefinedOrUnknownSVal Eval1 =
+        SVB.evalEQ(State, Value, SVB.makeIntVal(*ParmVal));
+    auto Assumed1 = State->assume(Eval1);
+    SVal Eval2 = SVB.evalBinOp(State, BO_LT, Value, SVB.makeIntVal(*ParmVal),
+                               SVB.getConditionType());
+    auto DefEval2 = Eval2.getAs<DefinedOrUnknownSVal>();
+    if (DefEval2) {
+      auto Assumed2 = State->assume(*DefEval2);
+      return ((Assumed1.first && !Assumed1.second) ||
+              (!Assumed1.first && Assumed1.second)) ||
+             ((Assumed2.first && !Assumed2.second) ||
+              (!Assumed2.first && Assumed2.second));
+    } else {
+      return ((Assumed1.first && !Assumed1.second) ||
+              (!Assumed1.first && Assumed1.second));
+    }
+  }
+};
+
+#if 0
+// Error return is a fixed value of an enum.
+// FIXME: It is not possible to get value of a specific enumerator (whose string name is known)
+// without a preprocessor object.
+class EnumErrorResultChecker : public CheckForErrorResultChecker {
+public:
+  EnumErrorResultChecker(const std::string &ErrorResultValue) : ErrorResultValue(ErrorResultValue) {;}
+
+  bool checkBranchCondition(CheckerContext &C, ProgramStateRef State, DefinedOrUnknownSVal Value, const QualType &RetTy) const override {
+
+  }
+private:
+  std::string ErrorResultValue;
+};
+#endif
+
+struct FnInfo {
+  CheckForErrorResultChecker *Checker;
+  Optional<unsigned int> ParmI;
+  mutable QualType RetTy;
+  mutable const llvm::APSInt *ParmVal;
+
+  FnInfo(CheckForErrorResultChecker *Checker, Optional<unsigned int> ParmI = {})
+      : Checker(Checker), ParmI(ParmI), ParmVal(nullptr) {
+    ;
+  }
+
+  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));
+    }
+  }
+
+  // These checkers are symmetric for the error condition.
+  // (The 'zero error' works with check for zero or nonzero error return value.)
+  ValueErrorResultChecker CheckZeroErrorResult;
+  NullErrorResultChecker CheckNullErrorResult;
+  ValueErrorResultChecker CheckEofErrorResult{-1ul};
+  NegativeErrorResultChecker CheckNegativeErrorResult;
+  ZeroOrEofErrorResultChecker CheckZeroOrEofErrorResult;
+  LessThanParmValErrorResultChecker CheckLessThanParmValErrorResult;
+
+  CallDescriptionMap<FnInfo> CheckedFunctions = {
+      {{"aligned_alloc", 2}, FnInfo{&CheckNullErrorResult}},
+      {{"asctime_s", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"at_quick_exit", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"atexit", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"bsearch", 5}, FnInfo{&CheckNullErrorResult}},
+      {{"bsearch_s", 6}, FnInfo{&CheckNullErrorResult}},
+      {{"btowc", 1}, FnInfo{&CheckEofErrorResult}},
+      {{"c16rtomb", 3}, FnInfo{&CheckEofErrorResult}},
+      {{"c32rtomb", 3}, FnInfo{&CheckEofErrorResult}},
+      {{"calloc", 2}, FnInfo{&CheckNullErrorResult}},
+      {{"clock", 0}, FnInfo{&CheckEofErrorResult}},
+
+      // FIXME: Knowledge of enum values is needed for these.
+      // (It is not possible to get a Preprocessor.)
+      //{{"cnd_broadcast", 1}, FnInfo{&CheckZeroErrorResult}},
+      //{{"cnd_init", 1}, FnInfo{&CheckZeroErrorResult}},
+      //{{"cnd_signal", 1}, FnInfo{&CheckZeroErrorResult}},
+      //{{"cnd_timedwait", 3}, FnInfo{&CheckZeroErrorResult}},
+      //{{"cnd_wait", 2}, FnInfo{&CheckZeroErrorResult}},
+
+      {{"ctime_s", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"fclose", 1}, FnInfo{&CheckZeroOrEofErrorResult}},
+      {{"fflush", 1}, FnInfo{&CheckZeroOrEofErrorResult}},
+      {{"fgetc", 1}, FnInfo{&CheckEofErrorResult}},
+      {{"fgetpos", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"fgets", 3}, FnInfo{&CheckNullErrorResult}},
+      {{"fgetwc", 1}, FnInfo{&CheckEofErrorResult}},
+      {{"fopen", 2}, FnInfo{&CheckNullErrorResult}},
+      {{"fopen_s", 3}, FnInfo{&CheckZeroErrorResult}},
+      //{{"fprintf",  2+}, FnInfo{&CheckZeroErrorResult}},
+      //{{"fprintf_s", 2+}, FnInfo{&CheckZeroErrorResult}},
+      {{"fputc", 2}, FnInfo{&CheckEofErrorResult}},
+      {{"fputs", 2}, FnInfo{&CheckEofErrorResult}},
+      {{"fputwc", 2}, FnInfo{&CheckEofErrorResult}},
+      {{"fputws", 2}, FnInfo{&CheckEofErrorResult}},
+      {{"fread", 4}, FnInfo{&CheckLessThanParmValErrorResult, 2}},
+      // FIXME: Implement next ones.
+      {{"freopen", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"freopen_s", 4}, FnInfo{&CheckZeroErrorResult}},
+      //{{"fscanf", 2+}, FnInfo{&CheckZeroErrorResult}},
+      //{{"fscanf_s", 2+}, FnInfo{&CheckZeroErrorResult}},
+      {{"fseek", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"fsetpos", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"ftell", 1}, FnInfo{&CheckZeroErrorResult}},
+      //{{"fwprintf", 2+}, FnInfo{&CheckZeroErrorResult}},
+      //{{"fwprintf_s", 2+}, FnInfo{&CheckZeroErrorResult}},
+      {{"fwrite", 4}, FnInfo{&CheckZeroErrorResult}},
+      //{{"fwscanf", 2+}, FnInfo{&CheckZeroErrorResult}},
+      //{{"fwscanf_s", 2+}, FnInfo{&CheckZeroErrorResult}},
+      {{"getc", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"getchar", 0}, FnInfo{&CheckZeroErrorResult}},
+      {{"getenv", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"getenv_s", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"gets_s", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"getwc", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"getwchar", 0}, FnInfo{&CheckZeroErrorResult}},
+      {{"gmtime", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"gmtime_s", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"localtime", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"localtime_s", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"malloc", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"mblen", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"mbrlen", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"mbrtoc16", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"mbrtoc32", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"mbrtowc", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"mbsrtowcs", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"mbsrtowcs_s", 6}, FnInfo{&CheckZeroErrorResult}},
+      {{"mbtowc", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"memchr", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"mktime", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"mtx_init", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"mtx_lock", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"mtx_timedlock", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"mtx_trylock", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"mtx_unlock", 1}, FnInfo{&CheckZeroErrorResult}},
+      //{{"printf_s", 1+}, FnInfo{&CheckZeroErrorResult}},
+      {{"putc", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"putwc", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"raise", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"realloc", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"remove", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"rename", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"setlocale", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"setvbuf", 4}, FnInfo{&CheckZeroErrorResult}},
+      //{{"scanf", 1+}, FnInfo{&CheckZeroErrorResult}},
+      //{{"scanf_s", 1+}, FnInfo{&CheckZeroErrorResult}},
+      {{"signal", 2}, FnInfo{&CheckZeroErrorResult}},
+      //{{"snprintf", 3+}, FnInfo{&CheckZeroErrorResult}},
+      //{{"snprintf_s", 3+}, FnInfo{&CheckZeroErrorResult}},
+      //{{"snwprintf_s", 3+}, FnInfo{&CheckZeroErrorResult}},
+      //{{"sprintf", 2+}, FnInfo{&CheckZeroErrorResult}},
+      //{{"sprintf_s", 3+}, FnInfo{&CheckZeroErrorResult}},
+      //{{"sscanf", 2+}, FnInfo{&CheckZeroErrorResult}},
+      //{{"sscanf_s", 2+}, FnInfo{&CheckZeroErrorResult}},
+      {{"strchr", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"strerror_s", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"strftime", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"strpbrk", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"strrchr", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"strstr", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"strtod", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"strtof", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"strtoimax", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"strtok", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"strtok_s", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"strtol", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"strtold", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"strtoll", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"strtoumax", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"strtoul", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"strtoull", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"strxfrm", 3}, FnInfo{&CheckZeroErrorResult}},
+      //{{"swprintf", 3+}, FnInfo{&CheckZeroErrorResult}},
+      //{{"swprintf_s", 3+}, FnInfo{&CheckZeroErrorResult}},
+      //{{"swscanf", 2+}, FnInfo{&CheckZeroErrorResult}},
+      //{{"swscanf_s", 2+}, FnInfo{&CheckZeroErrorResult}},
+      {{"thrd_create", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"thrd_detach", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"thrd_join", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"thrd_sleep", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"time", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"timespec_get", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"tmpfile", 0}, FnInfo{&CheckZeroErrorResult}},
+      {{"tmpfile_s", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"tmpnam", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"tmpnam_s", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"tss_create", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"tss_get", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"tss_set", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"ungetc", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"ungetwc", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"vprintf", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"vfprintf", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"vfprintf_s", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"vfscanf", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"vfscanf_s", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"vfwprintf", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"vfwprintf_s", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"vfwscanf", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"vfwscanf_s", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"vprintf_s", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"vscanf", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"vscanf_s", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"vsnprintf", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"vsnprintf_s", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"vsnwprintf_s", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"vsprintf", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"vsprintf_s", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"vsscanf", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"vsscanf_s", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"vswprintf", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"vswprintf_s", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"vswscanf", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"vswscanf_s", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"vwprintf_s", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"vwscanf", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"vwscanf_s", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcrtomb", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcrtomb_s", 5}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcschr", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcsftime", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcspbrk", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcsrchr", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcsrtombs", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcsrtombs_s", 5}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcsstr", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcstod", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcstof", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcstoimax", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcstok", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcstok_s", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcstol", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcstold", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcstoll", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcstombs", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcstombs_s", 5}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcstoumax", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcstoul", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcstoull", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"wcsxfrm", 3}, FnInfo{&CheckZeroErrorResult}},
+      {{"wctob", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"wctomb", 2}, FnInfo{&CheckZeroErrorResult}},
+      {{"wctomb_s", 4}, FnInfo{&CheckZeroErrorResult}},
+      {{"wctrans", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"wctype", 1}, FnInfo{&CheckZeroErrorResult}},
+      {{"wmemchr", 3}, FnInfo{&CheckZeroErrorResult}},
+      //{{"wprintf_s", 1+}, FnInfo{&CheckZeroErrorResult}},
+      //{{"wscanf", 1+}, FnInfo{&CheckZeroErrorResult}},
+      //{{"wscanf_s", 1+}, FnInfo{&CheckZeroErrorResult}},
+  };
+};
+
+} // 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());
+  ProgramStateRef State = C.getState();
+
+  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;
+
+  // Lazy-init the return type when the function is found.
+  if (Fn->RetTy.isNull())
+    Fn->RetTy = FD->getReturnType();
+
+  // Try to compute value of a specific argument (needed later).
+  if (Fn->ParmI)
+    Fn->ParmVal =
+        C.getSValBuilder().getKnownValue(State, Call.getArgSVal(*Fn->ParmI));
+
+  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, I.second.RetTy, I.second.ParmVal);
+      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

Reply via email to