xref: /llvm-project/clang/test/Analysis/stream-note.c (revision 2e3c7dbbcbfa37ae83251bb3da388df772680689)
1 // RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Stream -analyzer-output text \
2 // RUN:   -analyzer-config unix.Stream:Pedantic=true \
3 // RUN:   -verify %s
4 // RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Stream,unix.StdCLibraryFunctions -analyzer-output text \
5 // RUN:   -analyzer-config unix.Stream:Pedantic=true \
6 // RUN:   -analyzer-config unix.StdCLibraryFunctions:ModelPOSIX=true -verify=expected,stdargs %s
7 
8 #include "Inputs/system-header-simulator.h"
9 
10 void check_note_at_correct_open(void) {
11   FILE *F1 = tmpfile(); // expected-note {{Stream opened here}}
12   // stdargs-note@-1 {{'tmpfile' is successful}}
13   if (!F1)
14     // expected-note@-1 {{'F1' is non-null}}
15     // expected-note@-2 {{Taking false branch}}
16     return;
17   FILE *F2 = tmpfile();
18   if (!F2) {
19     // expected-note@-1 {{'F2' is non-null}}
20     // expected-note@-2 {{Taking false branch}}
21     fclose(F1);
22     return;
23   }
24   rewind(F2);
25   fclose(F2);
26   rewind(F1);
27 }
28 // expected-warning@-1 {{Opened stream never closed. Potential resource leak}}
29 // expected-note@-2 {{Opened stream never closed. Potential resource leak}}
30 
31 void check_note_fopen(void) {
32   FILE *F = fopen("file", "r"); // expected-note {{Stream opened here}}
33   // stdargs-note@-1 {{'fopen' is successful}}
34   if (!F)
35     // expected-note@-1 {{'F' is non-null}}
36     // expected-note@-2 {{Taking false branch}}
37     return;
38 }
39 // expected-warning@-1 {{Opened stream never closed. Potential resource leak}}
40 // expected-note@-2 {{Opened stream never closed. Potential resource leak}}
41 
42 void check_note_freopen(void) {
43   FILE *F = fopen("file", "r"); // expected-note {{Stream opened here}}
44   // stdargs-note@-1 {{'fopen' is successful}}
45   if (!F)
46     // expected-note@-1 {{'F' is non-null}}
47     // expected-note@-2 {{Taking false branch}}
48     return;
49   F = freopen(0, "w", F); // expected-note {{Stream reopened here}}
50   // stdargs-note@-1 {{'freopen' is successful}}
51   if (!F)
52     // expected-note@-1 {{'F' is non-null}}
53     // expected-note@-2 {{Taking false branch}}
54     return;
55 }
56 // expected-warning@-1 {{Opened stream never closed. Potential resource leak}}
57 // expected-note@-2 {{Opened stream never closed. Potential resource leak}}
58 
59 void check_note_fdopen(int fd) {
60   FILE *F = fdopen(fd, "r"); // expected-note {{Stream opened here}}
61   // stdargs-note@-1 {{'fdopen' is successful}}
62   if (!F)
63     // expected-note@-1 {{'F' is non-null}}
64     // expected-note@-2 {{Taking false branch}}
65     return;
66 }
67 // expected-warning@-1 {{Opened stream never closed. Potential resource leak}}
68 // expected-note@-2 {{Opened stream never closed. Potential resource leak}}
69 
70 void check_note_leak_2(int c) {
71   FILE *F1 = fopen("foo1.c", "r"); // expected-note {{Stream opened here}}
72   // stdargs-note@-1 {{'fopen' is successful}}
73   if (!F1)
74     // expected-note@-1 {{'F1' is non-null}}
75     // expected-note@-2 {{Taking false branch}}
76     // expected-note@-3 {{'F1' is non-null}}
77     // expected-note@-4 {{Taking false branch}}
78     return;
79   FILE *F2 = fopen("foo2.c", "r"); // expected-note {{Stream opened here}}
80   // stdargs-note@-1 {{'fopen' is successful}}
81   if (!F2) {
82     // expected-note@-1 {{'F2' is non-null}}
83     // expected-note@-2 {{Taking false branch}}
84     // expected-note@-3 {{'F2' is non-null}}
85     // expected-note@-4 {{Taking false branch}}
86     fclose(F1);
87     return;
88   }
89   if (c)
90     // expected-note@-1 {{Assuming 'c' is not equal to 0}}
91     // expected-note@-2 {{Taking true branch}}
92     // expected-note@-3 {{Assuming 'c' is not equal to 0}}
93     // expected-note@-4 {{Taking true branch}}
94     return;
95   // expected-warning@-1 {{Opened stream never closed. Potential resource leak}}
96   // expected-note@-2 {{Opened stream never closed. Potential resource leak}}
97   // expected-warning@-3 {{Opened stream never closed. Potential resource leak}}
98   // expected-note@-4 {{Opened stream never closed. Potential resource leak}}
99   fclose(F1);
100   fclose(F2);
101 }
102 
103 void check_track_null(void) {
104   FILE *F;
105   F = fopen("foo1.c", "r"); // expected-note {{Value assigned to 'F'}} expected-note {{Assuming pointer value is null}}
106   // stdargs-note@-1 {{'fopen' fails}}
107   if (F != NULL) {          // expected-note {{Taking false branch}} expected-note {{'F' is equal to NULL}}
108     fclose(F);
109     return;
110   }
111   fclose(F); // expected-warning {{Stream pointer might be NULL}}
112              // expected-note@-1 {{Stream pointer might be NULL}}
113 }
114 
115 void check_eof_notes_feof_after_feof(void) {
116   FILE *F;
117   char Buf[10];
118   F = fopen("foo1.c", "r");
119   if (F == NULL) { // expected-note {{Taking false branch}} expected-note {{'F' is not equal to NULL}}
120     return;
121   }
122   fread(Buf, 1, 1, F);
123   if (feof(F)) { // expected-note {{Taking true branch}}
124     clearerr(F);
125     fread(Buf, 1, 1, F);   // expected-note {{Assuming stream reaches end-of-file here}}
126     if (feof(F)) {         // expected-note {{Taking true branch}}
127       fread(Buf, 1, 1, F); // expected-warning {{Read function called when stream is in EOF state. Function has no effect}}
128       // expected-note@-1 {{Read function called when stream is in EOF state. Function has no effect}}
129     }
130   }
131   fclose(F);
132 }
133 
134 void check_eof_notes_feof_after_no_feof(void) {
135   FILE *F;
136   char Buf[10];
137   F = fopen("foo1.c", "r");
138   if (F == NULL) { // expected-note {{Taking false branch}} expected-note {{'F' is not equal to NULL}}
139     return;
140   }
141   fread(Buf, 1, 1, F);
142   if (feof(F)) { // expected-note {{Taking false branch}}
143     fclose(F);
144     return;
145   } else if (ferror(F)) { // expected-note {{Taking false branch}}
146     fclose(F);
147     return;
148   }
149   fread(Buf, 1, 1, F);   // expected-note {{Assuming stream reaches end-of-file here}}
150   if (feof(F)) {         // expected-note {{Taking true branch}}
151     fread(Buf, 1, 1, F); // expected-warning {{Read function called when stream is in EOF state. Function has no effect}}
152     // expected-note@-1 {{Read function called when stream is in EOF state. Function has no effect}}
153   }
154   fclose(F);
155 }
156 
157 void check_eof_notes_feof_or_no_error(void) {
158   FILE *F;
159   char Buf[10];
160   F = fopen("foo1.c", "r");
161   if (F == NULL) // expected-note {{Taking false branch}} expected-note {{'F' is not equal to NULL}}
162     return;
163   int RRet = fread(Buf, 1, 1, F); // expected-note {{Assuming stream reaches end-of-file here}}
164   if (ferror(F)) {                // expected-note {{Taking false branch}}
165   } else {
166     fread(Buf, 1, 1, F); // expected-warning {{Read function called when stream is in EOF state. Function has no effect}}
167     // expected-note@-1 {{Read function called when stream is in EOF state. Function has no effect}}
168   }
169   fclose(F);
170 }
171 
172 void check_indeterminate_notes(void) {
173   FILE *F;
174   F = fopen("foo1.c", "r");
175   if (F == NULL)     // expected-note {{Taking false branch}} \
176                      // expected-note {{'F' is not equal to NULL}}
177     return;
178   int R = fgetc(F);  // no note
179   if (R >= 0) {      // expected-note {{Taking true branch}} \
180                      // expected-note {{'R' is >= 0}}
181     fgetc(F);        // expected-note {{Assuming this stream operation fails}}
182     if (ferror(F))   // expected-note {{Taking true branch}}
183       fgetc(F);      // expected-warning {{File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior}} \
184                      // expected-note {{File position of the stream might be 'indeterminate' after a failed operation. Can cause undefined behavior}}
185   }
186   fclose(F);
187 }
188 
189 void check_indeterminate_after_clearerr(void) {
190   FILE *F;
191   char Buf[10];
192   F = fopen("foo1.c", "r");
193   if (F == NULL)          // expected-note {{Taking false branch}} \
194                           // expected-note {{'F' is not equal to NULL}}
195     return;
196   fread(Buf, 1, 1, F);    // expected-note {{Assuming this stream operation fails}}
197   if (ferror(F)) {        // expected-note {{Taking true branch}}
198     clearerr(F);
199     fread(Buf, 1, 1, F);  // expected-warning {{might be 'indeterminate' after a failed operation}} \
200                           // expected-note {{might be 'indeterminate' after a failed operation}}
201   }
202   fclose(F);
203 }
204 
205 void check_indeterminate_eof(void) {
206   FILE *F;
207   char Buf[2];
208   F = fopen("foo1.c", "r");
209   if (F == NULL)               // expected-note {{Taking false branch}} \
210                                // expected-note {{'F' is not equal to NULL}} \
211                                // expected-note {{Taking false branch}} \
212                                // expected-note {{'F' is not equal to NULL}}
213     return;
214   fgets(Buf, sizeof(Buf), F);  // expected-note {{Assuming this stream operation fails}} \
215                                // expected-note {{Assuming stream reaches end-of-file here}}
216 
217   fgets(Buf, sizeof(Buf), F);  // expected-warning {{might be 'indeterminate'}} \
218                                // expected-note {{might be 'indeterminate'}} \
219                                // expected-warning {{stream is in EOF state}} \
220                                // expected-note {{stream is in EOF state}}
221   fclose(F);
222 }
223 
224 void check_indeterminate_fseek(void) {
225   FILE *F = fopen("file", "r");
226   if (!F)                           // expected-note {{Taking false branch}} \
227                                     // expected-note {{'F' is non-null}}
228     return;
229   int Ret = fseek(F, 1, SEEK_SET);  // expected-note {{Assuming this stream operation fails}}
230   if (Ret) {                        // expected-note {{Taking true branch}} \
231                                     // expected-note {{'Ret' is -1}}
232     char Buf[2];
233     fwrite(Buf, 1, 2, F);           // expected-warning {{might be 'indeterminate'}} \
234                                     // expected-note {{might be 'indeterminate'}}
235   }
236   fclose(F);
237 }
238 
239 void error_fseek_ftell(void) {
240   FILE *F = fopen("file", "r");
241   if (!F)                 // expected-note {{Taking false branch}} \
242                           // expected-note {{'F' is non-null}}
243     return;
244   fseek(F, 0, SEEK_END);  // expected-note {{Assuming this stream operation fails}}
245   long size = ftell(F);   // expected-warning {{might be 'indeterminate'}} \
246                           // expected-note {{might be 'indeterminate'}}
247   if (size == -1) {
248     fclose(F);
249     return;
250   }
251   if (size == 1)
252     fprintf(F, "abcd");
253   fclose(F);
254 }
255 
256 void error_fseek_read_eof(void) {
257   FILE *F = fopen("file", "r");
258   if (!F)
259     return;
260   if (fseek(F, 22, SEEK_SET) == -1) {
261     fclose(F);
262     return;
263   }
264   fgetc(F); // no warning
265   fclose(F);
266 }
267 
268 void check_note_at_use_after_close(void) {
269   FILE *F = tmpfile();
270   if (!F) // expected-note {{'F' is non-null}} expected-note {{Taking false branch}}
271     return;
272   fclose(F); // expected-note {{Stream is closed here}}
273   rewind(F); // expected-warning {{Use of a stream that might be already closed}}
274   // expected-note@-1 {{Use of a stream that might be already closed}}
275 }
276