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