xref: /llvm-project/clang/test/Analysis/getline-unixapi.c (revision 730ca47a0cc7380def6df1d25b30c1378fd8bf14)
1 // RUN: %clang_analyze_cc1 -analyzer-checker=core,unix,debug.ExprInspection -verify %s
2 
3 #include "Inputs/system-header-simulator.h"
4 #include "Inputs/system-header-simulator-for-malloc.h"
5 #include "Inputs/system-header-simulator-for-valist.h"
6 
7 void clang_analyzer_eval(int);
8 void clang_analyzer_dump_int(int);
9 void clang_analyzer_dump_ptr(void*);
10 void clang_analyzer_warnIfReached();
11 
test_getline_null_lineptr()12 void test_getline_null_lineptr() {
13   FILE *F1 = tmpfile();
14   if (!F1)
15     return;
16 
17   char **buffer = NULL;
18   size_t n = 0;
19   getline(buffer, &n, F1); // expected-warning {{Line pointer might be NULL}}
20   fclose(F1);
21 }
22 
test_getline_null_size()23 void test_getline_null_size() {
24   FILE *F1 = tmpfile();
25   if (!F1)
26     return;
27   char *buffer = NULL;
28   getline(&buffer, NULL, F1); // expected-warning {{Size pointer might be NULL}}
29   fclose(F1);
30 }
31 
test_getline_null_buffer_size_gt0()32 void test_getline_null_buffer_size_gt0() {
33   FILE *F1 = tmpfile();
34   if (!F1)
35     return;
36   char *buffer = NULL;
37   size_t n = 8;
38   getline(&buffer, &n, F1); // ok since posix 2018
39   free(buffer);
40   fclose(F1);
41 }
42 
test_getline_null_buffer_size_gt0_2(size_t n)43 void test_getline_null_buffer_size_gt0_2(size_t n) {
44   FILE *F1 = tmpfile();
45   if (!F1)
46     return;
47   char *buffer = NULL;
48   if (n > 0) {
49     getline(&buffer, &n, F1); // ok since posix 2018
50   }
51   free(buffer);
52   fclose(F1);
53 }
54 
test_getline_null_buffer_unknown_size(size_t n)55 void test_getline_null_buffer_unknown_size(size_t n) {
56   FILE *F1 = tmpfile();
57   if (!F1)
58     return;
59   char *buffer = NULL;
60 
61   getline(&buffer, &n, F1);  // ok
62   fclose(F1);
63   free(buffer);
64 }
65 
test_getline_null_buffer_undef_size()66 void test_getline_null_buffer_undef_size() {
67   FILE *F1 = tmpfile();
68   if (!F1)
69     return;
70 
71   char *buffer = NULL;
72   size_t n;
73 
74   getline(&buffer, &n, F1); // ok since posix 2018
75   fclose(F1);
76   free(buffer);
77 }
78 
test_getline_buffer_size_0()79 void test_getline_buffer_size_0() {
80   FILE *F1 = tmpfile();
81   if (!F1)
82     return;
83 
84   char *buffer = malloc(10);
85   size_t n = 0;
86   if (buffer != NULL)
87     getline(&buffer, &n, F1); // ok, the buffer is enough for 0 character
88   fclose(F1);
89   free(buffer);
90 }
91 
test_getline_buffer_bad_size()92 void test_getline_buffer_bad_size() {
93   FILE *F1 = tmpfile();
94   if (!F1)
95     return;
96 
97   char *buffer = malloc(10);
98   size_t n = 100;
99   if (buffer != NULL)
100     getline(&buffer, &n, F1); // expected-warning {{The buffer from the first argument is smaller than the size specified by the second parameter}}
101   fclose(F1);
102   free(buffer);
103 }
104 
test_getline_buffer_smaller_size()105 void test_getline_buffer_smaller_size() {
106   FILE *F1 = tmpfile();
107   if (!F1)
108     return;
109 
110   char *buffer = malloc(100);
111   size_t n = 10;
112   if (buffer != NULL)
113     getline(&buffer, &n, F1); // ok, there is enough space for 10 characters
114   fclose(F1);
115   free(buffer);
116 }
117 
test_getline_buffer_undef_size()118 void test_getline_buffer_undef_size() {
119   FILE *F1 = tmpfile();
120   if (!F1)
121     return;
122 
123   char *buffer = malloc(100);
124   size_t n;
125   if (buffer != NULL)
126     getline(&buffer, &n, F1); // expected-warning {{The buffer from the first argument is not NULL, but the size specified by the second parameter is undefined}}
127   fclose(F1);
128   free(buffer);
129 }
130 
131 
test_getline_null_buffer()132 void test_getline_null_buffer() {
133   FILE *F1 = tmpfile();
134   if (!F1)
135     return;
136   char *buffer = NULL;
137   size_t n = 0;
138   ssize_t r = getline(&buffer, &n, F1);
139   // getline returns -1 on failure, number of char reads on success (>= 0)
140   if (r < -1) {
141     clang_analyzer_warnIfReached(); // must not happen
142   } else {
143     // The buffer could be allocated both on failure and success
144     clang_analyzer_dump_int(n);      // expected-warning {{conj_$}}
145     clang_analyzer_dump_ptr(buffer); // expected-warning {{conj_$}}
146   }
147   free(buffer);
148   fclose(F1);
149 }
150 
test_getdelim_null_size()151 void test_getdelim_null_size() {
152   FILE *F1 = tmpfile();
153   if (!F1)
154     return;
155   char *buffer = NULL;
156   getdelim(&buffer, NULL, ',', F1); // expected-warning {{Size pointer might be NULL}}
157   fclose(F1);
158 }
159 
test_getdelim_null_buffer_size_gt0()160 void test_getdelim_null_buffer_size_gt0() {
161   FILE *F1 = tmpfile();
162   if (!F1)
163     return;
164   char *buffer = NULL;
165   size_t n = 8;
166   getdelim(&buffer, &n, ';', F1); // ok since posix 2018
167   free(buffer);
168   fclose(F1);
169 }
170 
test_getdelim_null_buffer_size_gt0_2(size_t n)171 void test_getdelim_null_buffer_size_gt0_2(size_t n) {
172   FILE *F1 = tmpfile();
173   if (!F1)
174     return;
175   char *buffer = NULL;
176   if (n > 0) {
177     getdelim(&buffer, &n, ' ', F1);  // ok since posix 2018
178   }
179   free(buffer);
180   fclose(F1);
181 }
182 
test_getdelim_null_buffer_unknown_size(size_t n)183 void test_getdelim_null_buffer_unknown_size(size_t n) {
184   FILE *F1 = tmpfile();
185   if (!F1)
186     return;
187   char *buffer = NULL;
188   getdelim(&buffer, &n, '-', F1);  // ok
189   fclose(F1);
190   free(buffer);
191 }
192 
test_getdelim_null_buffer()193 void test_getdelim_null_buffer() {
194   FILE *F1 = tmpfile();
195   if (!F1)
196     return;
197   char *buffer = NULL;
198   size_t n = 0;
199   ssize_t r = getdelim(&buffer, &n, '\r', F1);
200   // getdelim returns -1 on failure, number of char reads on success (>= 0)
201   if (r < -1) {
202     clang_analyzer_warnIfReached(); // must not happen
203   }
204   else {
205     // The buffer could be allocated both on failure and success
206     clang_analyzer_dump_int(n);      // expected-warning {{conj_$}}
207     clang_analyzer_dump_ptr(buffer); // expected-warning {{conj_$}}
208   }
209   free(buffer);
210   fclose(F1);
211 }
212 
test_getline_while()213 void test_getline_while() {
214   FILE *file = fopen("file.txt", "r");
215   if (file == NULL) {
216     return;
217   }
218 
219   char *line = NULL;
220   size_t len = 0;
221   ssize_t read;
222 
223   while ((read = getline(&line, &len, file)) != -1) {
224     printf("%s\n", line);
225   }
226 
227   free(line);
228   fclose(file);
229 }
230 
test_getline_return_check()231 void test_getline_return_check() {
232   FILE *file = fopen("file.txt", "r");
233   if (file == NULL) {
234     return;
235   }
236 
237   char *line = NULL;
238   size_t len = 0;
239   ssize_t r = getline(&line, &len, file);
240 
241   if (r != -1) {
242     if (line[0] == '\0') {} // ok
243   }
244   free(line);
245   fclose(file);
246 }
247 
test_getline_clear_eof()248 void test_getline_clear_eof() {
249   FILE *file = fopen("file.txt", "r");
250   if (file == NULL) {
251     return;
252   }
253 
254   size_t n = 10;
255   char *buffer = malloc(n);
256   ssize_t read = fread(buffer, n, 1, file);
257   if (feof(file)) {
258     clearerr(file);
259     getline(&buffer, &n, file); // ok
260   }
261   fclose(file);
262   free(buffer);
263 }
264 
test_getline_not_null(char ** buffer,size_t * size)265 void test_getline_not_null(char **buffer, size_t *size) {
266   FILE *file = fopen("file.txt", "r");
267   if (file == NULL) {
268     return;
269   }
270 
271   getline(buffer, size, file);
272   fclose(file);
273 
274   if (size == NULL || buffer == NULL) {
275     clang_analyzer_warnIfReached(); // must not happen
276   }
277 }
278 
test_getline_size_constraint(size_t size)279 void test_getline_size_constraint(size_t size) {
280   FILE *file = fopen("file.txt", "r");
281   if (file == NULL) {
282     return;
283   }
284 
285   size_t old_size = size;
286   char *buffer = malloc(10);
287   if (buffer != NULL) {
288     ssize_t r = getline(&buffer, &size, file);
289     if (r >= 0) {
290       // Since buffer has a size of 10, old_size must be less than or equal to 10.
291       // Otherwise, there would be UB.
292       clang_analyzer_eval(old_size <= 10); // expected-warning{{TRUE}}
293     }
294   }
295   fclose(file);
296   free(buffer);
297 }
298 
test_getline_negative_buffer()299 void test_getline_negative_buffer() {
300   FILE *file = fopen("file.txt", "r");
301   if (file == NULL) {
302     return;
303   }
304 
305   char *buffer = NULL;
306   size_t n = -1;
307   getline(&buffer, &n, file); // ok since posix 2018
308   free(buffer);
309   fclose(file);
310 }
311 
test_getline_negative_buffer_2(char * buffer)312 void test_getline_negative_buffer_2(char *buffer) {
313   FILE *file = fopen("file.txt", "r");
314   if (file == NULL) {
315     return;
316   }
317 
318   size_t n = -1;
319   (void)getline(&buffer, &n, file); // ok
320   free(buffer);
321   fclose(file);
322 }
323