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