xref: /llvm-project/clang/test/Analysis/stream.c (revision 2e3c7dbbcbfa37ae83251bb3da388df772680689)
1 // RUN: %clang_analyze_cc1 -triple=x86_64-pc-linux-gnu -analyzer-checker=core,unix.Stream,debug.ExprInspection \
2 // RUN:   -analyzer-config eagerly-assume=false,unix.Stream:Pedantic=true -verify %s
3 // RUN: %clang_analyze_cc1 -triple=armv8-none-linux-eabi -analyzer-checker=core,unix.Stream,debug.ExprInspection \
4 // RUN:   -analyzer-config eagerly-assume=false,unix.Stream:Pedantic=true -verify %s
5 // RUN: %clang_analyze_cc1 -triple=aarch64-linux-gnu -analyzer-checker=core,unix.Stream,debug.ExprInspection \
6 // RUN:   -analyzer-config eagerly-assume=false,unix.Stream:Pedantic=true -verify %s
7 // RUN: %clang_analyze_cc1 -triple=hexagon -analyzer-checker=core,unix.Stream,debug.ExprInspection \
8 // RUN:   -analyzer-config eagerly-assume=false,unix.Stream:Pedantic=true -verify %s
9 
10 #include "Inputs/system-header-simulator.h"
11 #include "Inputs/system-header-simulator-for-malloc.h"
12 #include "Inputs/system-header-simulator-for-valist.h"
13 
14 void clang_analyzer_eval(int);
15 
16 void check_fread(void) {
17   FILE *fp = tmpfile();
18   fread(0, 0, 0, fp); // expected-warning {{Stream pointer might be NULL}}
19   fclose(fp);
20 }
21 
22 void check_fwrite(void) {
23   FILE *fp = tmpfile();
24   fwrite(0, 0, 0, fp); // expected-warning {{Stream pointer might be NULL}}
25   fclose(fp);
26 }
27 
28 void check_fgetc(void) {
29   FILE *fp = tmpfile();
30   fgetc(fp); // expected-warning {{Stream pointer might be NULL}}
31   fclose(fp);
32 }
33 
34 void check_fgets(void) {
35   FILE *fp = tmpfile();
36   char buf[256];
37   fgets(buf, sizeof(buf), fp); // expected-warning {{Stream pointer might be NULL}}
38   fclose(fp);
39 }
40 
41 void check_fputc(void) {
42   FILE *fp = tmpfile();
43   fputc('A', fp); // expected-warning {{Stream pointer might be NULL}}
44   fclose(fp);
45 }
46 
47 void check_fputs(void) {
48   FILE *fp = tmpfile();
49   fputs("ABC", fp); // expected-warning {{Stream pointer might be NULL}}
50   fclose(fp);
51 }
52 
53 void check_fprintf(void) {
54   FILE *fp = tmpfile();
55   fprintf(fp, "ABC"); // expected-warning {{Stream pointer might be NULL}}
56   fclose(fp);
57 }
58 
59 void check_fscanf(void) {
60   FILE *fp = tmpfile();
61   fscanf(fp, "ABC"); // expected-warning {{Stream pointer might be NULL}}
62   fclose(fp);
63 }
64 
65 void check_ungetc(void) {
66   FILE *fp = tmpfile();
67   ungetc('A', fp); // expected-warning {{Stream pointer might be NULL}}
68   fclose(fp);
69 }
70 
71 void check_fseek(void) {
72   FILE *fp = tmpfile();
73   fseek(fp, 0, 0); // expected-warning {{Stream pointer might be NULL}}
74   fclose(fp);
75 }
76 
77 void check_fseeko(void) {
78   FILE *fp = tmpfile();
79   fseeko(fp, 0, 0); // expected-warning {{Stream pointer might be NULL}}
80   fclose(fp);
81 }
82 
83 void check_ftell(void) {
84   FILE *fp = tmpfile();
85   ftell(fp); // expected-warning {{Stream pointer might be NULL}}
86   fclose(fp);
87 }
88 
89 void check_ftello(void) {
90   FILE *fp = tmpfile();
91   ftello(fp); // expected-warning {{Stream pointer might be NULL}}
92   fclose(fp);
93 }
94 
95 void check_rewind(void) {
96   FILE *fp = tmpfile();
97   rewind(fp); // expected-warning {{Stream pointer might be NULL}}
98   fclose(fp);
99 }
100 
101 void check_fgetpos(void) {
102   FILE *fp = tmpfile();
103   fpos_t pos;
104   fgetpos(fp, &pos); // expected-warning {{Stream pointer might be NULL}}
105   fclose(fp);
106 }
107 
108 void check_fsetpos(void) {
109   FILE *fp = tmpfile();
110   fpos_t pos;
111   fsetpos(fp, &pos); // expected-warning {{Stream pointer might be NULL}}
112   fclose(fp);
113 }
114 
115 void check_clearerr(void) {
116   FILE *fp = tmpfile();
117   clearerr(fp); // expected-warning {{Stream pointer might be NULL}}
118   fclose(fp);
119 }
120 
121 void check_feof(void) {
122   FILE *fp = tmpfile();
123   feof(fp); // expected-warning {{Stream pointer might be NULL}}
124   fclose(fp);
125 }
126 
127 void check_ferror(void) {
128   FILE *fp = tmpfile();
129   ferror(fp); // expected-warning {{Stream pointer might be NULL}}
130   fclose(fp);
131 }
132 
133 void check_fileno(void) {
134   FILE *fp = tmpfile();
135   fileno(fp); // expected-warning {{Stream pointer might be NULL}}
136   fclose(fp);
137 }
138 
139 void f_open(void) {
140   FILE *p = fopen("foo", "r");
141   char buf[1024];
142   fread(buf, 1, 1, p); // expected-warning {{Stream pointer might be NULL}}
143   fclose(p);
144 }
145 
146 void f_dopen(int fd) {
147   FILE *F = fdopen(fd, "r");
148   char buf[1024];
149   fread(buf, 1, 1, F); // expected-warning {{Stream pointer might be NULL}}
150   fclose(F);
151 }
152 
153 void f_vfprintf(int fd, va_list args) {
154   FILE *F = fdopen(fd, "r");
155   vfprintf(F, "%d", args); // expected-warning {{Stream pointer might be NULL}}
156   fclose(F);
157 }
158 
159 void f_vfscanf(int fd, va_list args) {
160   FILE *F = fdopen(fd, "r");
161   vfscanf(F, "%u", args); // expected-warning {{Stream pointer might be NULL}}
162   fclose(F);
163 }
164 
165 void f_seek(void) {
166   FILE *p = fopen("foo", "r");
167   if (!p)
168     return;
169   fseek(p, 1, SEEK_SET); // no-warning
170   fseek(p, 1, 3); // expected-warning {{The whence argument to fseek() should be SEEK_SET, SEEK_END, or SEEK_CUR}}
171   fclose(p);
172 }
173 
174 void f_seeko(void) {
175   FILE *p = fopen("foo", "r");
176   if (!p)
177     return;
178   fseeko(p, 1, SEEK_SET); // no-warning
179   fseeko(p, 1, 3); // expected-warning {{The whence argument to fseek() should be SEEK_SET, SEEK_END, or SEEK_CUR}}
180   fclose(p);
181 }
182 
183 void f_double_close(void) {
184   FILE *p = fopen("foo", "r");
185   if (!p)
186     return;
187   fclose(p);
188   fclose(p); // expected-warning {{Use of a stream that might be already closed}}
189 }
190 
191 void f_double_close_alias(void) {
192   FILE *p1 = fopen("foo", "r");
193   if (!p1)
194     return;
195   FILE *p2 = p1;
196   fclose(p1);
197   fclose(p2); // expected-warning {{Use of a stream that might be already closed}}
198 }
199 
200 void f_use_after_close(void) {
201   FILE *p = fopen("foo", "r");
202   if (!p)
203     return;
204   fclose(p);
205   clearerr(p); // expected-warning {{Use of a stream that might be already closed}}
206 }
207 
208 void f_open_after_close(void) {
209   FILE *p = fopen("foo", "r");
210   if (!p)
211     return;
212   fclose(p);
213   p = fopen("foo", "r");
214   if (!p)
215     return;
216   fclose(p);
217 }
218 
219 void f_reopen_after_close(void) {
220   FILE *p = fopen("foo", "r");
221   if (!p)
222     return;
223   fclose(p);
224   // Allow reopen after close.
225   p = freopen("foo", "w", p);
226   if (!p)
227     return;
228   fclose(p);
229 }
230 
231 void f_leak(int c) {
232   FILE *p = fopen("foo.c", "r");
233   if (!p)
234     return;
235   if(c)
236     return; // expected-warning {{Opened stream never closed. Potential resource leak}}
237   fclose(p);
238 }
239 
240 FILE *f_null_checked(void) {
241   FILE *p = fopen("foo.c", "r");
242   if (p)
243     return p; // no-warning
244   else
245     return 0;
246 }
247 
248 void pr7831(FILE *fp) {
249   fclose(fp); // no-warning
250 }
251 
252 // PR 8081 - null pointer crash when 'whence' is not an integer constant
253 void pr8081(FILE *stream, long offset, int whence) {
254   fseek(stream, offset, whence);
255 }
256 
257 void check_freopen_1(void) {
258   FILE *f1 = freopen("foo.c", "r", (FILE *)0); // expected-warning {{Stream pointer might be NULL}}
259   f1 = freopen(0, "w", (FILE *)0x123456);      // Do not report this as error.
260 }
261 
262 void check_freopen_2(void) {
263   FILE *f1 = fopen("foo.c", "r");
264   if (f1) {
265     FILE *f2 = freopen(0, "w", f1);
266     if (f2) {
267       // Check if f1 and f2 point to the same stream.
268       fclose(f1);
269       fclose(f2); // expected-warning {{Use of a stream that might be already closed}}
270     } else {
271       // Reopen failed.
272       // f1 is non-NULL but points to a possibly invalid stream.
273       rewind(f1); // expected-warning {{Stream might be invalid}}
274       // f2 is NULL but the previous error stops the checker.
275       rewind(f2);
276     }
277   }
278 }
279 
280 void check_freopen_3(void) {
281   FILE *f1 = fopen("foo.c", "r");
282   if (f1) {
283     // Unchecked result of freopen.
284     // The f1 may be invalid after this call.
285     freopen(0, "w", f1);
286     rewind(f1); // expected-warning {{Stream might be invalid}}
287     fclose(f1);
288   }
289 }
290 
291 extern FILE *GlobalF;
292 extern void takeFile(FILE *);
293 
294 void check_escape1(void) {
295   FILE *F = tmpfile();
296   if (!F)
297     return;
298   fwrite("1", 1, 1, F); // may fail
299   GlobalF = F;
300   fwrite("1", 1, 1, F); // no warning
301 }
302 
303 void check_escape2(void) {
304   FILE *F = tmpfile();
305   if (!F)
306     return;
307   fwrite("1", 1, 1, F); // may fail
308   takeFile(F);
309   fwrite("1", 1, 1, F); // no warning
310 }
311 
312 void check_escape3(void) {
313   FILE *F = tmpfile();
314   if (!F)
315     return;
316   takeFile(F);
317   F = freopen(0, "w", F);
318   if (!F)
319     return;
320   fwrite("1", 1, 1, F); // may fail
321   fwrite("1", 1, 1, F); // no warning
322 }
323 
324 void check_escape4(void) {
325   FILE *F = tmpfile();
326   if (!F)
327     return;
328   fwrite("1", 1, 1, F); // may fail
329 
330   // no escape at a non-StreamChecker-handled system call
331   setbuf(F, "0");
332 
333   fwrite("1", 1, 1, F); // expected-warning {{might be 'indeterminate'}}
334   fclose(F);
335 }
336 
337 int Test;
338 _Noreturn void handle_error(void);
339 
340 void check_leak_noreturn_1(void) {
341   FILE *F1 = tmpfile();
342   if (!F1)
343     return;
344   if (Test == 1) {
345     handle_error(); // no warning
346   }
347   rewind(F1);
348 } // expected-warning {{Opened stream never closed. Potential resource leak}}
349 
350 // Check that "location uniqueing" works.
351 // This results in reporting only one occurence of resource leak for a stream.
352 void check_leak_noreturn_2(void) {
353   FILE *F1 = tmpfile();
354   if (!F1)
355     return;
356   if (Test == 1) {
357     return; // no warning
358   }
359   rewind(F1);
360 } // expected-warning {{Opened stream never closed. Potential resource leak}}
361 // FIXME: This warning should be placed at the `return` above.
362 // See https://reviews.llvm.org/D83120 about details.
363 
364 void fflush_after_fclose(void) {
365   FILE *F = tmpfile();
366   int Ret;
367   fflush(NULL);                      // no-warning
368   if (!F)
369     return;
370   if ((Ret = fflush(F)) != 0)
371     clang_analyzer_eval(Ret == EOF); // expected-warning {{TRUE}}
372   fclose(F);
373   fflush(F);                         // expected-warning {{Use of a stream that might be already closed}}
374 }
375 
376 void fflush_on_open_failed_stream(void) {
377   FILE *F = tmpfile();
378   if (!F) {
379     fflush(F); // no-warning
380     return;
381   }
382   fclose(F);
383 }
384 
385 void getline_null_file() {
386   char *buffer = NULL;
387   size_t n = 0;
388   getline(&buffer, &n, NULL); // expected-warning {{Stream pointer might be NULL}}
389 }
390 
391 void getdelim_null_file() {
392   char *buffer = NULL;
393   size_t n = 0;
394   getdelim(&buffer, &n, '\n', NULL); // expected-warning {{Stream pointer might be NULL}}
395 }
396 
397 void getline_buffer_on_error() {
398   FILE *file = fopen("file.txt", "r");
399   if (file == NULL) {
400     return;
401   }
402 
403   char *line = NULL;
404   size_t len = 0;
405   if (getline(&line, &len, file) == -1) {
406     if (line[0] == '\0') {} // expected-warning {{The left operand of '==' is a garbage value}}
407   } else {
408     if (line[0] == '\0') {} // no warning
409   }
410 
411   free(line);
412   fclose(file);
413 }
414 
415 void getline_ret_value() {
416   FILE *file = fopen("file.txt", "r");
417   if (file == NULL) {
418     return;
419   }
420 
421   size_t n = 0;
422   char *buffer = NULL;
423   ssize_t r = getline(&buffer, &n, file);
424 
425   if (r > -1) {
426     // The return value does *not* include the terminating null byte.
427     // The buffer must be large enough to include it.
428     clang_analyzer_eval(n > r); // expected-warning{{TRUE}}
429     clang_analyzer_eval(buffer != NULL);  // expected-warning{{TRUE}}
430   }
431 
432   fclose(file);
433   free(buffer);
434 }
435 
436 
437 void getline_buffer_size_negative() {
438   FILE *file = fopen("file.txt", "r");
439   if (file == NULL) {
440     return;
441   }
442 
443   size_t n = -1;
444   clang_analyzer_eval((ssize_t)n >= 0); // expected-warning{{FALSE}}
445   char *buffer = NULL;
446   ssize_t r = getline(&buffer, &n, file);
447 
448   if (r > -1) {
449     clang_analyzer_eval((ssize_t)n > r); // expected-warning{{TRUE}}
450     clang_analyzer_eval(buffer != NULL);  // expected-warning{{TRUE}}
451   }
452 
453   free(buffer);
454   fclose(file);
455 }
456 
457 void gh_93408_regression(void *buffer) {
458   FILE *f = fopen("/tmp/foo.txt", "r");
459   fread(buffer, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} no-crash
460   fclose(f);
461 }
462 
463 typedef void VOID;
464 void gh_93408_regression_typedef(VOID *buffer) {
465   FILE *f = fopen("/tmp/foo.txt", "r");
466   fread(buffer, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} no-crash
467   fclose(f);
468 }
469 
470 struct FAM {
471   int data;
472   int tail[];
473 };
474 
475 struct FAM0 {
476   int data;
477   int tail[0];
478 };
479 
480 void gh_93408_regression_FAM(struct FAM *p) {
481   FILE *f = fopen("/tmp/foo.txt", "r");
482   fread(p->tail, 1, 1, f); // expected-warning {{Stream pointer might be NULL}}
483   fclose(f);
484 }
485 
486 void gh_93408_regression_FAM0(struct FAM0 *p) {
487   FILE *f = fopen("/tmp/foo.txt", "r");
488   fread(p->tail, 1, 1, f); // expected-warning {{Stream pointer might be NULL}}
489   fclose(f);
490 }
491 
492 struct ZeroSized {
493     int data[0];
494 };
495 
496 void gh_93408_regression_ZeroSized(struct ZeroSized *buffer) {
497   FILE *f = fopen("/tmp/foo.txt", "r");
498   fread(buffer, 1, 1, f); // expected-warning {{Stream pointer might be NULL}} no-crash
499   fclose(f);
500 }
501 
502 extern FILE *non_standard_stream_ptr;
503 void test_fopen_does_not_alias_with_standard_streams(void) {
504   FILE *f = fopen("file", "r");
505   if (!f) return;
506   clang_analyzer_eval(f == stdin);  // expected-warning {{FALSE}} no-TRUE
507   clang_analyzer_eval(f == stdout); // expected-warning {{FALSE}} no-TRUE
508   clang_analyzer_eval(f == stderr); // expected-warning {{FALSE}} no-TRUE
509   clang_analyzer_eval(f == non_standard_stream_ptr); // expected-warning {{UNKNOWN}}
510   if (f != stdout) {
511     fclose(f);
512   }
513 } // no-leak: 'fclose()' is always called because 'f' cannot be 'stdout'.
514 
515 void reopen_std_stream(void) {
516   FILE *oldStdout = stdout;
517   fclose(stdout);
518   FILE *fp = fopen("blah", "w");
519   if (!fp) return;
520 
521   stdout = fp; // Let's make them alias.
522   clang_analyzer_eval(fp == oldStdout);     // expected-warning {{UNKNOWN}}
523   clang_analyzer_eval(fp == stdout);        // expected-warning {{TRUE}} no-FALSE
524   clang_analyzer_eval(oldStdout == stdout); // expected-warning {{UNKNOWN}}
525 }
526 
527 void only_success_path_does_not_alias_with_stdout(void) {
528   if (stdout) return;
529   FILE *f = fopen("/tmp/foof", "r"); // no-crash
530   if (!f) return;
531   fclose(f);
532 }
533