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