LCOV - code coverage report
Current view: top level - gcc/analyzer - poisoned-value-diagnostic.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 92.9 % 85 79
Test Date: 2026-05-11 19:44:49 Functions: 100.0 % 10 10
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Implementation of class ana::poisoned_value_diagnostic.
       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 "analyzer/region-model.h"
      24              : #include "analyzer/feasible-graph.h"
      25              : #include "diagnostics/sarif-sink.h"
      26              : 
      27              : #if ENABLE_ANALYZER
      28              : 
      29              : namespace ana {
      30              : 
      31              : /* A subclass of pending_diagnostic for complaining about uses of
      32              :    poisoned values.  */
      33              : 
      34              : class poisoned_value_diagnostic
      35              : : public pending_diagnostic_subclass<poisoned_value_diagnostic>
      36              : {
      37              : public:
      38         2261 :   poisoned_value_diagnostic (tree expr, enum poison_kind pkind,
      39              :                              const region *src_region,
      40              :                              tree check_expr)
      41         2261 :   : m_expr (expr), m_pkind (pkind),
      42         2261 :     m_src_region (src_region),
      43         2261 :     m_check_expr (check_expr)
      44              :   {}
      45              : 
      46        14459 :   const char *get_kind () const final override { return "poisoned_value_diagnostic"; }
      47              : 
      48           73 :   bool use_of_uninit_p () const final override
      49              :   {
      50           73 :     return m_pkind == poison_kind::uninit;
      51              :   }
      52              : 
      53         1368 :   bool operator== (const poisoned_value_diagnostic &other) const
      54              :   {
      55         1368 :     return (m_expr == other.m_expr
      56         1343 :             && m_pkind == other.m_pkind
      57         2711 :             && m_src_region == other.m_src_region);
      58              :   }
      59              : 
      60         1966 :   int get_controlling_option () const final override
      61              :   {
      62         1966 :     switch (m_pkind)
      63              :       {
      64            0 :       default:
      65            0 :         gcc_unreachable ();
      66              :       case poison_kind::uninit:
      67              :         return OPT_Wanalyzer_use_of_uninitialized_value;
      68              :       case poison_kind::freed:
      69              :       case poison_kind::deleted:
      70              :         return OPT_Wanalyzer_use_after_free;
      71              :       case poison_kind::popped_stack:
      72              :         return OPT_Wanalyzer_use_of_pointer_in_stale_stack_frame;
      73              :       }
      74              :   }
      75              : 
      76         1389 :   bool terminate_path_p () const final override { return true; }
      77              : 
      78          577 :   bool emit (diagnostic_emission_context &ctxt) final override
      79              :   {
      80          577 :     switch (m_pkind)
      81              :       {
      82            0 :       default:
      83            0 :         gcc_unreachable ();
      84          550 :       case poison_kind::uninit:
      85          550 :         {
      86          550 :           ctxt.add_cwe (457); /* "CWE-457: Use of Uninitialized Variable".  */
      87          550 :           return ctxt.warn ("use of uninitialized value %qE",
      88          550 :                             m_expr);
      89              :         }
      90            3 :         break;
      91            3 :       case poison_kind::freed:
      92            3 :         {
      93            3 :           ctxt.add_cwe (416); /* "CWE-416: Use After Free".  */
      94            3 :           return ctxt.warn ("use after %<free%> of %qE",
      95            3 :                             m_expr);
      96              :         }
      97            9 :         break;
      98            9 :       case poison_kind::deleted:
      99            9 :         {
     100            9 :           ctxt.add_cwe (416); /* "CWE-416: Use After Free".  */
     101            9 :           return ctxt.warn ("use after %<delete%> of %qE",
     102            9 :                             m_expr);
     103              :         }
     104           15 :         break;
     105           15 :       case poison_kind::popped_stack:
     106           15 :         {
     107              :           /* TODO: which CWE?  */
     108           15 :           return ctxt.warn
     109           15 :             ("dereferencing pointer %qE to within stale stack frame",
     110           15 :              m_expr);
     111              :         }
     112              :         break;
     113              :       }
     114              :   }
     115              : 
     116              :   bool
     117         1154 :   describe_final_event (pretty_printer &pp,
     118              :                         const evdesc::final_event &) final override
     119              :   {
     120         1154 :     switch (m_pkind)
     121              :       {
     122            0 :       default:
     123            0 :         gcc_unreachable ();
     124         1100 :       case poison_kind::uninit:
     125         1100 :         {
     126         1100 :           pp_printf (&pp,
     127              :                      "use of uninitialized value %qE here",
     128              :                      m_expr);
     129         1100 :           return true;
     130              :         }
     131            6 :       case poison_kind::freed:
     132            6 :         {
     133            6 :           pp_printf (&pp,
     134              :                      "use after %<free%> of %qE here",
     135              :                      m_expr);
     136            6 :           return true;
     137              :         }
     138           18 :       case poison_kind::deleted:
     139           18 :         {
     140           18 :           pp_printf (&pp,
     141              :                      "use after %<delete%> of %qE here",
     142              :                      m_expr);
     143           18 :           return true;
     144              :         }
     145           30 :       case poison_kind::popped_stack:
     146           30 :         {
     147           30 :           pp_printf (&pp,
     148              :                      "dereferencing pointer %qE to within stale stack frame",
     149              :                      m_expr);
     150           30 :           return true;
     151              :         }
     152              :       }
     153              :   }
     154              : 
     155         1154 :   void mark_interesting_stuff (interesting_t *interest) final override
     156              :   {
     157         1154 :     if (m_src_region)
     158              :       {
     159         1084 :         interest->add_region_creation (m_src_region);
     160         1084 :         interest->add_read_region (m_src_region, "poisoned value");
     161              :       }
     162         1154 :   }
     163              : 
     164              :   /* Attempt to suppress false positives.
     165              :      Reject paths where the value of the underlying region isn't poisoned.
     166              :      This can happen due to state merging when exploring the exploded graph,
     167              :      where the more precise analysis during feasibility analysis finds that
     168              :      the region is in fact valid.
     169              :      To do this we need to get the value from the fgraph.  Unfortunately
     170              :      we can't simply query the state of m_src_region (from the enode),
     171              :      since it might be a different region in the fnode state (e.g. with
     172              :      heap-allocated regions, the numbering could be different).
     173              :      Hence we access m_check_expr, if available.  */
     174              : 
     175         1325 :   bool check_valid_fpath_p (const feasible_node &fnode)
     176              :     const final override
     177              :   {
     178         1325 :     if (!m_check_expr)
     179              :       return true;
     180         1217 :     const svalue *fsval = fnode.get_model ().get_rvalue (m_check_expr, nullptr);
     181              :     /* Check to see if the expr is also poisoned in FNODE (and in the
     182              :        same way).  */
     183         1217 :     const poisoned_svalue * fspval = fsval->dyn_cast_poisoned_svalue ();
     184         1217 :     if (!fspval)
     185              :       return false;
     186         1217 :     if (fspval->get_poison_kind () != m_pkind)
     187              :       return false;
     188              :     return true;
     189              :   }
     190              : 
     191              :   void
     192            8 :   maybe_add_sarif_properties (diagnostics::sarif_object &result_obj)
     193              :     const final override
     194              :   {
     195            8 :     auto &props = result_obj.get_or_create_properties ();
     196              : #define PROPERTY_PREFIX "gcc/analyzer/poisoned_value_diagnostic/"
     197            8 :     props.set (PROPERTY_PREFIX "expr", tree_to_json (m_expr));
     198            8 :     props.set_string (PROPERTY_PREFIX "kind", poison_kind_to_str (m_pkind));
     199            8 :     if (m_src_region)
     200            8 :       props.set (PROPERTY_PREFIX "src_region", m_src_region->to_json ());
     201            8 :     props.set (PROPERTY_PREFIX "check_expr", tree_to_json (m_check_expr));
     202              : #undef PROPERTY_PREFIX
     203            8 :   }
     204              : 
     205              : private:
     206              :   tree m_expr;
     207              :   enum poison_kind m_pkind;
     208              :   const region *m_src_region;
     209              :   tree m_check_expr;
     210              : };
     211              : 
     212              : std::unique_ptr<pending_diagnostic>
     213         2261 : make_poisoned_value_diagnostic (tree expr, enum poison_kind pkind,
     214              :                                 const region *src_region,
     215              :                                 tree check_expr)
     216              : {
     217         2261 :   return std::make_unique<poisoned_value_diagnostic> (expr, pkind,
     218         2261 :                                                       src_region, check_expr);
     219              : }
     220              : 
     221              : } // namespace ana
     222              : 
     223              : #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.