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