Branch data Line data Source code
1 : : /* A state machine for detecting misuses of <stdio.h>'s FILE * API.
2 : : Copyright (C) 2019-2025 Free Software Foundation, Inc.
3 : : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 : :
5 : : This file is part of GCC.
6 : :
7 : : GCC is free software; you can redistribute it and/or modify it
8 : : under the terms of the GNU General Public License as published by
9 : : the Free Software Foundation; either version 3, or (at your option)
10 : : any later version.
11 : :
12 : : GCC is distributed in the hope that it will be useful, but
13 : : WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : General Public License for more details.
16 : :
17 : : You should have received a copy of the GNU General Public License
18 : : along with GCC; see the file COPYING3. If not see
19 : : <http://www.gnu.org/licenses/>. */
20 : :
21 : : #include "analyzer/common.h"
22 : :
23 : : #include "diagnostic-event-id.h"
24 : : #include "selftest.h"
25 : :
26 : : #include "analyzer/analyzer-logging.h"
27 : : #include "analyzer/sm.h"
28 : : #include "analyzer/pending-diagnostic.h"
29 : : #include "analyzer/function-set.h"
30 : : #include "analyzer/analyzer-selftests.h"
31 : : #include "analyzer/call-string.h"
32 : : #include "analyzer/program-point.h"
33 : : #include "analyzer/program-state.h"
34 : : #include "analyzer/store.h"
35 : : #include "analyzer/region-model.h"
36 : : #include "analyzer/call-details.h"
37 : :
38 : : #if ENABLE_ANALYZER
39 : :
40 : : namespace ana {
41 : :
42 : : namespace {
43 : :
44 : : /* A state machine for detecting misuses of <stdio.h>'s FILE * API. */
45 : :
46 : : class fileptr_state_machine : public state_machine
47 : : {
48 : : public:
49 : : fileptr_state_machine (logger *logger);
50 : :
51 : 1041888 : bool inherited_state_p () const final override { return false; }
52 : :
53 : : state_machine::state_t
54 : 1041130 : get_default_state (const svalue *sval) const final override
55 : : {
56 : 1041130 : if (tree cst = sval->maybe_get_constant ())
57 : : {
58 : 154121 : if (zerop (cst))
59 : 71360 : return m_null;
60 : : }
61 : 969770 : return m_start;
62 : : }
63 : :
64 : : bool on_stmt (sm_context &sm_ctxt,
65 : : const supernode *node,
66 : : const gimple *stmt) const final override;
67 : :
68 : : void on_condition (sm_context &sm_ctxt,
69 : : const supernode *node,
70 : : const gimple *stmt,
71 : : const svalue *lhs,
72 : : enum tree_code op,
73 : : const svalue *rhs) const final override;
74 : :
75 : : bool can_purge_p (state_t s) const final override;
76 : :
77 : : std::unique_ptr<pending_diagnostic>
78 : : on_leak (tree var,
79 : : const program_state *old_state,
80 : : const program_state *new_state) const final override;
81 : :
82 : : /* State for a FILE * returned from fopen that hasn't been checked for
83 : : NULL.
84 : : It could be an open stream, or could be NULL. */
85 : : state_t m_unchecked;
86 : :
87 : : /* State for a FILE * that's known to be NULL. */
88 : : state_t m_null;
89 : :
90 : : /* State for a FILE * that's known to be a non-NULL open stream. */
91 : : state_t m_nonnull;
92 : :
93 : : /* State for a FILE * that's had fclose called on it. */
94 : : state_t m_closed;
95 : :
96 : : /* Stop state, for a FILE * we don't want to track any more. */
97 : : state_t m_stop;
98 : : };
99 : :
100 : : /* Base class for diagnostics relative to fileptr_state_machine. */
101 : :
102 : 0 : class file_diagnostic : public pending_diagnostic
103 : : {
104 : : public:
105 : 206 : file_diagnostic (const fileptr_state_machine &sm, tree arg)
106 : 206 : : m_sm (sm), m_arg (arg)
107 : : {}
108 : :
109 : 206 : bool subclass_equal_p (const pending_diagnostic &base_other) const override
110 : : {
111 : 206 : return same_tree_p (m_arg, ((const file_diagnostic &)base_other).m_arg);
112 : : }
113 : :
114 : : bool
115 : 96 : describe_state_change (pretty_printer &pp,
116 : : const evdesc::state_change &change) override
117 : : {
118 : 96 : if (change.m_old_state == m_sm.get_start_state ()
119 : 96 : && change.m_new_state == m_sm.m_unchecked)
120 : : // TODO: verify that it's the fopen stmt, not a copy
121 : : {
122 : 20 : pp_string (&pp, "opened here");
123 : 20 : return true;
124 : : }
125 : 76 : if (change.m_old_state == m_sm.m_unchecked
126 : 76 : && change.m_new_state == m_sm.m_nonnull)
127 : : {
128 : 76 : if (change.m_expr)
129 : : {
130 : 76 : pp_printf (&pp,
131 : : "assuming %qE is non-NULL",
132 : : change.m_expr);
133 : 76 : return true;
134 : : }
135 : : else
136 : : {
137 : 0 : pp_printf (&pp, "assuming FILE * is non-NULL");
138 : 0 : return true;
139 : : }
140 : : }
141 : 0 : if (change.m_new_state == m_sm.m_null)
142 : : {
143 : 0 : if (change.m_expr)
144 : : {
145 : 0 : pp_printf (&pp, "assuming %qE is NULL",
146 : : change.m_expr);
147 : 0 : return true;
148 : : }
149 : : else
150 : : {
151 : 0 : pp_printf (&pp, "assuming FILE * is NULL");
152 : 0 : return true;
153 : : }
154 : : }
155 : : return false;
156 : : }
157 : :
158 : : diagnostic_event::meaning
159 : 24 : get_meaning_for_state_change (const evdesc::state_change &change)
160 : : const final override
161 : : {
162 : 24 : if (change.m_old_state == m_sm.get_start_state ()
163 : 24 : && change.m_new_state == m_sm.m_unchecked)
164 : 8 : return diagnostic_event::meaning (diagnostic_event::verb::acquire,
165 : 8 : diagnostic_event::noun::resource);
166 : 16 : if (change.m_new_state == m_sm.m_closed)
167 : 8 : return diagnostic_event::meaning (diagnostic_event::verb::release,
168 : 8 : diagnostic_event::noun::resource);
169 : 8 : return diagnostic_event::meaning ();
170 : : }
171 : :
172 : : protected:
173 : : const fileptr_state_machine &m_sm;
174 : : tree m_arg;
175 : : };
176 : :
177 : 0 : class double_fclose : public file_diagnostic
178 : : {
179 : : public:
180 : 12 : double_fclose (const fileptr_state_machine &sm, tree arg)
181 : 12 : : file_diagnostic (sm, arg)
182 : : {}
183 : :
184 : 126 : const char *get_kind () const final override { return "double_fclose"; }
185 : :
186 : 22 : int get_controlling_option () const final override
187 : : {
188 : 22 : return OPT_Wanalyzer_double_fclose;
189 : : }
190 : :
191 : 10 : bool emit (diagnostic_emission_context &ctxt) final override
192 : : {
193 : : /* CWE-1341: Multiple Releases of Same Resource or Handle. */
194 : 10 : ctxt.add_cwe (1341);
195 : 10 : return ctxt.warn ("double %<fclose%> of FILE %qE",
196 : 10 : m_arg);
197 : : }
198 : :
199 : : bool
200 : 60 : describe_state_change (pretty_printer &pp,
201 : : const evdesc::state_change &change) override
202 : : {
203 : 60 : if (change.m_new_state == m_sm.m_closed)
204 : : {
205 : 20 : m_first_fclose_event = change.m_event_id;
206 : 20 : pp_printf (&pp, "first %qs here", "fclose");
207 : 20 : return true;
208 : : }
209 : 40 : return file_diagnostic::describe_state_change (pp, change);
210 : : }
211 : :
212 : : bool
213 : 20 : describe_final_event (pretty_printer &pp,
214 : : const evdesc::final_event &) final override
215 : : {
216 : 20 : if (m_first_fclose_event.known_p ())
217 : 20 : pp_printf (&pp,
218 : : "second %qs here; first %qs was at %@",
219 : : "fclose", "fclose",
220 : : &m_first_fclose_event);
221 : : else
222 : 0 : pp_printf (&pp,
223 : : "second %qs here", "fclose");
224 : 20 : return true;
225 : : }
226 : :
227 : : private:
228 : : diagnostic_event_id_t m_first_fclose_event;
229 : : };
230 : :
231 : : class file_leak : public file_diagnostic
232 : : {
233 : : public:
234 : 194 : file_leak (const fileptr_state_machine &sm, tree arg,
235 : : const program_state *final_state)
236 : 194 : : file_diagnostic (sm, arg),
237 : 194 : m_final_state ()
238 : : {
239 : 194 : if (final_state)
240 : 194 : m_final_state = std::make_unique<program_state> (*final_state);
241 : 194 : }
242 : :
243 : 1208 : const char *get_kind () const final override { return "file_leak"; }
244 : :
245 : 98 : int get_controlling_option () const final override
246 : : {
247 : 98 : return OPT_Wanalyzer_file_leak;
248 : : }
249 : :
250 : 88 : bool emit (diagnostic_emission_context &ctxt) final override
251 : : {
252 : : /* CWE-775: "Missing Release of File Descriptor or Handle after
253 : : Effective Lifetime". */
254 : 88 : ctxt.add_cwe (775);
255 : 88 : if (m_arg)
256 : 88 : return ctxt.warn ("leak of FILE %qE", m_arg);
257 : : else
258 : 0 : return ctxt.warn ("leak of FILE");
259 : : }
260 : :
261 : : bool
262 : 232 : describe_state_change (pretty_printer &pp,
263 : : const evdesc::state_change &change) final override
264 : : {
265 : 232 : if (change.m_new_state == m_sm.m_unchecked)
266 : : {
267 : 176 : m_fopen_event = change.m_event_id;
268 : 176 : pp_string (&pp, "opened here");
269 : 176 : return true;
270 : : }
271 : 56 : return file_diagnostic::describe_state_change (pp, change);
272 : : }
273 : :
274 : : bool
275 : 176 : describe_final_event (pretty_printer &pp,
276 : : const evdesc::final_event &ev) final override
277 : : {
278 : 176 : if (m_fopen_event.known_p ())
279 : : {
280 : 176 : if (ev.m_expr)
281 : 176 : pp_printf (&pp,
282 : : "%qE leaks here; was opened at %@",
283 : : ev.m_expr, &m_fopen_event);
284 : : else
285 : 0 : pp_printf (&pp,
286 : : "leaks here; was opened at %@",
287 : : &m_fopen_event);
288 : : }
289 : : else
290 : : {
291 : 0 : if (ev.m_expr)
292 : 0 : pp_printf (&pp, "%qE leaks here", ev.m_expr);
293 : : else
294 : 0 : pp_printf (&pp, "leaks here");
295 : : }
296 : 176 : return true;
297 : : }
298 : :
299 : : const program_state *
300 : 88 : get_final_state () const final override
301 : : {
302 : 88 : return m_final_state.get ();
303 : : }
304 : :
305 : : private:
306 : : diagnostic_event_id_t m_fopen_event;
307 : : std::unique_ptr<program_state> m_final_state;
308 : : };
309 : :
310 : : /* fileptr_state_machine's ctor. */
311 : :
312 : 3308 : fileptr_state_machine::fileptr_state_machine (logger *logger)
313 : : : state_machine ("file", logger),
314 : 6616 : m_unchecked (add_state ("unchecked")),
315 : 3308 : m_null (add_state ("null")),
316 : 3308 : m_nonnull (add_state ("nonnull")),
317 : 3308 : m_closed (add_state ("closed")),
318 : 6616 : m_stop (add_state ("stop"))
319 : : {
320 : 3308 : }
321 : :
322 : : /* Get a set of functions that are known to take a FILE * that must be open,
323 : : and are known to not close it. */
324 : :
325 : : static function_set
326 : 55754 : get_file_using_fns ()
327 : : {
328 : : // TODO: populate this list more fully
329 : 55754 : static const char * const funcnames[] = {
330 : : /* This array must be kept sorted. */
331 : : "__fbufsize",
332 : : "__flbf",
333 : : "__fpending",
334 : : "__fpurge",
335 : : "__freadable",
336 : : "__freading",
337 : : "__fsetlocking",
338 : : "__fwritable",
339 : : "__fwriting",
340 : : "clearerr",
341 : : "clearerr_unlocked",
342 : : "feof",
343 : : "feof_unlocked",
344 : : "ferror",
345 : : "ferror_unlocked",
346 : : "fflush", // safe to call with NULL
347 : : "fflush_unlocked", // safe to call with NULL
348 : : "fgetc",
349 : : "fgetc_unlocked",
350 : : "fgetpos",
351 : : "fgets",
352 : : "fgets_unlocked",
353 : : "fgetwc_unlocked",
354 : : "fgetws_unlocked",
355 : : "fileno",
356 : : "fileno_unlocked",
357 : : "fprintf",
358 : : "fputc",
359 : : "fputc_unlocked",
360 : : "fputs",
361 : : "fputs_unlocked",
362 : : "fputwc_unlocked",
363 : : "fputws_unlocked",
364 : : "fread_unlocked",
365 : : "fseek",
366 : : "fsetpos",
367 : : "ftell",
368 : : "fwrite_unlocked",
369 : : "getc",
370 : : "getc_unlocked",
371 : : "getwc_unlocked",
372 : : "putc",
373 : : "putc_unlocked",
374 : : "rewind",
375 : : "setbuf",
376 : : "setbuffer",
377 : : "setlinebuf",
378 : : "setvbuf",
379 : : "ungetc",
380 : : "vfprintf"
381 : : };
382 : 55754 : const size_t count = ARRAY_SIZE (funcnames);
383 : 55754 : function_set fs (funcnames, count);
384 : 55754 : return fs;
385 : : }
386 : :
387 : : /* Return true if FNDECL is known to require an open FILE *, and is known
388 : : to not close it. */
389 : :
390 : : static bool
391 : 55750 : is_file_using_fn_p (tree fndecl)
392 : : {
393 : 55750 : function_set fs = get_file_using_fns ();
394 : 55750 : if (fs.contains_decl_p (fndecl))
395 : : return true;
396 : :
397 : : /* Also support variants of these names prefixed with "_IO_". */
398 : 54456 : const char *name = IDENTIFIER_POINTER (DECL_NAME (fndecl));
399 : 54456 : if (startswith (name, "_IO_") && fs.contains_name_p (name + 4))
400 : : return true;
401 : :
402 : : return false;
403 : : }
404 : :
405 : : /* Implementation of state_machine::on_stmt vfunc for fileptr_state_machine. */
406 : :
407 : : bool
408 : 269538 : fileptr_state_machine::on_stmt (sm_context &sm_ctxt,
409 : : const supernode *node,
410 : : const gimple *stmt) const
411 : : {
412 : 269538 : if (const gcall *call = dyn_cast <const gcall *> (stmt))
413 : 57779 : if (tree callee_fndecl = sm_ctxt.get_fndecl_for_call (*call))
414 : : {
415 : 56297 : if (is_named_call_p (callee_fndecl, "fopen", *call, 2))
416 : : {
417 : 266 : tree lhs = gimple_call_lhs (call);
418 : 266 : if (lhs)
419 : 266 : sm_ctxt.on_transition (node, stmt, lhs, m_start, m_unchecked);
420 : : else
421 : : {
422 : : /* TODO: report leak. */
423 : : }
424 : 266 : return true;
425 : : }
426 : :
427 : 56031 : if (is_named_call_p (callee_fndecl, "fclose", *call, 1))
428 : : {
429 : 281 : tree arg = gimple_call_arg (call, 0);
430 : :
431 : 281 : sm_ctxt.on_transition (node, stmt, arg, m_start, m_closed);
432 : :
433 : : // TODO: is it safe to call fclose (NULL) ?
434 : 281 : sm_ctxt.on_transition (node, stmt, arg, m_unchecked, m_closed);
435 : 281 : sm_ctxt.on_transition (node, stmt, arg, m_null, m_closed);
436 : :
437 : 281 : sm_ctxt.on_transition (node, stmt , arg, m_nonnull, m_closed);
438 : :
439 : 281 : if (sm_ctxt.get_state (stmt, arg) == m_closed)
440 : : {
441 : 12 : tree diag_arg = sm_ctxt.get_diagnostic_tree (arg);
442 : 12 : sm_ctxt.warn (node, stmt, arg,
443 : 12 : std::make_unique<double_fclose> (*this,
444 : : diag_arg));
445 : 12 : sm_ctxt.set_next_state (stmt, arg, m_stop);
446 : : }
447 : 281 : return true;
448 : : }
449 : :
450 : 55750 : if (is_file_using_fn_p (callee_fndecl))
451 : : {
452 : : // TODO: operations on unchecked file
453 : : return true;
454 : : }
455 : : // etc
456 : : }
457 : :
458 : : return false;
459 : : }
460 : :
461 : : /* Implementation of state_machine::on_condition vfunc for
462 : : fileptr_state_machine.
463 : : Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */
464 : :
465 : : void
466 : 30200 : fileptr_state_machine::on_condition (sm_context &sm_ctxt,
467 : : const supernode *node,
468 : : const gimple *stmt,
469 : : const svalue *lhs,
470 : : enum tree_code op,
471 : : const svalue *rhs) const
472 : : {
473 : 30200 : if (!rhs->all_zeroes_p ())
474 : : return;
475 : :
476 : : // TODO: has to be a FILE *, specifically
477 : 19592 : if (!any_pointer_p (lhs))
478 : : return;
479 : : // TODO: has to be a FILE *, specifically
480 : 8048 : if (!any_pointer_p (rhs))
481 : : return;
482 : :
483 : 8048 : if (op == NE_EXPR)
484 : : {
485 : 4421 : log ("got 'ARG != 0' match");
486 : 4421 : sm_ctxt.on_transition (node, stmt,
487 : 4421 : lhs, m_unchecked, m_nonnull);
488 : : }
489 : 3627 : else if (op == EQ_EXPR)
490 : : {
491 : 3627 : log ("got 'ARG == 0' match");
492 : 3627 : sm_ctxt.on_transition (node, stmt,
493 : 3627 : lhs, m_unchecked, m_null);
494 : : }
495 : : }
496 : :
497 : : /* Implementation of state_machine::can_purge_p vfunc for fileptr_state_machine.
498 : : Don't allow purging of pointers in state 'unchecked' or 'nonnull'
499 : : (to avoid false leak reports). */
500 : :
501 : : bool
502 : 1033918 : fileptr_state_machine::can_purge_p (state_t s) const
503 : : {
504 : 1033918 : return s != m_unchecked && s != m_nonnull;
505 : : }
506 : :
507 : : /* Implementation of state_machine::on_leak vfunc for
508 : : fileptr_state_machine, for complaining about leaks of FILE * in
509 : : state 'unchecked' and 'nonnull'. */
510 : :
511 : : std::unique_ptr<pending_diagnostic>
512 : 194 : fileptr_state_machine::on_leak (tree var,
513 : : const program_state *,
514 : : const program_state *new_state) const
515 : : {
516 : 194 : return std::make_unique<file_leak> (*this, var, new_state);
517 : : }
518 : :
519 : : } // anonymous namespace
520 : :
521 : : /* Internal interface to this file. */
522 : :
523 : : std::unique_ptr<state_machine>
524 : 3308 : make_fileptr_state_machine (logger *logger)
525 : : {
526 : 3308 : return std::make_unique<fileptr_state_machine> (logger);
527 : : }
528 : :
529 : : /* Handler for various stdio-related builtins that merely have external
530 : : effects that are out of scope for the analyzer: we only want to model
531 : : the effects on the return value. */
532 : :
533 : 59544 : class kf_stdio_output_fn : public pure_known_function_with_default_return
534 : : {
535 : : public:
536 : 0 : bool matches_call_types_p (const call_details &) const final override
537 : : {
538 : 0 : return true;
539 : : }
540 : :
541 : : /* A no-op; we just want the conjured return value. */
542 : : };
543 : :
544 : : /* Handler for "ferror"". */
545 : :
546 : 6616 : class kf_ferror : public pure_known_function_with_default_return
547 : : {
548 : : public:
549 : 444 : bool matches_call_types_p (const call_details &cd) const final override
550 : : {
551 : 444 : return (cd.num_args () == 1
552 : 444 : && cd.arg_is_pointer_p (0));
553 : : }
554 : :
555 : : /* No side effects. */
556 : : };
557 : :
558 : : /* Handler for "fileno"". */
559 : :
560 : 3308 : class kf_fileno : public pure_known_function_with_default_return
561 : : {
562 : : public:
563 : 608 : bool matches_call_types_p (const call_details &cd) const final override
564 : : {
565 : 608 : return (cd.num_args () == 1
566 : 608 : && cd.arg_is_pointer_p (0));
567 : : }
568 : :
569 : : /* No side effects. */
570 : : };
571 : :
572 : : /* Handler for "fgets" and "fgets_unlocked". */
573 : :
574 : 9924 : class kf_fgets : public known_function
575 : : {
576 : : public:
577 : 1098 : bool matches_call_types_p (const call_details &cd) const final override
578 : : {
579 : 1098 : return (cd.num_args () == 3
580 : 1098 : && cd.arg_is_pointer_p (0)
581 : 2196 : && cd.arg_is_pointer_p (2));
582 : : }
583 : :
584 : 297 : void impl_call_pre (const call_details &cd) const final override
585 : : {
586 : : /* Ideally we would bifurcate state here between the
587 : : error vs no error cases. */
588 : 297 : region_model *model = cd.get_model ();
589 : 297 : const svalue *ptr_sval = cd.get_arg_svalue (0);
590 : 297 : if (const region *reg = ptr_sval->maybe_get_region ())
591 : : {
592 : 297 : const region *base_reg = reg->get_base_region ();
593 : 297 : const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg);
594 : 297 : model->set_value (base_reg, new_sval, cd.get_ctxt ());
595 : : }
596 : 297 : cd.set_any_lhs_with_defaults ();
597 : 297 : }
598 : : };
599 : :
600 : : /* Handler for "fread".
601 : : size_t fread(void *restrict buffer, size_t size, size_t count,
602 : : FILE *restrict stream);
603 : : See e.g. https://en.cppreference.com/w/c/io/fread
604 : : and https://www.man7.org/linux/man-pages/man3/fread.3.html */
605 : :
606 : 6616 : class kf_fread : public known_function
607 : : {
608 : : public:
609 : 1446 : bool matches_call_types_p (const call_details &cd) const final override
610 : : {
611 : 1446 : return (cd.num_args () == 4
612 : 1446 : && cd.arg_is_pointer_p (0)
613 : 1446 : && cd.arg_is_size_p (1)
614 : 1446 : && cd.arg_is_size_p (2)
615 : 2892 : && cd.arg_is_pointer_p (3));
616 : : }
617 : :
618 : : /* For now, assume that any call to "fread" fully clobbers the buffer
619 : : passed in. This isn't quite correct (e.g. errors, partial reads;
620 : : see PR analyzer/108689), but at least stops us falsely complaining
621 : : about the buffer being uninitialized. */
622 : 321 : void impl_call_pre (const call_details &cd) const final override
623 : : {
624 : 321 : region_model *model = cd.get_model ();
625 : 321 : const svalue *ptr_sval = cd.get_arg_svalue (0);
626 : 321 : if (const region *reg = ptr_sval->maybe_get_region ())
627 : : {
628 : 269 : const region *base_reg = reg->get_base_region ();
629 : 269 : const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg);
630 : 269 : model->set_value (base_reg, new_sval, cd.get_ctxt ());
631 : : }
632 : 321 : cd.set_any_lhs_with_defaults ();
633 : 321 : }
634 : : };
635 : :
636 : : /* Handler for "getc"". */
637 : :
638 : 6616 : class kf_getc : public pure_known_function_with_default_return
639 : : {
640 : : public:
641 : 704 : bool matches_call_types_p (const call_details &cd) const final override
642 : : {
643 : 704 : return (cd.num_args () == 1
644 : 704 : && cd.arg_is_pointer_p (0));
645 : : }
646 : : };
647 : :
648 : : /* Handler for "getchar"". */
649 : :
650 : 6616 : class kf_getchar : public pure_known_function_with_default_return
651 : : {
652 : : public:
653 : 48 : bool matches_call_types_p (const call_details &cd) const final override
654 : : {
655 : 48 : return cd.num_args () == 0;
656 : : }
657 : :
658 : : /* Empty. No side-effects (tracking stream state is out-of-scope
659 : : for the analyzer). */
660 : : };
661 : :
662 : : /* Populate KFM with instances of known functions relating to
663 : : stdio streams. */
664 : :
665 : : void
666 : 3308 : register_known_file_functions (known_function_manager &kfm)
667 : : {
668 : 3308 : kfm.add (BUILT_IN_FPRINTF, std::make_unique<kf_stdio_output_fn> ());
669 : 3308 : kfm.add (BUILT_IN_FPRINTF_UNLOCKED, std::make_unique<kf_stdio_output_fn> ());
670 : 3308 : kfm.add (BUILT_IN_FPUTC, std::make_unique<kf_stdio_output_fn> ());
671 : 3308 : kfm.add (BUILT_IN_FPUTC_UNLOCKED, std::make_unique<kf_stdio_output_fn> ());
672 : 3308 : kfm.add (BUILT_IN_FPUTS, std::make_unique<kf_stdio_output_fn> ());
673 : 3308 : kfm.add (BUILT_IN_FPUTS_UNLOCKED, std::make_unique<kf_stdio_output_fn> ());
674 : 3308 : kfm.add (BUILT_IN_FWRITE, std::make_unique<kf_stdio_output_fn> ());
675 : 3308 : kfm.add (BUILT_IN_FWRITE_UNLOCKED, std::make_unique<kf_stdio_output_fn> ());
676 : 3308 : kfm.add (BUILT_IN_PRINTF, std::make_unique<kf_stdio_output_fn> ());
677 : 3308 : kfm.add (BUILT_IN_PRINTF_UNLOCKED, std::make_unique<kf_stdio_output_fn> ());
678 : 3308 : kfm.add (BUILT_IN_PUTC, std::make_unique<kf_stdio_output_fn> ());
679 : 3308 : kfm.add (BUILT_IN_PUTCHAR, std::make_unique<kf_stdio_output_fn> ());
680 : 3308 : kfm.add (BUILT_IN_PUTCHAR_UNLOCKED, std::make_unique<kf_stdio_output_fn> ());
681 : 3308 : kfm.add (BUILT_IN_PUTC_UNLOCKED, std::make_unique<kf_stdio_output_fn> ());
682 : 3308 : kfm.add (BUILT_IN_PUTS, std::make_unique<kf_stdio_output_fn> ());
683 : 3308 : kfm.add (BUILT_IN_PUTS_UNLOCKED, std::make_unique<kf_stdio_output_fn> ());
684 : 3308 : kfm.add (BUILT_IN_VFPRINTF, std::make_unique<kf_stdio_output_fn> ());
685 : 3308 : kfm.add (BUILT_IN_VPRINTF, std::make_unique<kf_stdio_output_fn> ());
686 : :
687 : 3308 : kfm.add ("ferror", std::make_unique<kf_ferror> ());
688 : 3308 : kfm.add ("fgets", std::make_unique<kf_fgets> ());
689 : 3308 : kfm.add ("fgets_unlocked", std::make_unique<kf_fgets> ()); // non-standard
690 : 3308 : kfm.add ("fileno", std::make_unique<kf_fileno> ());
691 : 3308 : kfm.add ("fread", std::make_unique<kf_fread> ());
692 : 3308 : kfm.add ("getc", std::make_unique<kf_getc> ());
693 : 3308 : kfm.add ("getchar", std::make_unique<kf_getchar> ());
694 : :
695 : : /* Some C++ implementations use the std:: copies of these functions
696 : : from <cstdio> for <stdio.h>, so we must match against these too. */
697 : 3308 : kfm.add_std_ns ("ferror", std::make_unique<kf_ferror> ());
698 : 3308 : kfm.add_std_ns ("fgets", std::make_unique<kf_fgets> ());
699 : 3308 : kfm.add_std_ns ("fread", std::make_unique<kf_fread> ());
700 : 3308 : kfm.add_std_ns ("getc", std::make_unique<kf_getc> ());
701 : 3308 : kfm.add_std_ns ("getchar", std::make_unique<kf_getchar> ());
702 : 3308 : }
703 : :
704 : : #if CHECKING_P
705 : :
706 : : namespace selftest {
707 : :
708 : : /* Run all of the selftests within this file. */
709 : :
710 : : void
711 : 4 : analyzer_sm_file_cc_tests ()
712 : : {
713 : 4 : function_set fs = get_file_using_fns ();
714 : 4 : fs.assert_sorted ();
715 : 4 : fs.assert_sane ();
716 : 4 : }
717 : :
718 : : } // namespace selftest
719 : :
720 : : #endif /* CHECKING_P */
721 : :
722 : : } // namespace ana
723 : :
724 : : #endif /* #if ENABLE_ANALYZER */
|