xref: /llvm-project/clang/test/Analysis/std-c-library-functions-arg-constraints.c (revision c202a17d024068c70364116f2d06535d79535b30)
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=unix.StdCLibraryFunctions \
5 // RUN:   -analyzer-config unix.StdCLibraryFunctions:ModelPOSIX=true \
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=unix.StdCLibraryFunctions \
15 // RUN:   -analyzer-config unix.StdCLibraryFunctions:ModelPOSIX=true \
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 #include "Inputs/std-c-library-functions-POSIX.h"
23 
24 void clang_analyzer_eval(int);
25 void clang_analyzer_warnIfReached();
26 
27 int glob;
28 
test_alnum_concrete(int v)29 void test_alnum_concrete(int v) {
30   int ret = isalnum(256); // \
31   // report-warning{{The 1st argument to 'isalnum' is 256 but should be an unsigned char value or EOF}} \
32   // bugpath-warning{{The 1st argument to 'isalnum' is 256 but should be an unsigned char value or EOF}} \
33   // bugpath-note{{The 1st argument to 'isalnum' is 256 but should be an unsigned char value or EOF}}
34   (void)ret;
35 }
36 
test_alnum_symbolic(int x)37 void test_alnum_symbolic(int x) {
38   int ret = isalnum(x);
39   (void)ret;
40 
41   clang_analyzer_eval(EOF <= x && x <= 255); // \
42   // report-warning{{TRUE}} \
43   // bugpath-warning{{TRUE}} \
44   // bugpath-note{{TRUE}} \
45   // bugpath-note{{Left side of '&&' is true}} \
46   // bugpath-note{{'x' is <= 255}}
47 }
48 
test_alnum_symbolic2(int x)49 void test_alnum_symbolic2(int x) {
50   if (x > 255) { // \
51     // bugpath-note{{Assuming 'x' is > 255}} \
52     // bugpath-note{{Taking true branch}}
53 
54     int ret = isalnum(x); // \
55     // report-warning{{The 1st argument to 'isalnum' is >= 256 but should be an unsigned char value or EOF}} \
56     // bugpath-warning{{The 1st argument to 'isalnum' is >= 256 but should be an unsigned char value or EOF}} \
57     // bugpath-note{{The 1st argument to 'isalnum' is >= 256 but should be an unsigned char value or EOF}}
58 
59     (void)ret;
60   }
61 }
62 
test_toupper_concrete(int v)63 void test_toupper_concrete(int v) {
64   int ret = toupper(256); // \
65   // report-warning{{The 1st argument to 'toupper' is 256 but should be an unsigned char value or EOF}} \
66   // bugpath-warning{{The 1st argument to 'toupper' is 256 but should be an unsigned char value or EOF}} \
67   // bugpath-note{{The 1st argument to 'toupper' is 256 but should be an unsigned char value or EOF}}
68   (void)ret;
69 }
70 
test_toupper_symbolic(int x)71 void test_toupper_symbolic(int x) {
72   int ret = toupper(x);
73   (void)ret;
74 
75   clang_analyzer_eval(EOF <= x && x <= 255); // \
76   // report-warning{{TRUE}} \
77   // bugpath-warning{{TRUE}} \
78   // bugpath-note{{TRUE}} \
79   // bugpath-note{{Left side of '&&' is true}} \
80   // bugpath-note{{'x' is <= 255}}
81 }
82 
test_toupper_symbolic2(int x)83 void test_toupper_symbolic2(int x) {
84   if (x > 255) { // \
85     // bugpath-note{{Assuming 'x' is > 255}} \
86     // bugpath-note{{Taking true branch}}
87 
88     int ret = toupper(x); // \
89     // report-warning{{The 1st argument to 'toupper' is >= 256 but should be an unsigned char value or EOF}} \
90     // bugpath-warning{{The 1st argument to 'toupper' is >= 256 but should be an unsigned char value or EOF}} \
91     // bugpath-note{{The 1st argument to 'toupper' is >= 256 but should be an unsigned char value or EOF}}
92 
93     (void)ret;
94   }
95 }
96 
test_tolower_concrete(int v)97 void test_tolower_concrete(int v) {
98   int ret = tolower(256); // \
99   // report-warning{{The 1st argument to 'tolower' is 256 but should be an unsigned char value or EOF}} \
100   // bugpath-warning{{The 1st argument to 'tolower' is 256 but should be an unsigned char value or EOF}} \
101   // bugpath-note{{The 1st argument to 'tolower' is 256 but should be an unsigned char value or EOF}}
102   (void)ret;
103 }
104 
test_tolower_symbolic(int x)105 void test_tolower_symbolic(int x) {
106   int ret = tolower(x);
107   (void)ret;
108 
109   clang_analyzer_eval(EOF <= x && x <= 255); // \
110   // report-warning{{TRUE}} \
111   // bugpath-warning{{TRUE}} \
112   // bugpath-note{{TRUE}} \
113   // bugpath-note{{Left side of '&&' is true}} \
114   // bugpath-note{{'x' is <= 255}}
115 }
116 
test_tolower_symbolic2(int x)117 void test_tolower_symbolic2(int x) {
118   if (x > 255) { // \
119     // bugpath-note{{Assuming 'x' is > 255}} \
120     // bugpath-note{{Taking true branch}}
121 
122     int ret = tolower(x); // \
123     // report-warning{{The 1st argument to 'tolower' is >= 256 but should be an unsigned char value or EOF}} \
124     // bugpath-warning{{The 1st argument to 'tolower' is >= 256 but should be an unsigned char value or EOF}} \
125     // bugpath-note{{The 1st argument to 'tolower' is >= 256 but should be an unsigned char value or EOF}}
126 
127     (void)ret;
128   }
129 }
130 
test_toascii_concrete(int v)131 void test_toascii_concrete(int v) {
132   int ret = toascii(256); // \
133   // report-warning{{The 1st argument to 'toascii' is 256 but should be an unsigned char value or EOF}} \
134   // bugpath-warning{{The 1st argument to 'toascii' is 256 but should be an unsigned char value or EOF}} \
135   // bugpath-note{{The 1st argument to 'toascii' is 256 but should be an unsigned char value or EOF}}
136   (void)ret;
137 }
138 
test_toascii_symbolic(int x)139 void test_toascii_symbolic(int x) {
140   int ret = toascii(x);
141   (void)ret;
142 
143   clang_analyzer_eval(EOF <= x && x <= 255); // \
144   // report-warning{{TRUE}} \
145   // bugpath-warning{{TRUE}} \
146   // bugpath-note{{TRUE}} \
147   // bugpath-note{{Left side of '&&' is true}} \
148   // bugpath-note{{'x' is <= 255}}
149 }
150 
test_toascii_symbolic2(int x)151 void test_toascii_symbolic2(int x) {
152   if (x > 255) { // \
153     // bugpath-note{{Assuming 'x' is > 255}} \
154     // bugpath-note{{Taking true branch}}
155 
156     int ret = toascii(x); // \
157     // report-warning{{The 1st argument to 'toascii' is >= 256 but should be an unsigned char value or EOF}} \
158     // bugpath-warning{{The 1st argument to 'toascii' is >= 256 but should be an unsigned char value or EOF}} \
159     // bugpath-note{{The 1st argument to 'toascii' is >= 256 but should be an unsigned char value or EOF}}
160 
161     (void)ret;
162   }
163 }
164 
test_notnull_concrete(FILE * fp)165 void test_notnull_concrete(FILE *fp) {
166   fread(0, sizeof(int), 10, fp); // \
167   // report-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \
168   // bugpath-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \
169   // bugpath-note{{The 1st argument to 'fread' is NULL but should not be NULL}}
170 }
test_notnull_symbolic(FILE * fp,int * buf)171 void test_notnull_symbolic(FILE *fp, int *buf) {
172   fread(buf, sizeof(int), 10, fp);
173   clang_analyzer_eval(buf != 0); // \
174   // report-warning{{TRUE}} \
175   // bugpath-warning{{TRUE}} \
176   // bugpath-note{{TRUE}} \
177   // bugpath-note{{'buf' is not equal to null}}
178 }
test_notnull_symbolic2(FILE * fp,int * buf)179 void test_notnull_symbolic2(FILE *fp, int *buf) {
180   if (!buf)                          // bugpath-note{{Assuming 'buf' is null}} \
181             // bugpath-note{{Taking true branch}}
182     fread(buf, sizeof(int), 10, fp); // \
183     // report-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \
184     // bugpath-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \
185     // bugpath-note{{The 1st argument to 'fread' is NULL but should not be NULL}}
186 }
187 
188 int __not_null_buffer(void *, int, int);
189 
test_notnull_buffer_1(void * buf)190 void test_notnull_buffer_1(void *buf) {
191   __not_null_buffer(buf, 0, 1);
192   clang_analyzer_eval(buf != 0); // \
193   // report-warning{{TRUE}} \
194   // bugpath-warning{{TRUE}} \
195   // report-warning{{FALSE}} \
196   // bugpath-warning{{FALSE}} \
197   // bugpath-note{{TRUE}} \
198   // bugpath-note{{FALSE}} \
199   // bugpath-note{{Assuming 'buf' is equal to null}} \
200   // bugpath-note{{Assuming 'buf' is not equal to null}}
201 }
202 
test_notnull_buffer_2(void * buf)203 void test_notnull_buffer_2(void *buf) {
204   __not_null_buffer(buf, 1, 0);
205   clang_analyzer_eval(buf != 0); // \
206   // report-warning{{TRUE}} \
207   // bugpath-warning{{TRUE}} \
208   // report-warning{{FALSE}} \
209   // bugpath-warning{{FALSE}} \
210   // bugpath-note{{TRUE}} \
211   // bugpath-note{{FALSE}} \
212   // bugpath-note{{Assuming 'buf' is equal to null}} \
213   // bugpath-note{{Assuming 'buf' is not equal to null}}
214 }
215 
test_notnull_buffer_3(void * buf)216 void test_notnull_buffer_3(void *buf) {
217   __not_null_buffer(buf, 1, 1);
218   clang_analyzer_eval(buf != 0); // \
219   // report-warning{{TRUE}} \
220   // bugpath-warning{{TRUE}} \
221   // bugpath-note{{TRUE}} \
222   // bugpath-note{{'buf' is not equal to null}}
223 }
224 
test_no_node_after_bug(FILE * fp,size_t size,size_t n,void * buf)225 void test_no_node_after_bug(FILE *fp, size_t size, size_t n, void *buf) {
226   if (fp) // \
227   // bugpath-note{{Assuming 'fp' is null}} \
228   // bugpath-note{{Taking false branch}}
229     return;
230   size_t ret = fread(buf, size, n, fp); // \
231   // report-warning{{The 4th argument to 'fread' is NULL but should not be NULL}} \
232   // bugpath-warning{{The 4th argument to 'fread' is NULL but should not be NULL}} \
233   // bugpath-note{{The 4th argument to 'fread' is NULL but should not be NULL}}
234   clang_analyzer_warnIfReached(); // not reachable
235 }
236 
237 // This is one test case for the ARR38-C SEI-CERT rule.
ARR38_C_F(FILE * file)238 void ARR38_C_F(FILE *file) {
239   enum { BUFFER_SIZE = 1024 };
240   wchar_t wbuf[BUFFER_SIZE]; // bugpath-note{{'wbuf' initialized here}}
241 
242   const size_t size = sizeof(*wbuf);   // bugpath-note{{'size' initialized to}}
243   const size_t nitems = sizeof(wbuf);  // bugpath-note{{'nitems' initialized to}}
244 
245   // The 3rd parameter should be the number of elements to read, not
246   // the size in bytes.
247   fread(wbuf, size, nitems, file); // \
248   // report-warning{{The 1st argument to 'fread' is a buffer with size 4096 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 4096)}} \
249   // bugpath-warning{{The 1st argument to 'fread' is a buffer with size 4096 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 4096)}} \
250   // bugpath-note{{The 1st argument to 'fread' is a buffer with size 4096 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 4096)}}
251 }
252 
253 int __two_constrained_args(int, int);
test_constraints_on_multiple_args(int x,int y)254 void test_constraints_on_multiple_args(int x, int y) {
255   // State split should not happen here. I.e. x == 1 should not be evaluated
256   // FALSE.
257   __two_constrained_args(x, y);
258   //NOTE! Because of the second `clang_analyzer_eval` call we have two bug
259   clang_analyzer_eval(x == 1); // \
260   // report-warning{{TRUE}} \
261   // bugpath-warning{{TRUE}} \
262   // bugpath-note{{TRUE}}
263   clang_analyzer_eval(y == 1); // \
264   // report-warning{{TRUE}} \
265   // bugpath-warning{{TRUE}} \
266   // bugpath-note{{TRUE}}
267 }
268 
269 int __arg_constrained_twice(int);
test_multiple_constraints_on_same_arg(int x)270 void test_multiple_constraints_on_same_arg(int x) {
271   __arg_constrained_twice(x);
272   clang_analyzer_eval(x < 1 || x > 2); // \
273   // report-warning{{TRUE}} \
274   // bugpath-warning{{TRUE}} \
275   // bugpath-note{{TRUE}} \
276   // bugpath-note{{Assuming 'x' is < 1}} \
277   // bugpath-note{{Left side of '||' is true}}
278 }
279 
280 int __variadic(void *stream, const char *format, ...);
test_arg_constraint_on_variadic_fun(void)281 void test_arg_constraint_on_variadic_fun(void) {
282   __variadic(0, "%d%d", 1, 2); // \
283   // report-warning{{The 1st argument to '__variadic' is NULL but should not be NULL}} \
284   // bugpath-warning{{The 1st argument to '__variadic' is NULL but should not be NULL}} \
285   // bugpath-note{{The 1st argument to '__variadic' is NULL but should not be NULL}}
286 }
287 
288 int __buf_size_arg_constraint(const void *, size_t);
test_buf_size_concrete(void)289 void test_buf_size_concrete(void) {
290   char buf[3];                       // bugpath-note{{'buf' initialized here}}
291   __buf_size_arg_constraint(buf, 4); // \
292   // report-warning{{The 1st argument to '__buf_size_arg_constraint' is a buffer with size 3 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4)}} \
293   // bugpath-warning{{The 1st argument to '__buf_size_arg_constraint' is a buffer with size 3 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4)}} \
294   // bugpath-note{{The 1st argument to '__buf_size_arg_constraint' is a buffer with size 3 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4)}}
295 }
test_buf_size_symbolic(int s)296 void test_buf_size_symbolic(int s) {
297   char buf[3];
298   __buf_size_arg_constraint(buf, s);
299   clang_analyzer_eval(s <= 3); // \
300   // report-warning{{TRUE}} \
301   // bugpath-warning{{TRUE}} \
302   // bugpath-note{{TRUE}} \
303   // bugpath-note{{'s' is <= 3}}
304 }
test_buf_size_symbolic_and_offset(int s)305 void test_buf_size_symbolic_and_offset(int s) {
306   char buf[3];
307   __buf_size_arg_constraint(buf + 1, s);
308   clang_analyzer_eval(s <= 2); // \
309   // report-warning{{TRUE}} \
310   // bugpath-warning{{TRUE}} \
311   // bugpath-note{{TRUE}} \
312   // bugpath-note{{'s' is <= 2}}
313 }
314 
315 int __buf_size_arg_constraint_mul(const void *, size_t, size_t);
test_buf_size_concrete_with_multiplication(void)316 void test_buf_size_concrete_with_multiplication(void) {
317   short buf[3];                                         // bugpath-note{{'buf' initialized here}}
318   __buf_size_arg_constraint_mul(buf, 4, sizeof(short)); // \
319   // report-warning{{The 1st argument to '__buf_size_arg_constraint_mul' is a buffer with size 6 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 2)}} \
320   // bugpath-warning{{The 1st argument to '__buf_size_arg_constraint_mul' is a buffer with size 6 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 2)}} \
321   // bugpath-note{{The 1st argument to '__buf_size_arg_constraint_mul' is a buffer with size 6 but should be a buffer with size equal to or greater than the value of the 2nd argument (which is 4) times the 3rd argument (which is 2)}}
322 }
test_buf_size_symbolic_with_multiplication(size_t s)323 void test_buf_size_symbolic_with_multiplication(size_t s) {
324   short buf[3];
325   __buf_size_arg_constraint_mul(buf, s, sizeof(short));
326   clang_analyzer_eval(s * sizeof(short) <= 6); // \
327   // report-warning{{TRUE}} \
328   // bugpath-warning{{TRUE}} \
329   // bugpath-note{{TRUE}}
330 }
test_buf_size_symbolic_and_offset_with_multiplication(size_t s)331 void test_buf_size_symbolic_and_offset_with_multiplication(size_t s) {
332   short buf[3];
333   __buf_size_arg_constraint_mul(buf + 1, s, sizeof(short));
334   clang_analyzer_eval(s * sizeof(short) <= 4); // \
335   // report-warning{{TRUE}} \
336   // bugpath-warning{{TRUE}} \
337   // bugpath-note{{TRUE}}
338 }
339 
340 // The minimum buffer size for this function is set to 10.
341 int __buf_size_arg_constraint_concrete(const void *);
test_min_buf_size(void)342 void test_min_buf_size(void) {
343   char buf[9];// bugpath-note{{'buf' initialized here}}
344   __buf_size_arg_constraint_concrete(buf); // \
345   // report-warning{{The 1st argument to '__buf_size_arg_constraint_concrete' is a buffer with size 9 but should be a buffer with size equal to or greater than 10}} \
346   // bugpath-warning{{The 1st argument to '__buf_size_arg_constraint_concrete' is a buffer with size 9 but should be a buffer with size equal to or greater than 10}} \
347   // bugpath-note{{The 1st argument to '__buf_size_arg_constraint_concrete' is a buffer with size 9 but should be a buffer with size equal to or greater than 10}}
348 }
349 
test_file_fd_at_functions()350 void test_file_fd_at_functions() {
351   (void)linkat(-22, "from", AT_FDCWD, "to", 0); // \
352   // report-warning{{The 1st argument to 'linkat' is -22 but should be a valid file descriptor or AT_FDCWD}} \
353   // bugpath-warning{{The 1st argument to 'linkat' is -22 but should be a valid file descriptor or AT_FDCWD}} \
354   // bugpath-note{{The 1st argument to 'linkat' is -22 but should be a valid file descriptor or AT_FDCWD}}
355 
356   // no warning for these functions if the AT_FDCWD value is used
357   (void)openat(AT_FDCWD, "path", 0);
358   (void)linkat(AT_FDCWD, "from", AT_FDCWD, "to", 0);
359   (void)faccessat(AT_FDCWD, "path", 0, 0);
360   (void)symlinkat("oldpath", AT_FDCWD, "newpath");
361   (void)mkdirat(AT_FDCWD, "path", 0);
362   (void)mknodat(AT_FDCWD, "path", 0, 0);
363   (void)fchmodat(AT_FDCWD, "path", 0, 0);
364   (void)fchownat(AT_FDCWD, "path", 0, 0, 0);
365   (void)linkat(AT_FDCWD, "oldpath", AT_FDCWD, "newpath", 0);
366   (void)unlinkat(AT_FDCWD, "newpath", 0);
367   struct stat St;
368   (void)fstatat(AT_FDCWD, "newpath", &St, 0);
369   char Buf[10];
370   (void)readlinkat(AT_FDCWD, "newpath", Buf, 10);
371   (void)renameat(AT_FDCWD, "oldpath", AT_FDCWD, "newpath");
372 }
373