LCOV - code coverage report
Current view: top level - gcc/analyzer - sm-file.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 92.3 % 235 217
Test Date: 2026-02-28 14:20:25 Functions: 97.2 % 36 35
Legend: Lines:     hit not hit

            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 */
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.