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