xref: /llvm-project/clang/test/Analysis/std-c-library-functions-arg-constraints.c (revision ddc5d40dd285d6422dc66b9aa25064502af3218b)
1 // Check the basic reporting/warning and the application of constraints.
2 // RUN: %clang_analyze_cc1 %s \
3 // RUN:   -analyzer-checker=core \
4 // RUN:   -analyzer-checker=apiModeling.StdCLibraryFunctions \
5 // RUN:   -analyzer-checker=alpha.unix.StdCLibraryFunctionArgs \
6 // RUN:   -analyzer-checker=debug.StdCLibraryFunctionsTester \
7 // RUN:   -analyzer-checker=debug.ExprInspection \
8 // RUN:   -triple x86_64-unknown-linux-gnu \
9 // RUN:   -verify=report
10 
11 // Check the bugpath related to the reports.
12 // RUN: %clang_analyze_cc1 %s \
13 // RUN:   -analyzer-checker=core \
14 // RUN:   -analyzer-checker=apiModeling.StdCLibraryFunctions \
15 // RUN:   -analyzer-checker=alpha.unix.StdCLibraryFunctionArgs \
16 // RUN:   -analyzer-checker=debug.StdCLibraryFunctionsTester \
17 // RUN:   -analyzer-checker=debug.ExprInspection \
18 // RUN:   -triple x86_64-unknown-linux-gnu \
19 // RUN:   -analyzer-output=text \
20 // RUN:   -verify=bugpath
21 
22 void clang_analyzer_eval(int);
23 void clang_analyzer_warnIfReached();
24 
25 int glob;
26 
27 #define EOF -1
28 
29 int isalnum(int);
30 
31 void test_alnum_concrete(int v) {
32   int ret = isalnum(256); // \
33   // report-warning{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}} \
34   // bugpath-warning{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}} \
35   // bugpath-note{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}}
36   (void)ret;
37 }
38 
39 void test_alnum_symbolic(int x) {
40   int ret = isalnum(x); // \
41   // bugpath-note{{Assuming the character is non-alphanumeric}}
42   (void)ret;
43 
44   clang_analyzer_eval(EOF <= x && x <= 255); // \
45   // report-warning{{TRUE}} \
46   // bugpath-warning{{TRUE}} \
47   // bugpath-note{{TRUE}} \
48   // bugpath-note{{Left side of '&&' is true}} \
49   // bugpath-note{{'x' is <= 255}}
50 }
51 
52 void test_alnum_symbolic2(int x) {
53   if (x > 255) { // \
54     // bugpath-note{{Assuming 'x' is > 255}} \
55     // bugpath-note{{Taking true branch}}
56 
57     int ret = isalnum(x); // \
58     // report-warning{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}} \
59     // bugpath-warning{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}} \
60     // bugpath-note{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}}
61 
62     (void)ret;
63   }
64 }
65 
66 int toupper(int);
67 
68 void test_toupper_concrete(int v) {
69   int ret = toupper(256); // \
70   // report-warning{{The 1st argument to 'toupper' should be an unsigned char value or EOF}} \
71   // bugpath-warning{{The 1st argument to 'toupper' should be an unsigned char value or EOF}} \
72   // bugpath-note{{The 1st argument to 'toupper' should be an unsigned char value or EOF}}
73   (void)ret;
74 }
75 
76 void test_toupper_symbolic(int x) {
77   int ret = toupper(x);
78   (void)ret;
79 
80   clang_analyzer_eval(EOF <= x && x <= 255); // \
81   // report-warning{{TRUE}} \
82   // bugpath-warning{{TRUE}} \
83   // bugpath-note{{TRUE}} \
84   // bugpath-note{{Left side of '&&' is true}} \
85   // bugpath-note{{'x' is <= 255}}
86 }
87 
88 void test_toupper_symbolic2(int x) {
89   if (x > 255) { // \
90     // bugpath-note{{Assuming 'x' is > 255}} \
91     // bugpath-note{{Taking true branch}}
92 
93     int ret = toupper(x); // \
94     // report-warning{{The 1st argument to 'toupper' should be an unsigned char value or EOF}} \
95     // bugpath-warning{{The 1st argument to 'toupper' should be an unsigned char value or EOF}} \
96     // bugpath-note{{The 1st argument to 'toupper' should be an unsigned char value or EOF}}
97 
98     (void)ret;
99   }
100 }
101 
102 int tolower(int);
103 
104 void test_tolower_concrete(int v) {
105   int ret = tolower(256); // \
106   // report-warning{{The 1st argument to 'tolower' should be an unsigned char value or EOF}} \
107   // bugpath-warning{{The 1st argument to 'tolower' should be an unsigned char value or EOF}} \
108   // bugpath-note{{The 1st argument to 'tolower' should be an unsigned char value or EOF}}
109   (void)ret;
110 }
111 
112 void test_tolower_symbolic(int x) {
113   int ret = tolower(x);
114   (void)ret;
115 
116   clang_analyzer_eval(EOF <= x && x <= 255); // \
117   // report-warning{{TRUE}} \
118   // bugpath-warning{{TRUE}} \
119   // bugpath-note{{TRUE}} \
120   // bugpath-note{{Left side of '&&' is true}} \
121   // bugpath-note{{'x' is <= 255}}
122 }
123 
124 void test_tolower_symbolic2(int x) {
125   if (x > 255) { // \
126     // bugpath-note{{Assuming 'x' is > 255}} \
127     // bugpath-note{{Taking true branch}}
128 
129     int ret = tolower(x); // \
130     // report-warning{{The 1st argument to 'tolower' should be an unsigned char value or EOF}} \
131     // bugpath-warning{{The 1st argument to 'tolower' should be an unsigned char value or EOF}} \
132     // bugpath-note{{The 1st argument to 'tolower' should be an unsigned char value or EOF}}
133 
134     (void)ret;
135   }
136 }
137 
138 int toascii(int);
139 
140 void test_toascii_concrete(int v) {
141   int ret = toascii(256); // \
142   // report-warning{{The 1st argument to 'toascii' should be an unsigned char value or EOF}} \
143   // bugpath-warning{{The 1st argument to 'toascii' should be an unsigned char value or EOF}} \
144   // bugpath-note{{The 1st argument to 'toascii' should be an unsigned char value or EOF}}
145   (void)ret;
146 }
147 
148 void test_toascii_symbolic(int x) {
149   int ret = toascii(x);
150   (void)ret;
151 
152   clang_analyzer_eval(EOF <= x && x <= 255); // \
153   // report-warning{{TRUE}} \
154   // bugpath-warning{{TRUE}} \
155   // bugpath-note{{TRUE}} \
156   // bugpath-note{{Left side of '&&' is true}} \
157   // bugpath-note{{'x' is <= 255}}
158 }
159 
160 void test_toascii_symbolic2(int x) {
161   if (x > 255) { // \
162     // bugpath-note{{Assuming 'x' is > 255}} \
163     // bugpath-note{{Taking true branch}}
164 
165     int ret = toascii(x); // \
166     // report-warning{{The 1st argument to 'toascii' should be an unsigned char value or EOF}} \
167     // bugpath-warning{{The 1st argument to 'toascii' should be an unsigned char value or EOF}} \
168     // bugpath-note{{The 1st argument to 'toascii' should be an unsigned char value or EOF}}
169 
170     (void)ret;
171   }
172 }
173 
174 typedef struct FILE FILE;
175 typedef typeof(sizeof(int)) size_t;
176 size_t fread(void *restrict, size_t, size_t, FILE *restrict);
177 void test_notnull_concrete(FILE *fp) {
178   fread(0, sizeof(int), 10, fp); // \
179   // report-warning{{The 1st argument to 'fread' should not be NULL}} \
180   // bugpath-warning{{The 1st argument to 'fread' should not be NULL}} \
181   // bugpath-note{{The 1st argument to 'fread' should not be NULL}}
182 }
183 void test_notnull_symbolic(FILE *fp, int *buf) {
184   fread(buf, sizeof(int), 10, fp);
185   clang_analyzer_eval(buf != 0); // \
186   // report-warning{{TRUE}} \
187   // bugpath-warning{{TRUE}} \
188   // bugpath-note{{TRUE}} \
189   // bugpath-note{{'buf' is not equal to null}}
190 }
191 void test_notnull_symbolic2(FILE *fp, int *buf) {
192   if (!buf)                          // bugpath-note{{Assuming 'buf' is null}} \
193             // bugpath-note{{Taking true branch}}
194     fread(buf, sizeof(int), 10, fp); // \
195     // report-warning{{The 1st argument to 'fread' should not be NULL}} \
196     // bugpath-warning{{The 1st argument to 'fread' should not be NULL}} \
197     // bugpath-note{{The 1st argument to 'fread' should not be NULL}}
198 }
199 void test_no_node_after_bug(FILE *fp, size_t size, size_t n, void *buf) {
200   if (fp) // \
201   // bugpath-note{{Assuming 'fp' is null}} \
202   // bugpath-note{{Taking false branch}}
203     return;
204   size_t ret = fread(buf, size, n, fp); // \
205   // report-warning{{The 4th argument to 'fread' should not be NULL}} \
206   // bugpath-warning{{The 4th argument to 'fread' should not be NULL}} \
207   // bugpath-note{{The 4th argument to 'fread' should not be NULL}}
208   clang_analyzer_warnIfReached(); // not reachable
209 }
210 
211 typedef __WCHAR_TYPE__ wchar_t;
212 // This is one test case for the ARR38-C SEI-CERT rule.
213 void ARR38_C_F(FILE *file) {
214   enum { BUFFER_SIZE = 1024 };
215   wchar_t wbuf[BUFFER_SIZE]; // bugpath-note{{'wbuf' initialized here}}
216 
217   const size_t size = sizeof(*wbuf);   // bugpath-note{{'size' initialized to}}
218   const size_t nitems = sizeof(wbuf);  // bugpath-note{{'nitems' initialized to}}
219 
220   // The 3rd parameter should be the number of elements to read, not
221   // the size in bytes.
222   fread(wbuf, size, nitems, file); // \
223   // report-warning{{The size of the 1st argument to 'fread' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} \
224   // bugpath-warning{{The size of the 1st argument to 'fread' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} \
225   // bugpath-note{{The size of the 1st argument to 'fread' should be equal to or greater than the value of the 2nd argument times the 3rd argument}}
226 }
227 
228 int __two_constrained_args(int, int);
229 void test_constraints_on_multiple_args(int x, int y) {
230   // State split should not happen here. I.e. x == 1 should not be evaluated
231   // FALSE.
232   __two_constrained_args(x, y);
233   //NOTE! Because of the second `clang_analyzer_eval` call we have two bug
234   clang_analyzer_eval(x == 1); // \
235   // report-warning{{TRUE}} \
236   // bugpath-warning{{TRUE}} \
237   // bugpath-note{{TRUE}}
238   clang_analyzer_eval(y == 1); // \
239   // report-warning{{TRUE}} \
240   // bugpath-warning{{TRUE}} \
241   // bugpath-note{{TRUE}}
242 }
243 
244 int __arg_constrained_twice(int);
245 void test_multiple_constraints_on_same_arg(int x) {
246   __arg_constrained_twice(x);
247   clang_analyzer_eval(x < 1 || x > 2); // \
248   // report-warning{{TRUE}} \
249   // bugpath-warning{{TRUE}} \
250   // bugpath-note{{TRUE}} \
251   // bugpath-note{{Assuming 'x' is < 1}} \
252   // bugpath-note{{Left side of '||' is true}}
253 }
254 
255 int __variadic(void *stream, const char *format, ...);
256 void test_arg_constraint_on_variadic_fun(void) {
257   __variadic(0, "%d%d", 1, 2); // \
258   // report-warning{{The 1st argument to '__variadic' should not be NULL}} \
259   // bugpath-warning{{The 1st argument to '__variadic' should not be NULL}} \
260   // bugpath-note{{The 1st argument to '__variadic' should not be NULL}}
261 }
262 
263 int __buf_size_arg_constraint(const void *, size_t);
264 void test_buf_size_concrete(void) {
265   char buf[3];                       // bugpath-note{{'buf' initialized here}}
266   __buf_size_arg_constraint(buf, 4); // \
267   // report-warning{{The size of the 1st argument to '__buf_size_arg_constraint' should be equal to or greater than the value of the 2nd argument}} \
268   // bugpath-warning{{The size of the 1st argument to '__buf_size_arg_constraint' should be equal to or greater than the value of the 2nd argument}} \
269   // bugpath-note{{The size of the 1st argument to '__buf_size_arg_constraint' should be equal to or greater than the value of the 2nd argument}}
270 }
271 void test_buf_size_symbolic(int s) {
272   char buf[3];
273   __buf_size_arg_constraint(buf, s);
274   clang_analyzer_eval(s <= 3); // \
275   // report-warning{{TRUE}} \
276   // bugpath-warning{{TRUE}} \
277   // bugpath-note{{TRUE}} \
278   // bugpath-note{{'s' is <= 3}}
279 }
280 void test_buf_size_symbolic_and_offset(int s) {
281   char buf[3];
282   __buf_size_arg_constraint(buf + 1, s);
283   clang_analyzer_eval(s <= 2); // \
284   // report-warning{{TRUE}} \
285   // bugpath-warning{{TRUE}} \
286   // bugpath-note{{TRUE}} \
287   // bugpath-note{{'s' is <= 2}}
288 }
289 
290 int __buf_size_arg_constraint_mul(const void *, size_t, size_t);
291 void test_buf_size_concrete_with_multiplication(void) {
292   short buf[3];                                         // bugpath-note{{'buf' initialized here}}
293   __buf_size_arg_constraint_mul(buf, 4, sizeof(short)); // \
294   // report-warning{{The size of the 1st argument to '__buf_size_arg_constraint_mul' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} \
295   // bugpath-warning{{The size of the 1st argument to '__buf_size_arg_constraint_mul' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} \
296   // bugpath-note{{The size of the 1st argument to '__buf_size_arg_constraint_mul' should be equal to or greater than the value of the 2nd argument times the 3rd argument}}
297 }
298 void test_buf_size_symbolic_with_multiplication(size_t s) {
299   short buf[3];
300   __buf_size_arg_constraint_mul(buf, s, sizeof(short));
301   clang_analyzer_eval(s * sizeof(short) <= 6); // \
302   // report-warning{{TRUE}} \
303   // bugpath-warning{{TRUE}} \
304   // bugpath-note{{TRUE}}
305 }
306 void test_buf_size_symbolic_and_offset_with_multiplication(size_t s) {
307   short buf[3];
308   __buf_size_arg_constraint_mul(buf + 1, s, sizeof(short));
309   clang_analyzer_eval(s * sizeof(short) <= 4); // \
310   // report-warning{{TRUE}} \
311   // bugpath-warning{{TRUE}} \
312   // bugpath-note{{TRUE}}
313 }
314 
315 // The minimum buffer size for this function is set to 10.
316 int __buf_size_arg_constraint_concrete(const void *);
317 void test_min_buf_size(void) {
318   char buf[9];// bugpath-note{{'buf' initialized here}}
319   __buf_size_arg_constraint_concrete(buf); // \
320   // report-warning{{The size of the 1st argument to '__buf_size_arg_constraint_concrete' should be equal to or greater than 10}} \
321   // bugpath-warning{{The size of the 1st argument to '__buf_size_arg_constraint_concrete' should be equal to or greater than 10}} \
322   // bugpath-note{{The size of the 1st argument to '__buf_size_arg_constraint_concrete' should be equal to or greater than 10}}
323 }
324