llvm/clang/test/Analysis/stream-invalidate.c

// RUN: %clang_analyze_cc1 -verify %s \
// RUN: -analyzer-checker=core \
// RUN: -analyzer-checker=unix.Stream \
// RUN: -analyzer-checker=debug.ExprInspection

#include "Inputs/system-header-simulator.h"
#include "Inputs/system-header-simulator-for-valist.h"

void clang_analyzer_eval(int);
void clang_analyzer_dump(int);

void test_fread(void) {
  FILE *F = fopen("file", "r+");
  if (!F)
    return;

  char Buf[3] = {10, 10, 10};
  fread(Buf, 1, 3, F);
  // The check applies to success and failure.
  clang_analyzer_dump(Buf[0]); // expected-warning {{conj_$}} Should not preserve the previous value, thus should not be 10.
  clang_analyzer_dump(Buf[2]); // expected-warning {{conj_$}}
  if (feof(F)) {
    char Buf1[3] = {10, 10, 10};
    fread(Buf1, 1, 3, F); // expected-warning {{is in EOF state}}
    clang_analyzer_dump(Buf1[0]); // expected-warning {{10 S32b}}
    clang_analyzer_dump(Buf1[2]); // expected-warning {{10 S32b}}
  }

  fclose(F);
}

void test_fwrite(void) {
  FILE *F = fopen("file", "r+");
  if (!F)
    return;

  char Buf[3] = {10, 10, 10};
  fwrite(Buf, 1, 3, F);
  // The check applies to success and failure.
  clang_analyzer_dump(Buf[0]); // expected-warning {{10 S32b}}
  clang_analyzer_dump(Buf[2]); // expected-warning {{10 S32b}}

  fclose(F);
}

void test_fgets() {
  FILE *F = tmpfile();
  if (!F)
    return;

  char Buf[3] = {10, 10, 10};
  fgets(Buf, 3, F);
  // The check applies to success and failure.
  clang_analyzer_dump(Buf[0]); // expected-warning {{conj_$}} Should not preserve the previous value, thus should not be 10.
  clang_analyzer_dump(Buf[2]); // expected-warning {{conj_$}}
  if (feof(F)) {
    char Buf1[3] = {10, 10, 10};
    fgets(Buf1, 3, F); // expected-warning {{is in EOF state}}
    clang_analyzer_dump(Buf1[0]); // expected-warning {{10 S32b}}
    clang_analyzer_dump(Buf1[2]); // expected-warning {{10 S32b}}
  }

  fclose(F);
}

void test_fputs() {
  FILE *F = tmpfile();
  if (!F)
    return;

  char *Buf = "aaa";
  fputs(Buf, F);
  // The check applies to success and failure.
  clang_analyzer_dump(Buf[0]); // expected-warning {{97 S32b}}
  clang_analyzer_dump(Buf[2]); // expected-warning {{97 S32b}}
  clang_analyzer_dump(Buf[3]); // expected-warning {{0 S32b}}

  fclose(F);
}

void test_fscanf() {
  FILE *F = tmpfile();
  if (!F)
    return;

  int a = 1;
  unsigned b;
  int Ret = fscanf(F, "%d %u", &a, &b);
  if (Ret == 0) {
    clang_analyzer_dump(a); // expected-warning {{conj_$}}
    // FIXME: should be {{1 S32b}}.
    clang_analyzer_dump(b); // expected-warning {{conj_$}}
    // FIXME: should be {{uninitialized value}}.
  } else if (Ret == 1) {
    clang_analyzer_dump(a); // expected-warning {{conj_$}}
    clang_analyzer_dump(b); // expected-warning {{conj_$}}
    // FIXME: should be {{uninitialized value}}.
  } else if (Ret >= 2) {
    clang_analyzer_dump(a); // expected-warning {{conj_$}}
    clang_analyzer_dump(b); // expected-warning {{conj_$}}
    clang_analyzer_eval(Ret == 2); // expected-warning {{FALSE}} expected-warning {{TRUE}}
    // FIXME: should be only TRUE.
  } else {
    clang_analyzer_dump(a); // expected-warning {{1 S32b}}
    clang_analyzer_dump(b); // expected-warning {{uninitialized value}}
  }

  fclose(F);
}

void test_getdelim(char *P, size_t Sz) {
  FILE *F = tmpfile();
  if (!F)
    return;

  char *P1 = P;
  size_t Sz1 = Sz;
  ssize_t Ret = getdelim(&P, &Sz, '\t', F);
  if (Ret < 0) {
    clang_analyzer_eval(P == P1); // expected-warning {{FALSE}} \
                                  // expected-warning {{TRUE}}
    clang_analyzer_eval(Sz == Sz1); // expected-warning {{FALSE}} \
                                    // expected-warning {{TRUE}}
  } else {
    clang_analyzer_eval(P == P1); // expected-warning {{FALSE}} \
                                  // expected-warning {{TRUE}}
    clang_analyzer_eval(Sz == Sz1); // expected-warning {{FALSE}} \
                                    // expected-warning {{TRUE}}
  }

  fclose(F);
}

void test_fgetpos() {
  FILE *F = tmpfile();
  if (!F)
    return;

  fpos_t Pos = 1;
  int Ret = fgetpos(F, &Pos);
  if (Ret == 0) {
    clang_analyzer_dump(Pos); // expected-warning {{conj_$}}
  } else {
    clang_analyzer_dump(Pos); // expected-warning {{1 S32b}}
  }

  fclose(F);
}

void test_fprintf() {
  FILE *F1 = tmpfile();
  if (!F1)
    return;

  unsigned a = 42;
  char *output = "HELLO";
  int r = fprintf(F1, "%s\t%u\n", output, a);
  // fprintf does not invalidate any of its input
  // 69 is ascii for 'E'
  clang_analyzer_dump(a); // expected-warning {{42 S32b}}
  clang_analyzer_dump(output[1]); // expected-warning {{69 S32b}}
  fclose(F1);
}

int test_vfscanf_inner(const char *fmt, ...) {
  FILE *F1 = tmpfile();
  if (!F1)
    return EOF;

  va_list ap;
  va_start(ap, fmt);

  int r = vfscanf(F1, fmt, ap);

  fclose(F1);
  va_end(ap);
  return r;
}

void test_vfscanf() {
  int i = 42;
  int j = 43;
  int r = test_vfscanf_inner("%d", &i);
  if (r != EOF) {
    // i gets invalidated by the call to test_vfscanf_inner, not by vfscanf.
    clang_analyzer_dump(i); // expected-warning {{conj_$}}
    clang_analyzer_dump(j); // expected-warning {{43 S32b}}
  }
}