aboutsummaryrefslogtreecommitdiff
path: root/clang/test/Analysis/ArrayBound/assumption-reporting.c
blob: 535e623baa8158860be5912657f25bb5dbcc75ea (plain)
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
// RUN: %clang_analyze_cc1 -Wno-array-bounds -analyzer-output=text        \
// RUN:     -analyzer-checker=core,security.ArrayBound,unix.Malloc,optin.taint -verify %s

// When the checker security.ArrayBound encounters an array subscript operation
// that _may be_ in bounds, it assumes that indexing _is_ in bound. These
// assumptions will be reported to the user if the execution path leads to a
// bug report (made by any checker) and the symbol which was constrainted by
// the assumption is marked as interesting (with `markInteresting` or
// indirectly via `trackExpressionValue`) in that bug report.
//
// This test file validates the content of these note tags which describe the
// assumptions for the user.

int TenElements[10];

int irrelevantAssumptions(int arg) {
  int a = TenElements[arg];
  // Here the analyzer assumes that `arg` is in bounds, but doesn't report this
  // because `arg` is not interesting for the bug.
  int b = TenElements[13];
  // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
  // expected-note@-2 {{Access of 'TenElements' at index 13, while it holds only 10 'int' elements}}
  return a + b;
}


int assumingBoth(int arg) {
  int a = TenElements[arg];
  // expected-note@-1 {{Assuming index is non-negative and less than 10, the number of 'int' elements in 'TenElements'}}
  int b = TenElements[arg]; // no additional note, we already assumed that 'arg' is in bounds
  int c = TenElements[arg + 10];
  // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
  // expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
  return a + b + c;
}

int assumingBothPointerToMiddle(int arg) {
  // If we're accessing an TenElements through a pointer pointing to its middle, the checker
  // will speak about the "byte offset" measured from the beginning of the TenElements.
  int *p = TenElements + 2;
  int a = p[arg];
  // expected-note@-1 {{Assuming byte offset is non-negative and less than 40, the extent of 'TenElements'}}

  int b = TenElements[arg]; // This is normal access, and only the lower bound is new.
  int c = TenElements[arg + 10];
  // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
  // expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
  return a + b + c;
}

int assumingLower(int arg) {
  // expected-note@+2 {{Assuming 'arg' is < 10}}
  // expected-note@+1 {{Taking false branch}}
  if (arg >= 10)
    return 0;
  int a = TenElements[arg];
  // expected-note@-1 {{Assuming index is non-negative}}
  int b = TenElements[arg + 10];
  // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
  // expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
  return a + b;
}

int assumingUpper(int arg) {
  // expected-note@+2 {{Assuming 'arg' is >= 0}}
  // expected-note@+1 {{Taking false branch}}
  if (arg < 0)
    return 0;
  int a = TenElements[arg];
  // expected-note@-1 {{Assuming index is less than 10, the number of 'int' elements in 'TenElements'}}
  int b = TenElements[arg - 10];
  // expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
  // expected-note@-2 {{Access of 'TenElements' at negative byte offset}}
  return a + b;
}

int assumingUpperIrrelevant(int arg) {
  // FIXME: The assumption "assuming index is less than 10" is printed because
  // it's assuming something about the interesting variable `arg`; however,
  // it's irrelevant because in this testcase the out of bound access is
  // deduced from the _lower_ bound on `arg`. Currently the analyzer cannot
  // filter out assumptions that are logically irrelevant but "touch"
  // interesting symbols; eventually it would be good to add support for this.

  // expected-note@+2 {{Assuming 'arg' is >= 0}}
  // expected-note@+1 {{Taking false branch}}
  if (arg < 0)
    return 0;
  int a = TenElements[arg];
  // expected-note@-1 {{Assuming index is less than 10, the number of 'int' elements in 'TenElements'}}
  int b = TenElements[arg + 10];
  // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
  // expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
  return a + b;
}

int assumingUpperUnsigned(unsigned arg) {
  int a = TenElements[arg];
  // expected-note@-1 {{Assuming index is less than 10, the number of 'int' elements in 'TenElements'}}
  int b = TenElements[(int)arg - 10];
  // expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
  // expected-note@-2 {{Access of 'TenElements' at negative byte offset}}
  return a + b;
}

int assumingNothing(unsigned arg) {
  // expected-note@+2 {{Assuming 'arg' is < 10}}
  // expected-note@+1 {{Taking false branch}}
  if (arg >= 10)
    return 0;
  int a = TenElements[arg]; // no note here, we already know that 'arg' is in bounds
  int b = TenElements[(int)arg - 10];
  // expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
  // expected-note@-2 {{Access of 'TenElements' at negative byte offset}}
  return a + b;
}

short assumingConvertedToCharP(int arg) {
  // When indices are reported, the note will use the element type that's the
  // result type of the subscript operator.
  char *cp = (char*)TenElements;
  char a = cp[arg];
  // expected-note@-1 {{Assuming index is non-negative and less than 40, the number of 'char' elements in 'TenElements'}}
  char b = cp[arg]; // no additional note, we already assumed that 'arg' is in bounds
  char c = cp[arg + 40];
  // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
  // expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 40 'char' elements}}
  return a + b + c;
}

struct foo {
  int num;
  char a[8];
  char b[5];
};

int assumingConvertedToIntP(struct foo f, int arg) {
  // When indices are reported, the note will use the element type that's the
  // result type of the subscript operator.
  int a = ((int*)(f.a))[arg];
  // expected-note@-1 {{Assuming index is non-negative and less than 2, the number of 'int' elements in 'f.a'}}
  // However, if the extent of the memory region is not divisible by the
  // element size, the checker measures the offset and extent in bytes.
  int b = ((int*)(f.b))[arg];
  // expected-note@-1 {{Assuming byte offset is less than 5, the extent of 'f.b'}}
  int c = TenElements[arg-2];
  // expected-warning@-1 {{Out of bound access to memory preceding 'TenElements'}}
  // expected-note@-2 {{Access of 'TenElements' at negative byte offset}}
  return a + b + c;
}

int assumingPlainOffset(struct foo f, int arg) {
  // This TC is intended to check the corner case that the checker prints the
  // shorter "offset" instead of "byte offset" when it's irrelevant that the
  // offset is measured in bytes.

  // expected-note@+2 {{Assuming 'arg' is < 2}}
  // expected-note@+1 {{Taking false branch}}
  if (arg >= 2)
    return 0;

  int b = ((int*)(f.b))[arg];
  // expected-note@-1 {{Assuming byte offset is non-negative and less than 5, the extent of 'f.b'}}
  // FIXME: this should be {{Assuming offset is non-negative}}
  // but the current simplification algorithm doesn't realize that arg <= 1
  // implies that the byte offset arg*4 will be less than 5.

  int c = TenElements[arg+10];
  // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
  // expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
  return b + c;
}

typedef __typeof(sizeof(int)) size_t;
void *malloc(size_t size);
void free(void *ptr);

int assumingExtent(int arg) {
  // Verify that the assumption note is printed when the extent is interesting
  // (even if the index isn't interesting).
  int *mem = (int*)malloc(arg);

  mem[12] = 123;
  // expected-note@-1 {{Assuming index '12' is less than the number of 'int' elements in the heap area}}

  free(mem);

  return TenElements[arg];
  // expected-warning@-1 {{Out of bound access to memory after the end of 'TenElements'}}
  // expected-note@-2 {{Access of 'TenElements' at an overflowing index, while it holds only 10 'int' elements}}
}

int *extentInterestingness(int arg) {
  // Verify that in an out-of-bounds access issue the extent is marked as
  // interesting (so assumptions about its value are printed).
  int *mem = (int*)malloc(arg);

  TenElements[arg] = 123;
  // expected-note@-1 {{Assuming index is non-negative and less than 10, the number of 'int' elements in 'TenElements'}}

  return &mem[12];
  // expected-warning@-1 {{Out of bound access to memory after the end of the heap area}}
  // expected-note@-2 {{Access of 'int' element in the heap area at index 12}}
}

int triggeredByAnyReport(int arg) {
  // Verify that note tags explaining the assumptions made by ArrayBound are
  // not limited to ArrayBound reports but will appear on any bug report (that
  // marks the relevant symbol as interesting).
  TenElements[arg + 10] = 8;
  // expected-note@-1 {{Assuming index is non-negative and less than 10, the number of 'int' elements in 'TenElements'}}
  return 1024 >> arg;
  // expected-warning@-1 {{Right operand is negative in right shift}}
  // expected-note@-2 {{The result of right shift is undefined because the right operand is negative}}
}