Compiler projects using llvm
// RUN: %clang_analyze_cc1 -verify %s \
// RUN:   -analyzer-checker=core \
// RUN:   -analyzer-checker=apiModeling.Errno \
// RUN:   -analyzer-checker=debug.ExprInspection \
// RUN:   -analyzer-checker=debug.ErrnoTest \
// RUN:   -analyzer-checker=alpha.unix.Errno \
// RUN:   -DERRNO_VAR

// RUN: %clang_analyze_cc1 -verify %s \
// RUN:   -analyzer-checker=core \
// RUN:   -analyzer-checker=apiModeling.Errno \
// RUN:   -analyzer-checker=debug.ExprInspection \
// RUN:   -analyzer-checker=debug.ErrnoTest \
// RUN:   -analyzer-checker=alpha.unix.Errno \
// RUN:   -DERRNO_FUNC

#include "Inputs/system-header-simulator.h"
#ifdef ERRNO_VAR
#include "Inputs/errno_var.h"
#endif
#ifdef ERRNO_FUNC
#include "Inputs/errno_func.h"
#endif

void clang_analyzer_eval(int);
void ErrnoTesterChecker_setErrno(int);
int ErrnoTesterChecker_getErrno();
int ErrnoTesterChecker_setErrnoIfError();
int ErrnoTesterChecker_setErrnoIfErrorRange();
int ErrnoTesterChecker_setErrnoCheckState();

void something();

void test() {
  // Test if errno is initialized.
  clang_analyzer_eval(errno == 0); // expected-warning{{TRUE}}

  ErrnoTesterChecker_setErrno(1);
  // Test if errno was recognized and changed.
  clang_analyzer_eval(errno == 1);                         // expected-warning{{TRUE}}
  clang_analyzer_eval(ErrnoTesterChecker_getErrno() == 1); // expected-warning{{TRUE}}

  something();

  // Test if errno was invalidated.
  clang_analyzer_eval(errno);                         // expected-warning{{UNKNOWN}}
  clang_analyzer_eval(ErrnoTesterChecker_getErrno()); // expected-warning{{UNKNOWN}}
}

void testRange(int X) {
  if (X > 0) {
    ErrnoTesterChecker_setErrno(X);
    clang_analyzer_eval(errno > 0); // expected-warning{{TRUE}}
  }
}

void testIfError() {
  if (ErrnoTesterChecker_setErrnoIfError())
    clang_analyzer_eval(errno == 11); // expected-warning{{TRUE}}
}

void testIfErrorRange() {
  if (ErrnoTesterChecker_setErrnoIfErrorRange()) {
    clang_analyzer_eval(errno != 0); // expected-warning{{TRUE}}
    clang_analyzer_eval(errno == 1); // expected-warning{{FALSE}} expected-warning{{TRUE}}
  }
}

void testErrnoCheck0() {
  // If the function returns a success result code, value of 'errno'
  // is unspecified and it is unsafe to make any decision with it.
  // The function did not promise to not change 'errno' if no failure happens.
  int X = ErrnoTesterChecker_setErrnoCheckState();
  if (X == 0) {
    if (errno) { // expected-warning{{An undefined value may be read from 'errno' [alpha.unix.Errno]}}
    }
    if (errno) { // no warning for second time (analysis stops at the first warning)
    }
  }
  X = ErrnoTesterChecker_setErrnoCheckState();
  if (X == 0) {
    if (errno) { // expected-warning{{An undefined value may be read from 'errno' [alpha.unix.Errno]}}
    }
    errno = 0;
  }
  X = ErrnoTesterChecker_setErrnoCheckState();
  if (X == 0) {
    errno = 0;
    if (errno) { // no warning after overwritten 'errno'
    }
  }
}

void testErrnoCheck1() {
  // If the function returns error result code that is out-of-band (not a valid
  // non-error return value) the value of 'errno' can be checked but it is not
  // required to do so.
  int X = ErrnoTesterChecker_setErrnoCheckState();
  if (X == 1) {
    if (errno) { // no warning
    }
  }
  X = ErrnoTesterChecker_setErrnoCheckState();
  if (X == 1) {
    errno = 0; // no warning
  }
}

void testErrnoCheck2() {
  // If the function returns an in-band error result the value of 'errno' is
  // required to be checked to verify if error happened.
  // The same applies to other functions that can indicate failure only by
  // change of 'errno'.
  int X = ErrnoTesterChecker_setErrnoCheckState();
  if (X == 2) {
    errno = 0; // expected-warning{{Value of 'errno' was not checked and is overwritten here [alpha.unix.Errno]}}
    errno = 0;
  }
  X = ErrnoTesterChecker_setErrnoCheckState();
  if (X == 2) {
    errno = 0; // expected-warning{{Value of 'errno' was not checked and is overwritten here [alpha.unix.Errno]}}
    if (errno) {
    }
  }
}

void testErrnoCheck3() {
  int X = ErrnoTesterChecker_setErrnoCheckState();
  if (X == 2) {
    if (errno) {
    }
    errno = 0; // no warning after 'errno' was read
  }
  X = ErrnoTesterChecker_setErrnoCheckState();
  if (X == 2) {
    int A = errno;
    errno = 0; // no warning after 'errno' was read
  }
}

void testErrnoCheckUndefinedLoad() {
  int X = ErrnoTesterChecker_setErrnoCheckState();
  if (X == 0) {
    if (errno) { // expected-warning{{An undefined value may be read from 'errno' [alpha.unix.Errno]}}
    }
  }
}

void testErrnoNotCheckedAtSystemCall() {
  int X = ErrnoTesterChecker_setErrnoCheckState();
  if (X == 2) {
    printf("%i", 1); // expected-warning{{Value of 'errno' was not checked and may be overwritten by function 'printf' [alpha.unix.Errno]}}
    printf("%i", 1); // no warning ('printf' does not change errno state)
  }
}

void testErrnoCheckStateInvalidate() {
  int X = ErrnoTesterChecker_setErrnoCheckState();
  if (X == 0) {
    something();
    if (errno) { // no warning after an invalidating function call
    }
  }
  X = ErrnoTesterChecker_setErrnoCheckState();
  if (X == 0) {
    printf("%i", 1);
    if (errno) { // no warning after an invalidating standard function call
    }
  }
}

void testErrnoCheckStateInvalidate1() {
  int X = ErrnoTesterChecker_setErrnoCheckState();
  if (X == 2) {
    clang_analyzer_eval(errno); // expected-warning{{TRUE}}
    something();
    clang_analyzer_eval(errno); // expected-warning{{UNKNOWN}}
    errno = 0;                  // no warning after invalidation
  }
}

void test_if_cond_in_expr() {
  ErrnoTesterChecker_setErrnoIfError();
  if (errno + 10 > 2) {
    // expected-warning@-1{{An undefined value may be read from 'errno'}}
  }
}

void test_for_cond() {
  ErrnoTesterChecker_setErrnoIfError();
  for (; errno != 0;) {
  // expected-warning@-1{{An undefined value may be read from 'errno'}}
  }
}

void test_do_cond() {
  ErrnoTesterChecker_setErrnoIfError();
  do {
  } while (errno != 0);
  // expected-warning@-1{{An undefined value may be read from 'errno'}}
}

void test_while_cond() {
  ErrnoTesterChecker_setErrnoIfError();
  while (errno != 0) {
  // expected-warning@-1{{An undefined value may be read from 'errno'}}
  }
}

void test_switch_cond() {
  ErrnoTesterChecker_setErrnoIfError();
  switch (errno) {}
  // expected-warning@-1{{An undefined value may be read from 'errno'}}
}

void test_conditional_cond() {
  ErrnoTesterChecker_setErrnoIfError();
  int A = errno ? 1 : 2;
  // expected-warning@-1{{An undefined value may be read from 'errno'}}
}

void test_binary_conditional_cond() {
  ErrnoTesterChecker_setErrnoIfError();
  int A = errno ?: 2;
  // expected-warning@-1{{An undefined value may be read from 'errno'}}
}

void test_errno_store_into_variable() {
  ErrnoTesterChecker_setErrnoIfError();
  int a = errno; // AllowNonConditionErrnoRead is on by default, no warning
}

void test_errno_store_into_variable_in_expr() {
  ErrnoTesterChecker_setErrnoIfError();
  int a = errno > 1; // AllowNonConditionErrnoRead is on by default, no warning
}

int test_errno_return() {
  ErrnoTesterChecker_setErrnoIfError();
  return errno;
}

void test_errno_pointer1() {
  ErrnoTesterChecker_setErrnoIfError();
  int *ErrnoP = &errno;
  int A = errno ? 1 : 2;
  // expected-warning@-1{{An undefined value may be read from 'errno'}}
}

void test_errno_pointer2() {
  ErrnoTesterChecker_setErrnoIfError();
  int *ErrnoP = &errno;
  int A = (*ErrnoP) ? 1 : 2;
  // expected-warning@-1{{An undefined value may be read from 'errno'}}
}

int f(int);

void test_errno_in_condition_in_function_call() {
  ErrnoTesterChecker_setErrnoIfError();
  if (f(errno) != 0) {
  }
}