reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus.NewDelete,cplusplus.NewDeleteLeaks,debug.ExprInspection -analyzer-config c++-allocator-inlining=true -std=c++11 -verify -analyzer-config eagerly-assume=false %s

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

typedef __typeof__(sizeof(int)) size_t;

void *conjure();
void exit(int);

struct S;

S *global_s;

// Recursive operator kinda placement new.
void *operator new(size_t size, S *place);

enum class ConstructionKind : char {
  Garbage,
  Recursive
};

struct S {
public:
  int x;
  S(): x(1) {}
  S(int y): x(y) {}

  S(ConstructionKind k) {
    switch (k) {
    case ConstructionKind::Recursive: { // Call one more operator new 'r'ecursively.
      S *s = new (nullptr) S(5);
      x = s->x + 1;
      global_s = s;
      return;
    }
    case ConstructionKind::Garbage: {
      // Leaves garbage in 'x'.
    }
    }
  }
  ~S() {}
};

// Do not try this at home!
void *operator new(size_t size, S *place) {
  if (!place)
    return new S();
  return place;
}

void testThatCharConstructorIndeedYieldsGarbage() {
  S *s = new S(ConstructionKind::Garbage);
  clang_analyzer_eval(s->x == 0); // expected-warning{{UNKNOWN}}
  clang_analyzer_eval(s->x == 1); // expected-warning{{UNKNOWN}}
  // FIXME: This should warn, but MallocChecker doesn't default-bind regions
  // returned by standard operator new to garbage.
  s->x += 1; // no-warning
  delete s;
}


void testChainedOperatorNew() {
  S *s;
  // * Evaluate standard new.
  // * Evaluate constructor S(3).
  // * Bind value for standard new.
  // * Evaluate our custom new.
  // * Evaluate constructor S(Garbage).
  // * Bind value for our custom new.
  s = new (new S(3)) S(ConstructionKind::Garbage);
  clang_analyzer_eval(s->x == 3); // expected-warning{{TRUE}}
  // expected-warning@+9{{Potential leak of memory pointed to by 's'}}

  // * Evaluate standard new.
  // * Evaluate constructor S(Garbage).
  // * Bind value for standard new.
  // * Evaluate our custom new.
  // * Evaluate constructor S(4).
  // * Bind value for our custom new.
  s = new (new S(ConstructionKind::Garbage)) S(4);
  clang_analyzer_eval(s->x == 4); // expected-warning{{TRUE}}
  delete s;

  // -> Enter our custom new (nullptr).
  //   * Evaluate standard new.
  //   * Inline constructor S().
  //   * Bind value for standard new.
  // <- Exit our custom new (nullptr).
  // * Evaluate constructor S(Garbage).
  // * Bind value for our custom new.
  s = new (nullptr) S(ConstructionKind::Garbage);
  clang_analyzer_eval(s->x == 1); // expected-warning{{TRUE}}
  delete s;

  // -> Enter our custom new (nullptr).
  //   * Evaluate standard new.
  //   * Inline constructor S().
  //   * Bind value for standard new.
  // <- Exit our custom new (nullptr).
  // -> Enter constructor S(Recursive).
  //   -> Enter our custom new (nullptr).
  //     * Evaluate standard new.
  //     * Inline constructor S().
  //     * Bind value for standard new.
  //   <- Exit our custom new (nullptr).
  //   * Evaluate constructor S(5).
  //   * Bind value for our custom new (nullptr).
  //   * Assign that value to global_s.
  // <- Exit constructor S(Recursive).
  // * Bind value for our custom new (nullptr).
  global_s = nullptr;
  s = new (nullptr) S(ConstructionKind::Recursive);
  clang_analyzer_eval(global_s); // expected-warning{{TRUE}}
  clang_analyzer_eval(global_s->x == 5); // expected-warning{{TRUE}}
  clang_analyzer_eval(s->x == 6); // expected-warning{{TRUE}}
  delete s;
}