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