LCOV - code coverage report
Current view: top level - gcc/analyzer - bounds-checking.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 80.7 % 680 549
Test Date: 2026-02-28 14:20:25 Functions: 90.7 % 54 49
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /* Bounds-checking of reads and writes to memory regions.
       2              :    Copyright (C) 2019-2026 Free Software Foundation, Inc.
       3              : 
       4              : This file is part of GCC.
       5              : 
       6              : GCC is free software; you can redistribute it and/or modify it
       7              : under the terms of the GNU General Public License as published by
       8              : the Free Software Foundation; either version 3, or (at your option)
       9              : any later version.
      10              : 
      11              : GCC is distributed in the hope that it will be useful, but
      12              : WITHOUT ANY WARRANTY; without even the implied warranty of
      13              : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14              : General Public License for more details.
      15              : 
      16              : You should have received a copy of the GNU General Public License
      17              : along with GCC; see the file COPYING3.  If not see
      18              : <http://www.gnu.org/licenses/>.  */
      19              : 
      20              : #include "analyzer/common.h"
      21              : 
      22              : #include "intl.h"
      23              : #include "diagnostics/diagram.h"
      24              : #include "diagnostics/sarif-sink.h"
      25              : 
      26              : #include "analyzer/analyzer-logging.h"
      27              : #include "analyzer/region-model.h"
      28              : #include "analyzer/checker-event.h"
      29              : #include "analyzer/checker-path.h"
      30              : #include "analyzer/access-diagram.h"
      31              : 
      32              : #if ENABLE_ANALYZER
      33              : 
      34              : namespace ana {
      35              : 
      36              : /* Abstract base class for all out-of-bounds warnings.  */
      37              : 
      38              : class out_of_bounds : public pending_diagnostic
      39              : {
      40              : public:
      41              :   class oob_region_creation_event_capacity : public region_creation_event_capacity
      42              :   {
      43              :   public:
      44          439 :     oob_region_creation_event_capacity (tree byte_capacity,
      45              :                                         const event_loc_info &loc_info,
      46              :                                         out_of_bounds &oob)
      47          439 :     : region_creation_event_capacity (byte_capacity,
      48              :                                       loc_info),
      49          439 :       m_oob (oob)
      50              :     {
      51              :     }
      52          439 :     void prepare_for_emission (checker_path *path,
      53              :                                pending_diagnostic *pd,
      54              :                                diagnostics::paths::event_id_t emission_id) override
      55              :     {
      56          439 :       region_creation_event_capacity::prepare_for_emission (path,
      57              :                                                             pd,
      58              :                                                             emission_id);
      59          439 :       m_oob.m_region_creation_event_id = emission_id;
      60          439 :     }
      61              :   private:
      62              :     out_of_bounds &m_oob;
      63              :   };
      64              : 
      65          770 :   out_of_bounds (const region_model &model,
      66              :                  const region *reg,
      67              :                  tree diag_arg,
      68              :                  const svalue *sval_hint)
      69          429 :   : m_model (model), m_reg (reg), m_diag_arg (diag_arg), m_sval_hint (sval_hint)
      70              :   {}
      71              : 
      72          711 :   bool subclass_equal_p (const pending_diagnostic &base_other) const override
      73              :   {
      74          711 :     const out_of_bounds &other
      75              :       (static_cast <const out_of_bounds &>(base_other));
      76          711 :     return (m_reg == other.m_reg
      77          711 :             && pending_diagnostic::same_tree_p (m_diag_arg, other.m_diag_arg));
      78              :   }
      79              : 
      80         1299 :   int get_controlling_option () const final override
      81              :   {
      82         1299 :     return OPT_Wanalyzer_out_of_bounds;
      83              :   }
      84              : 
      85          554 :   void mark_interesting_stuff (interesting_t *interest) final override
      86              :   {
      87          554 :     interest->add_region_creation (m_reg->get_base_region ());
      88          554 :   }
      89              : 
      90          206 :   void add_region_creation_events (const region *,
      91              :                                    tree byte_capacity,
      92              :                                    const event_loc_info &loc_info,
      93              :                                    checker_path &emission_path) override
      94              :   {
      95              :     /* The memory space is described in the diagnostic message itself,
      96              :        so we don't need an event for that.  */
      97          206 :     if (byte_capacity)
      98          132 :       emission_path.add_event
      99          132 :         (std::make_unique<oob_region_creation_event_capacity> (byte_capacity,
     100              :                                                                loc_info,
     101              :                                                                *this));
     102          206 :   }
     103              : 
     104              :   void
     105            4 :   maybe_add_sarif_properties (diagnostics::sarif_object &result_obj)
     106              :     const override
     107              :   {
     108            4 :     auto &props = result_obj.get_or_create_properties ();
     109              : #define PROPERTY_PREFIX "gcc/analyzer/out_of_bounds/"
     110            8 :     props.set_string (PROPERTY_PREFIX "dir",
     111            4 :                       get_dir () == access_direction::read ? "read" : "write");
     112            4 :     props.set (PROPERTY_PREFIX "model", m_model.to_json ());
     113            4 :     props.set (PROPERTY_PREFIX "region", m_reg->to_json ());
     114            4 :     props.set (PROPERTY_PREFIX "diag_arg", tree_to_json (m_diag_arg));
     115            4 :     if (m_sval_hint)
     116            4 :       props.set (PROPERTY_PREFIX "sval_hint", m_sval_hint->to_json ());
     117            8 :     props.set (PROPERTY_PREFIX "region_creation_event_id",
     118            4 :                diagnostic_event_id_to_json (m_region_creation_event_id));
     119              : #undef PROPERTY_PREFIX
     120            4 :   }
     121              : 
     122              :   virtual enum access_direction get_dir () const = 0;
     123              : 
     124              : protected:
     125          554 :   enum memory_space get_memory_space () const
     126              :   {
     127          554 :     return m_reg->get_memory_space ();
     128              :   }
     129              : 
     130              :   void
     131          554 :   maybe_show_notes (diagnostic_emission_context &ctxt) const
     132              :   {
     133          554 :     maybe_describe_array_bounds (ctxt.get_location ());
     134          554 :     maybe_show_diagram (ctxt.get_logger ());
     135          554 :   }
     136              : 
     137              :   /* Potentially add a note about valid ways to index this array, such
     138              :      as (given "int arr[10];"):
     139              :        note: valid subscripts for 'arr' are '[0]' to '[9]'
     140              :      We print the '[' and ']' characters so as to express the valid
     141              :      subscripts using C syntax, rather than just as byte ranges,
     142              :      which hopefully is more clear to the user.  */
     143              :   void
     144          554 :   maybe_describe_array_bounds (location_t loc) const
     145              :   {
     146          554 :     if (!m_diag_arg)
     147              :       return;
     148          397 :     tree t = TREE_TYPE (m_diag_arg);
     149          397 :     if (!t)
     150              :       return;
     151          397 :     if (TREE_CODE (t) != ARRAY_TYPE)
     152              :       return;
     153          354 :     tree domain = TYPE_DOMAIN (t);
     154          354 :     if (!domain)
     155              :       return;
     156          353 :     tree max_idx = TYPE_MAX_VALUE (domain);
     157          353 :     if (!max_idx)
     158              :       return;
     159          351 :     tree min_idx = TYPE_MIN_VALUE (domain);
     160          351 :     inform (loc,
     161              :             "valid subscripts for %qE are %<[%E]%> to %<[%E]%>",
     162              :             m_diag_arg, min_idx, max_idx);
     163              :   }
     164              : 
     165              :   void
     166          554 :   maybe_show_diagram (logger *logger) const
     167              :   {
     168          554 :     access_operation op (m_model, get_dir (), *m_reg, m_sval_hint);
     169              : 
     170              :     /* Don't attempt to make a diagram if there's no valid way of
     171              :        accessing the base region (e.g. a 0-element array).  */
     172          554 :     if (op.get_valid_bits ().empty_p ())
     173            8 :       return;
     174              : 
     175          550 :     if (const text_art::theme *theme = global_dc->get_diagram_theme ())
     176              :       {
     177           72 :         text_art::style_manager sm;
     178           72 :         text_art::canvas canvas (make_access_diagram (op, sm, *theme, logger));
     179           72 :         if (canvas.get_size ().w == 0 && canvas.get_size ().h == 0)
     180              :           {
     181              :             /* In lieu of exceptions, return a zero-sized diagram if there's
     182              :                a problem.  Give up if that's happened.  */
     183            4 :             return;
     184              :           }
     185           68 :         diagnostics::diagram diagram
     186              :           (canvas,
     187              :            /* Alt text.  */
     188           68 :            _("Diagram visualizing the predicted out-of-bounds access"));
     189           68 :         global_dc->emit_diagram (diagram);
     190           72 :       }
     191              :   }
     192              : 
     193              :   text_art::canvas
     194           72 :   make_access_diagram (const access_operation &op,
     195              :                        text_art::style_manager &sm,
     196              :                        const text_art::theme &theme,
     197              :                        logger *logger) const
     198              :   {
     199           72 :     access_diagram d (op, m_region_creation_event_id, sm, theme, logger);
     200           72 :     return d.to_canvas (sm);
     201           72 :   }
     202              : 
     203              :   region_model m_model;
     204              :   const region *m_reg;
     205              :   tree m_diag_arg;
     206              :   const svalue *m_sval_hint;
     207              :   diagnostics::paths::event_id_t m_region_creation_event_id;
     208              : };
     209              : 
     210              : /* Abstract base class for all out-of-bounds warnings where the
     211              :    out-of-bounds range is concrete.  */
     212              : 
     213              : class concrete_out_of_bounds : public out_of_bounds
     214              : {
     215              : public:
     216          621 :   concrete_out_of_bounds (const region_model &model,
     217              :                           const region *reg, tree diag_arg,
     218              :                           bit_range out_of_bounds_bits,
     219              :                           const svalue *sval_hint)
     220          621 :   : out_of_bounds (model, reg, diag_arg, sval_hint),
     221         1242 :     m_out_of_bounds_bits (out_of_bounds_bits)
     222              :   {}
     223              : 
     224          566 :   bool subclass_equal_p (const pending_diagnostic &base_other) const override
     225              :   {
     226          566 :     const concrete_out_of_bounds &other
     227              :       (static_cast <const concrete_out_of_bounds &>(base_other));
     228          566 :     return (out_of_bounds::subclass_equal_p (other)
     229          566 :             && m_out_of_bounds_bits == other.m_out_of_bounds_bits);
     230              :   }
     231              : 
     232            4 :   void maybe_add_sarif_properties (diagnostics::sarif_object &result_obj)
     233              :     const override
     234              :   {
     235            4 :     out_of_bounds::maybe_add_sarif_properties (result_obj);
     236            4 :     auto &props = result_obj.get_or_create_properties ();
     237              : #define PROPERTY_PREFIX "gcc/analyzer/concrete_out_of_bounds/"
     238            8 :     props.set (PROPERTY_PREFIX "out_of_bounds_bits",
     239            4 :                m_out_of_bounds_bits.to_json ());
     240            4 :     byte_range out_of_bounds_bytes (0, 0);
     241            4 :     if (get_out_of_bounds_bytes (&out_of_bounds_bytes))
     242            8 :       props.set (PROPERTY_PREFIX "out_of_bounds_bytes",
     243            4 :                  out_of_bounds_bytes.to_json ());
     244              : #undef PROPERTY_PREFIX
     245            4 :   }
     246              : 
     247          848 :   bool get_out_of_bounds_bytes (byte_range *out) const
     248              :   {
     249            4 :     return m_out_of_bounds_bits.as_byte_range (out);
     250              :   }
     251              : 
     252              : protected:
     253              :   bit_range m_out_of_bounds_bits;
     254              : };
     255              : 
     256              : /* Abstract subclass to complaing about concrete out-of-bounds
     257              :    past the end of the buffer.  */
     258              : 
     259              : class concrete_past_the_end : public concrete_out_of_bounds
     260              : {
     261              : public:
     262          429 :   concrete_past_the_end (const region_model &model,
     263              :                          const region *reg, tree diag_arg, bit_range range,
     264              :                          tree bit_bound,
     265              :                          const svalue *sval_hint)
     266          429 :   : concrete_out_of_bounds (model, reg, diag_arg, range, sval_hint),
     267          429 :     m_bit_bound (bit_bound),
     268          429 :     m_byte_bound (NULL_TREE)
     269              :   {
     270          429 :     if (m_bit_bound && TREE_CODE (m_bit_bound) == INTEGER_CST)
     271          429 :       m_byte_bound
     272          858 :         = wide_int_to_tree (size_type_node,
     273          429 :                             wi::to_offset (m_bit_bound) >> LOG2_BITS_PER_UNIT);
     274          429 :   }
     275              : 
     276              :   bool
     277          402 :   subclass_equal_p (const pending_diagnostic &base_other) const final override
     278              :   {
     279          402 :     const concrete_past_the_end &other
     280              :       (static_cast <const concrete_past_the_end &>(base_other));
     281          402 :     return (concrete_out_of_bounds::subclass_equal_p (other)
     282          804 :             && pending_diagnostic::same_tree_p (m_bit_bound,
     283          402 :                                                 other.m_bit_bound));
     284              :   }
     285              : 
     286          307 :   void add_region_creation_events (const region *,
     287              :                                    tree,
     288              :                                    const event_loc_info &loc_info,
     289              :                                    checker_path &emission_path) final override
     290              :   {
     291          307 :     if (m_byte_bound && TREE_CODE (m_byte_bound) == INTEGER_CST)
     292          307 :       emission_path.add_event
     293          307 :         (std::make_unique<oob_region_creation_event_capacity> (m_byte_bound,
     294              :                                                                loc_info,
     295              :                                                                *this));
     296          307 :   }
     297              : 
     298            4 :   void maybe_add_sarif_properties (diagnostics::sarif_object &result_obj)
     299              :     const final override
     300              :   {
     301            4 :     concrete_out_of_bounds::maybe_add_sarif_properties (result_obj);
     302            4 :     auto &props = result_obj.get_or_create_properties ();
     303              : #define PROPERTY_PREFIX "gcc/analyzer/concrete_past_the_end/"
     304            8 :     props.set (PROPERTY_PREFIX "bit_bound",
     305            4 :                tree_to_json (m_bit_bound));
     306            8 :     props.set (PROPERTY_PREFIX "byte_bound",
     307            4 :                tree_to_json (m_byte_bound));
     308              : #undef PROPERTY_PREFIX
     309            4 :   }
     310              : 
     311              : protected:
     312              :   tree m_bit_bound;
     313              :   tree m_byte_bound;
     314              : };
     315              : 
     316              : /* Concrete subclass to complain about buffer overflows.  */
     317              : 
     318              : class concrete_buffer_overflow : public concrete_past_the_end
     319              : {
     320              : public:
     321          178 :   concrete_buffer_overflow (const region_model &model,
     322              :                             const region *reg, tree diag_arg,
     323              :                             bit_range range, tree bit_bound,
     324              :                             const svalue *sval_hint)
     325          178 :   : concrete_past_the_end (model, reg, diag_arg, range, bit_bound, sval_hint)
     326              :   {}
     327              : 
     328         1820 :   const char *get_kind () const final override
     329              :   {
     330         1820 :     return "concrete_buffer_overflow";
     331              :   }
     332              : 
     333          156 :   bool emit (diagnostic_emission_context &ctxt) final override
     334              :   {
     335          156 :     bool warned;
     336          156 :     switch (get_memory_space ())
     337              :       {
     338           54 :       default:
     339           54 :         ctxt.add_cwe (787);
     340           54 :         warned = ctxt.warn ("buffer overflow");
     341           54 :         break;
     342           73 :       case MEMSPACE_STACK:
     343           73 :         ctxt.add_cwe (121);
     344           73 :         warned = ctxt.warn ("stack-based buffer overflow");
     345           73 :         break;
     346           29 :       case MEMSPACE_HEAP:
     347           29 :         ctxt.add_cwe (122);
     348           29 :         warned = ctxt.warn ("heap-based buffer overflow");
     349           29 :         break;
     350              :       }
     351              : 
     352          156 :     if (warned)
     353              :       {
     354          156 :         if (wi::fits_uhwi_p (m_out_of_bounds_bits.m_size_in_bits))
     355              :           {
     356          156 :             unsigned HOST_WIDE_INT num_bad_bits
     357          156 :               = m_out_of_bounds_bits.m_size_in_bits.to_uhwi ();
     358          156 :             if (num_bad_bits % BITS_PER_UNIT == 0)
     359              :               {
     360          156 :                 unsigned HOST_WIDE_INT num_bad_bytes
     361              :                   = num_bad_bits / BITS_PER_UNIT;
     362          156 :                 if (m_diag_arg)
     363          124 :                   inform_n (ctxt.get_location (),
     364              :                             num_bad_bytes,
     365              :                             "write of %wu byte to beyond the end of %qE",
     366              :                             "write of %wu bytes to beyond the end of %qE",
     367              :                             num_bad_bytes,
     368              :                             m_diag_arg);
     369              :                 else
     370           32 :                   inform_n (ctxt.get_location (),
     371              :                             num_bad_bytes,
     372              :                             "write of %wu byte to beyond the end of the region",
     373              :                             "write of %wu bytes to beyond the end of the region",
     374              :                             num_bad_bytes);
     375              :               }
     376              :             else
     377              :               {
     378            0 :                 if (m_diag_arg)
     379            0 :                   inform_n (ctxt.get_location (),
     380              :                             num_bad_bits,
     381              :                             "write of %wu bit to beyond the end of %qE",
     382              :                             "write of %wu bits to beyond the end of %qE",
     383              :                             num_bad_bits,
     384              :                             m_diag_arg);
     385              :                 else
     386            0 :                   inform_n (ctxt.get_location (),
     387              :                             num_bad_bits,
     388              :                             "write of %wu bit to beyond the end of the region",
     389              :                             "write of %wu bits to beyond the end of the region",
     390              :                             num_bad_bits);
     391              :               }
     392              :           }
     393            0 :         else if (m_diag_arg)
     394            0 :           inform (ctxt.get_location (),
     395              :                   "write to beyond the end of %qE",
     396              :                   m_diag_arg);
     397              : 
     398          156 :         maybe_show_notes (ctxt);
     399              :       }
     400              : 
     401          156 :     return warned;
     402              :   }
     403              : 
     404              :   bool
     405          312 :   describe_final_event (pretty_printer &pp,
     406              :                         const evdesc::final_event &) final override
     407              :   {
     408          312 :     if (m_byte_bound || !m_bit_bound)
     409              :       {
     410          312 :         byte_range out_of_bounds_bytes (0, 0);
     411          312 :         if (get_out_of_bounds_bytes (&out_of_bounds_bytes))
     412              :           {
     413          312 :             describe_final_event_as_bytes (pp, out_of_bounds_bytes);
     414          312 :             return true;
     415              :           }
     416              :       }
     417            0 :     describe_final_event_as_bits (pp);
     418            0 :     return true;
     419              :   }
     420              : 
     421              :   void
     422          312 :   describe_final_event_as_bytes (pretty_printer &pp,
     423              :                                  const byte_range &out_of_bounds_bytes)
     424              :   {
     425          312 :     byte_size_t start = out_of_bounds_bytes.get_start_byte_offset ();
     426          312 :     byte_size_t end = out_of_bounds_bytes.get_last_byte_offset ();
     427          312 :     char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
     428          312 :     print_dec (start, start_buf, SIGNED);
     429          312 :     char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
     430          312 :     print_dec (end, end_buf, SIGNED);
     431              : 
     432          312 :     if (start == end)
     433              :       {
     434           84 :         if (m_diag_arg)
     435           56 :           pp_printf (&pp,
     436              :                      "out-of-bounds write at byte %s but %qE"
     437              :                      " ends at byte %E", start_buf, m_diag_arg,
     438              :                      m_byte_bound);
     439              :         else
     440           28 :           pp_printf (&pp,
     441              :                      "out-of-bounds write at byte %s but region"
     442              :                      " ends at byte %E", start_buf,
     443              :                      m_byte_bound);
     444              :       }
     445              :     else
     446              :       {
     447          228 :         if (m_diag_arg)
     448          192 :           pp_printf (&pp,
     449              :                      "out-of-bounds write from byte %s till"
     450              :                      " byte %s but %qE ends at byte %E",
     451              :                      start_buf, end_buf, m_diag_arg,
     452              :                      m_byte_bound);
     453              :         else
     454           36 :           pp_printf (&pp,
     455              :                      "out-of-bounds write from byte %s till"
     456              :                      " byte %s but region ends at byte %E",
     457              :                      start_buf, end_buf, m_byte_bound);
     458              :       }
     459          312 :   }
     460              : 
     461              :   void
     462            0 :   describe_final_event_as_bits (pretty_printer &pp)
     463              :   {
     464            0 :     bit_size_t start = m_out_of_bounds_bits.get_start_bit_offset ();
     465            0 :     bit_size_t end = m_out_of_bounds_bits.get_last_bit_offset ();
     466            0 :     char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
     467            0 :     print_dec (start, start_buf, SIGNED);
     468            0 :     char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
     469            0 :     print_dec (end, end_buf, SIGNED);
     470              : 
     471            0 :     if (start == end)
     472              :       {
     473            0 :         if (m_diag_arg)
     474            0 :           pp_printf (&pp,
     475              :                      "out-of-bounds write at bit %s but %qE"
     476              :                      " ends at bit %E", start_buf, m_diag_arg,
     477              :                      m_bit_bound);
     478              :         else
     479            0 :           pp_printf (&pp,
     480              :                      "out-of-bounds write at bit %s but region"
     481              :                      " ends at bit %E", start_buf,
     482              :                      m_bit_bound);
     483              :       }
     484              :     else
     485              :       {
     486            0 :         if (m_diag_arg)
     487            0 :           pp_printf (&pp,
     488              :                      "out-of-bounds write from bit %s till"
     489              :                      " bit %s but %qE ends at bit %E",
     490              :                      start_buf, end_buf, m_diag_arg,
     491              :                      m_bit_bound);
     492              :         else
     493            0 :           pp_printf (&pp,
     494              :                      "out-of-bounds write from bit %s till"
     495              :                      " bit %s but region ends at bit %E",
     496              :                      start_buf, end_buf, m_bit_bound);
     497              :       }
     498            0 :   }
     499              : 
     500          160 :   enum access_direction get_dir () const final override { return access_direction::write; }
     501              : };
     502              : 
     503              : /* Concrete subclass to complain about buffer over-reads.  */
     504              : 
     505              : class concrete_buffer_over_read : public concrete_past_the_end
     506              : {
     507              : public:
     508          251 :   concrete_buffer_over_read (const region_model &model,
     509              :                              const region *reg, tree diag_arg,
     510              :                              bit_range range, tree bit_bound)
     511          251 :   : concrete_past_the_end (model, reg, diag_arg, range, bit_bound, nullptr)
     512              :   {}
     513              : 
     514         1970 :   const char *get_kind () const final override
     515              :   {
     516         1970 :     return "concrete_buffer_over_read";
     517              :   }
     518              : 
     519          154 :   bool emit (diagnostic_emission_context &ctxt) final override
     520              :   {
     521          154 :     bool warned;
     522          154 :     ctxt.add_cwe (126);
     523          154 :     switch (get_memory_space ())
     524              :       {
     525           44 :       default:
     526           44 :         warned = ctxt.warn ("buffer over-read");
     527           44 :         break;
     528          102 :       case MEMSPACE_STACK:
     529          102 :         warned = ctxt.warn ("stack-based buffer over-read");
     530          102 :         break;
     531            8 :       case MEMSPACE_HEAP:
     532            8 :         warned = ctxt.warn ("heap-based buffer over-read");
     533            8 :         break;
     534              :       }
     535              : 
     536          154 :     if (warned)
     537              :       {
     538          154 :         if (wi::fits_uhwi_p (m_out_of_bounds_bits.m_size_in_bits))
     539              :           {
     540          154 :             unsigned HOST_WIDE_INT num_bad_bits
     541          154 :               = m_out_of_bounds_bits.m_size_in_bits.to_uhwi ();
     542          154 :             if (num_bad_bits % BITS_PER_UNIT == 0)
     543              :               {
     544          154 :                 unsigned HOST_WIDE_INT num_bad_bytes
     545              :                   = num_bad_bits / BITS_PER_UNIT;
     546          154 :                 if (m_diag_arg)
     547          146 :                   inform_n (ctxt.get_location (),
     548              :                             num_bad_bytes,
     549              :                             "read of %wu byte from after the end of %qE",
     550              :                             "read of %wu bytes from after the end of %qE",
     551              :                             num_bad_bytes,
     552              :                             m_diag_arg);
     553              :                 else
     554            8 :                   inform_n (ctxt.get_location (),
     555              :                             num_bad_bytes,
     556              :                             "read of %wu byte from after the end of the region",
     557              :                             "read of %wu bytes from after the end of the region",
     558              :                             num_bad_bytes);
     559              :               }
     560              :             else
     561              :               {
     562            0 :                 if (m_diag_arg)
     563            0 :                   inform_n (ctxt.get_location (),
     564              :                             num_bad_bits,
     565              :                             "read of %wu bit from after the end of %qE",
     566              :                             "read of %wu bits from after the end of %qE",
     567              :                             num_bad_bits,
     568              :                             m_diag_arg);
     569              :                 else
     570            0 :                   inform_n (ctxt.get_location (),
     571              :                             num_bad_bits,
     572              :                             "read of %wu bit from after the end of the region",
     573              :                             "read of %wu bits from after the end of the region",
     574              :                             num_bad_bits);
     575              :               }
     576              :           }
     577            0 :         else if (m_diag_arg)
     578            0 :           inform (ctxt.get_location (),
     579              :                   "read from after the end of %qE",
     580              :                   m_diag_arg);
     581              : 
     582          154 :         maybe_show_notes (ctxt);
     583              :       }
     584              : 
     585          154 :     return warned;
     586              :   }
     587              : 
     588              :   bool
     589          308 :   describe_final_event (pretty_printer &pp,
     590              :                         const evdesc::final_event &) final override
     591              :   {
     592          308 :     if (m_byte_bound || !m_bit_bound)
     593              :       {
     594          308 :         byte_range out_of_bounds_bytes (0, 0);
     595          308 :         if (get_out_of_bounds_bytes (&out_of_bounds_bytes))
     596              :           {
     597          308 :             describe_final_event_as_bytes (pp, out_of_bounds_bytes);
     598          308 :             return true;
     599              :           }
     600              :       }
     601            0 :     describe_final_event_as_bits (pp);
     602            0 :     return true;
     603              :   }
     604              : 
     605              :   void
     606          308 :   describe_final_event_as_bytes (pretty_printer &pp,
     607              :                                  const byte_range &out_of_bounds_bytes)
     608              :   {
     609          308 :     byte_size_t start = out_of_bounds_bytes.get_start_byte_offset ();
     610          308 :     byte_size_t end = out_of_bounds_bytes.get_last_byte_offset ();
     611          308 :     char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
     612          308 :     print_dec (start, start_buf, SIGNED);
     613          308 :     char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
     614          308 :     print_dec (end, end_buf, SIGNED);
     615              : 
     616          308 :     if (start == end)
     617              :       {
     618          208 :         if (m_diag_arg)
     619          200 :           pp_printf (&pp,
     620              :                      "out-of-bounds read at byte %s but %qE"
     621              :                      " ends at byte %E", start_buf, m_diag_arg,
     622              :                      m_byte_bound);
     623              :         else
     624            8 :           pp_printf (&pp,
     625              :                      "out-of-bounds read at byte %s but region"
     626              :                      " ends at byte %E", start_buf,
     627              :                      m_byte_bound);
     628              :       }
     629              :     else
     630              :       {
     631          100 :         if (m_diag_arg)
     632           92 :           pp_printf (&pp,
     633              :                      "out-of-bounds read from byte %s till"
     634              :                      " byte %s but %qE ends at byte %E",
     635              :                      start_buf, end_buf, m_diag_arg,
     636              :                      m_byte_bound);
     637              :         else
     638            8 :           pp_printf (&pp,
     639              :                      "out-of-bounds read from byte %s till"
     640              :                      " byte %s but region ends at byte %E",
     641              :                      start_buf, end_buf, m_byte_bound);
     642              :       }
     643          308 :   }
     644              : 
     645              :   void
     646            0 :   describe_final_event_as_bits (pretty_printer &pp)
     647              :   {
     648            0 :     bit_size_t start = m_out_of_bounds_bits.get_start_bit_offset ();
     649            0 :     bit_size_t end = m_out_of_bounds_bits.get_last_bit_offset ();
     650            0 :     char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
     651            0 :     print_dec (start, start_buf, SIGNED);
     652            0 :     char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
     653            0 :     print_dec (end, end_buf, SIGNED);
     654              : 
     655            0 :     if (start == end)
     656              :       {
     657            0 :         if (m_diag_arg)
     658            0 :           pp_printf (&pp,
     659              :                      "out-of-bounds read at bit %s but %qE"
     660              :                      " ends at bit %E", start_buf, m_diag_arg,
     661              :                      m_bit_bound);
     662              :         else
     663            0 :           pp_printf (&pp,
     664              :                      "out-of-bounds read at bit %s but region"
     665              :                      " ends at bit %E", start_buf,
     666              :                      m_bit_bound);
     667              :       }
     668              :     else
     669              :       {
     670            0 :         if (m_diag_arg)
     671            0 :           pp_printf (&pp,
     672              :                      "out-of-bounds read from bit %s till"
     673              :                      " bit %s but %qE ends at bit %E",
     674              :                      start_buf, end_buf, m_diag_arg,
     675              :                      m_bit_bound);
     676              :         else
     677            0 :           pp_printf (&pp,
     678              :                      "out-of-bounds read from bit %s till"
     679              :                      " bit %s but region ends at bit %E",
     680              :                      start_buf, end_buf, m_bit_bound);
     681              :       }
     682            0 :   }
     683              : 
     684          154 :   enum access_direction get_dir () const final override { return access_direction::read; }
     685              : };
     686              : 
     687              : /* Concrete subclass to complain about buffer underwrites.  */
     688              : 
     689              : class concrete_buffer_underwrite : public concrete_out_of_bounds
     690              : {
     691              : public:
     692           54 :   concrete_buffer_underwrite (const region_model &model,
     693              :                               const region *reg, tree diag_arg,
     694              :                               bit_range range,
     695              :                               const svalue *sval_hint)
     696           54 :   : concrete_out_of_bounds (model, reg, diag_arg, range, sval_hint)
     697              :   {}
     698              : 
     699          438 :   const char *get_kind () const final override
     700              :   {
     701          438 :     return "concrete_buffer_underwrite";
     702              :   }
     703              : 
     704           54 :   bool emit (diagnostic_emission_context &ctxt) final override
     705              :   {
     706           54 :     bool warned;
     707           54 :     ctxt.add_cwe (124);
     708           54 :     switch (get_memory_space ())
     709              :       {
     710           36 :       default:
     711           36 :         warned = ctxt.warn ("buffer underwrite");
     712           36 :         break;
     713           14 :       case MEMSPACE_STACK:
     714           14 :         warned = ctxt.warn ("stack-based buffer underwrite");
     715           14 :         break;
     716            4 :       case MEMSPACE_HEAP:
     717            4 :         warned = ctxt.warn ("heap-based buffer underwrite");
     718            4 :         break;
     719              :       }
     720           54 :     if (warned)
     721           54 :       maybe_show_notes (ctxt);
     722           54 :     return warned;
     723              :   }
     724              : 
     725              :   bool
     726          108 :   describe_final_event (pretty_printer &pp,
     727              :                         const evdesc::final_event &) final override
     728              :   {
     729          108 :     byte_range out_of_bounds_bytes (0, 0);
     730          108 :     if (get_out_of_bounds_bytes (&out_of_bounds_bytes))
     731          108 :       describe_final_event_as_bytes (pp, out_of_bounds_bytes);
     732              :     else
     733            0 :       describe_final_event_as_bits (pp);
     734          108 :     return true;
     735              :   }
     736              : 
     737              :   void
     738          108 :   describe_final_event_as_bytes (pretty_printer &pp,
     739              :                                  const byte_range &out_of_bounds_bytes)
     740              :   {
     741          108 :     byte_size_t start = out_of_bounds_bytes.get_start_byte_offset ();
     742          108 :     byte_size_t end = out_of_bounds_bytes.get_last_byte_offset ();
     743          108 :     char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
     744          108 :     print_dec (start, start_buf, SIGNED);
     745          108 :     char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
     746          108 :     print_dec (end, end_buf, SIGNED);
     747              : 
     748          108 :     if (start == end)
     749              :       {
     750           24 :         if (m_diag_arg)
     751           24 :           pp_printf (&pp,
     752              :                      "out-of-bounds write at byte %s but %qE"
     753              :                      " starts at byte 0",
     754              :                      start_buf, m_diag_arg);
     755              :         else
     756            0 :           pp_printf (&pp,
     757              :                      "out-of-bounds write at byte %s but region"
     758              :                      " starts at byte 0", start_buf);
     759              :       }
     760              :     else
     761              :       {
     762           84 :         if (m_diag_arg)
     763           74 :           pp_printf (&pp,
     764              :                      "out-of-bounds write from byte %s till"
     765              :                      " byte %s but %qE starts at byte 0",
     766              :                      start_buf, end_buf, m_diag_arg);
     767              :         else
     768           10 :           pp_printf (&pp,
     769              :                      "out-of-bounds write from byte %s till"
     770              :                      " byte %s but region starts at byte 0",
     771          108 :                      start_buf, end_buf);;
     772              :       }
     773          108 :   }
     774              : 
     775              :   void
     776            0 :   describe_final_event_as_bits (pretty_printer &pp)
     777              :   {
     778            0 :     bit_size_t start = m_out_of_bounds_bits.get_start_bit_offset ();
     779            0 :     bit_size_t end = m_out_of_bounds_bits.get_last_bit_offset ();
     780            0 :     char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
     781            0 :     print_dec (start, start_buf, SIGNED);
     782            0 :     char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
     783            0 :     print_dec (end, end_buf, SIGNED);
     784              : 
     785            0 :     if (start == end)
     786              :       {
     787            0 :         if (m_diag_arg)
     788            0 :           pp_printf (&pp,
     789              :                      "out-of-bounds write at bit %s but %qE"
     790              :                      " starts at bit 0",
     791              :                      start_buf, m_diag_arg);
     792              :         else
     793            0 :           pp_printf (&pp,
     794              :                      "out-of-bounds write at bit %s but region"
     795              :                      " starts at bit 0", start_buf);
     796              :       }
     797              :     else
     798              :       {
     799            0 :         if (m_diag_arg)
     800            0 :           pp_printf (&pp,
     801              :                      "out-of-bounds write from bit %s till"
     802              :                      " bit %s but %qE starts at bit 0",
     803              :                      start_buf, end_buf, m_diag_arg);
     804              :         else
     805            0 :           pp_printf (&pp,
     806              :                      "out-of-bounds write from bit %s till"
     807              :                      " bit %s but region starts at bit 0",
     808            0 :                      start_buf, end_buf);;
     809              :       }
     810            0 :   }
     811              : 
     812           54 :   enum access_direction get_dir () const final override { return access_direction::write; }
     813              : };
     814              : 
     815              : /* Concrete subclass to complain about buffer under-reads.  */
     816              : 
     817              : class concrete_buffer_under_read : public concrete_out_of_bounds
     818              : {
     819              : public:
     820          138 :   concrete_buffer_under_read (const region_model &model,
     821              :                               const region *reg, tree diag_arg,
     822              :                               bit_range range)
     823          138 :   : concrete_out_of_bounds (model, reg, diag_arg, range, nullptr)
     824              :   {}
     825              : 
     826          807 :   const char *get_kind () const final override
     827              :   {
     828          807 :     return "concrete_buffer_under_read";
     829              :   }
     830              : 
     831           58 :   bool emit (diagnostic_emission_context &ctxt) final override
     832              :   {
     833           58 :     bool warned;
     834           58 :     ctxt.add_cwe (127);
     835           58 :     switch (get_memory_space ())
     836              :       {
     837           40 :       default:
     838           40 :         warned = ctxt.warn ("buffer under-read");
     839           40 :         break;
     840           14 :       case MEMSPACE_STACK:
     841           14 :         warned = ctxt.warn ("stack-based buffer under-read");
     842           14 :         break;
     843            4 :       case MEMSPACE_HEAP:
     844            4 :         warned = ctxt.warn ("heap-based buffer under-read");
     845            4 :         break;
     846              :       }
     847           58 :     if (warned)
     848           58 :       maybe_show_notes (ctxt);
     849           58 :     return warned;
     850              :   }
     851              : 
     852              :   bool
     853          116 :   describe_final_event (pretty_printer &pp,
     854              :                         const evdesc::final_event &) final override
     855              :   {
     856          116 :     byte_range out_of_bounds_bytes (0, 0);
     857          116 :     if (get_out_of_bounds_bytes (&out_of_bounds_bytes))
     858          116 :       describe_final_event_as_bytes (pp, out_of_bounds_bytes);
     859              :     else
     860            0 :       describe_final_event_as_bits (pp);
     861          116 :     return true;
     862              :   }
     863              : 
     864              :   void
     865          116 :   describe_final_event_as_bytes (pretty_printer &pp,
     866              :                                  const byte_range &out_of_bounds_bytes)
     867              :   {
     868          116 :     byte_size_t start = out_of_bounds_bytes.get_start_byte_offset ();
     869          116 :     byte_size_t end = out_of_bounds_bytes.get_last_byte_offset ();
     870          116 :     char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
     871          116 :     print_dec (start, start_buf, SIGNED);
     872          116 :     char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
     873          116 :     print_dec (end, end_buf, SIGNED);
     874              : 
     875          116 :     if (start == end)
     876              :       {
     877           34 :         if (m_diag_arg)
     878           34 :           pp_printf (&pp,
     879              :                      "out-of-bounds read at byte %s but %qE"
     880              :                      " starts at byte 0",
     881              :                      start_buf, m_diag_arg);
     882              :         else
     883            0 :           pp_printf (&pp,
     884              :                      "out-of-bounds read at byte %s but region"
     885              :                      " starts at byte 0",
     886              :                      start_buf);
     887              :       }
     888              :     else
     889              :       {
     890           82 :         if (m_diag_arg)
     891           74 :           pp_printf (&pp,
     892              :                      "out-of-bounds read from byte %s till"
     893              :                      " byte %s but %qE starts at byte 0",
     894              :                      start_buf, end_buf, m_diag_arg);
     895              :         else
     896            8 :           pp_printf (&pp,
     897              :                      "out-of-bounds read from byte %s till"
     898              :                      " byte %s but region starts at byte 0",
     899          116 :                      start_buf, end_buf);;
     900              :       }
     901          116 :   }
     902              : 
     903              :   void
     904            0 :   describe_final_event_as_bits (pretty_printer &pp)
     905              :   {
     906            0 :     bit_size_t start = m_out_of_bounds_bits.get_start_bit_offset ();
     907            0 :     bit_size_t end = m_out_of_bounds_bits.get_last_bit_offset ();
     908            0 :     char start_buf[WIDE_INT_PRINT_BUFFER_SIZE];
     909            0 :     print_dec (start, start_buf, SIGNED);
     910            0 :     char end_buf[WIDE_INT_PRINT_BUFFER_SIZE];
     911            0 :     print_dec (end, end_buf, SIGNED);
     912              : 
     913            0 :     if (start == end)
     914              :       {
     915            0 :         if (m_diag_arg)
     916            0 :           pp_printf (&pp,
     917              :                      "out-of-bounds read at bit %s but %qE"
     918              :                      " starts at bit 0",
     919              :                      start_buf, m_diag_arg);
     920              :         else
     921            0 :           pp_printf (&pp,
     922              :                      "out-of-bounds read at bit %s but region"
     923              :                      " starts at bit 0", start_buf);
     924              :       }
     925              :     else
     926              :       {
     927            0 :         if (m_diag_arg)
     928            0 :           pp_printf (&pp,
     929              :                      "out-of-bounds read from bit %s till"
     930              :                      " bit %s but %qE starts at bit 0",
     931              :                      start_buf, end_buf, m_diag_arg);
     932              :         else
     933            0 :           pp_printf (&pp,
     934              :                      "out-of-bounds read from bit %s till"
     935              :                      " bit %s but region starts at bit 0",
     936            0 :                      start_buf, end_buf);;
     937              :       }
     938            0 :   }
     939              : 
     940           58 :   enum access_direction get_dir () const final override { return access_direction::read; }
     941              : };
     942              : 
     943              : /* Abstract class to complain about out-of-bounds read/writes where
     944              :    the values are symbolic.  */
     945              : 
     946              : class symbolic_past_the_end : public out_of_bounds
     947              : {
     948              : public:
     949          149 :   symbolic_past_the_end (const region_model &model,
     950              :                          const region *reg, tree diag_arg, tree offset,
     951              :                          tree num_bytes, tree capacity,
     952              :                          const svalue *sval_hint)
     953          149 :   : out_of_bounds (model, reg, diag_arg, sval_hint),
     954          149 :     m_offset (offset),
     955          149 :     m_num_bytes (num_bytes),
     956          298 :     m_capacity (capacity)
     957              :   {}
     958              : 
     959              :   bool
     960          145 :   subclass_equal_p (const pending_diagnostic &base_other) const final override
     961              :   {
     962          145 :     const symbolic_past_the_end &other
     963              :       (static_cast <const symbolic_past_the_end &>(base_other));
     964          145 :     return (out_of_bounds::subclass_equal_p (other)
     965          145 :             && pending_diagnostic::same_tree_p (m_offset, other.m_offset)
     966          145 :             && pending_diagnostic::same_tree_p (m_num_bytes, other.m_num_bytes)
     967          290 :             && pending_diagnostic::same_tree_p (m_capacity, other.m_capacity));
     968              :   }
     969              : 
     970              :   void
     971            0 :   maybe_add_sarif_properties (diagnostics::sarif_object &result_obj)
     972              :     const final override
     973              :   {
     974            0 :     out_of_bounds::maybe_add_sarif_properties (result_obj);
     975            0 :     auto &props = result_obj.get_or_create_properties ();
     976              : #define PROPERTY_PREFIX "gcc/analyzer/symbolic_past_the_end/"
     977            0 :     props.set (PROPERTY_PREFIX "offset", tree_to_json (m_offset));
     978            0 :     props.set (PROPERTY_PREFIX "num_bytes", tree_to_json (m_num_bytes));
     979            0 :     props.set (PROPERTY_PREFIX "capacity", tree_to_json (m_capacity));
     980              : #undef PROPERTY_PREFIX
     981            0 :   }
     982              : 
     983              : protected:
     984              :   tree m_offset;
     985              :   tree m_num_bytes;
     986              :   tree m_capacity;
     987              : };
     988              : 
     989              : /* Concrete subclass to complain about overflows with symbolic values.  */
     990              : 
     991              : class symbolic_buffer_overflow : public symbolic_past_the_end
     992              : {
     993              : public:
     994          112 :   symbolic_buffer_overflow (const region_model &model,
     995              :                             const region *reg, tree diag_arg, tree offset,
     996              :                             tree num_bytes, tree capacity,
     997              :                             const svalue *sval_hint)
     998              :   : symbolic_past_the_end (model, reg, diag_arg, offset, num_bytes, capacity,
     999          112 :                            sval_hint)
    1000              :   {
    1001              :   }
    1002              : 
    1003         1837 :   const char *get_kind () const final override
    1004              :   {
    1005         1837 :     return "symbolic_buffer_overflow";
    1006              :   }
    1007              : 
    1008          111 :   bool emit (diagnostic_emission_context &ctxt) final override
    1009              :   {
    1010          111 :     bool warned;
    1011          111 :     switch (get_memory_space ())
    1012              :       {
    1013           36 :       default:
    1014           36 :         ctxt.add_cwe (787);
    1015           36 :         warned = ctxt.warn ("buffer overflow");
    1016           36 :         break;
    1017           30 :       case MEMSPACE_STACK:
    1018           30 :         ctxt.add_cwe (121);
    1019           30 :         warned = ctxt.warn ("stack-based buffer overflow");
    1020           30 :         break;
    1021           45 :       case MEMSPACE_HEAP:
    1022           45 :         ctxt.add_cwe (122);
    1023           45 :         warned =  ctxt.warn ("heap-based buffer overflow");
    1024           45 :         break;
    1025              :       }
    1026          111 :     if (warned)
    1027          111 :       maybe_show_notes (ctxt);
    1028          111 :     return warned;
    1029              :   }
    1030              : 
    1031              :   bool
    1032          222 :   describe_final_event (pretty_printer &pp,
    1033              :                         const evdesc::final_event &) final override
    1034              :   {
    1035          222 :     if (m_offset)
    1036              :       {
    1037              :         /* Known offset.  */
    1038          188 :         if (m_num_bytes)
    1039              :           {
    1040              :             /* Known offset, known size.  */
    1041          182 :             if (TREE_CODE (m_num_bytes) == INTEGER_CST)
    1042              :               {
    1043              :                 /* Known offset, known constant size.  */
    1044          164 :                 if (pending_diagnostic::same_tree_p (m_num_bytes,
    1045              :                                                      integer_one_node))
    1046              :                   {
    1047              :                     /* Singular m_num_bytes.  */
    1048          120 :                     if (m_diag_arg)
    1049           32 :                       pp_printf (&pp,
    1050              :                                  "write of %E byte at offset %qE exceeds %qE",
    1051              :                                  m_num_bytes, m_offset, m_diag_arg);
    1052              :                     else
    1053           88 :                       pp_printf (&pp,
    1054              :                                  "write of %E byte at offset %qE exceeds"
    1055              :                                  " the buffer",
    1056              :                                  m_num_bytes, m_offset);
    1057              :                   }
    1058              :                 else
    1059              :                   {
    1060              :                     /* Plural m_num_bytes.  */
    1061           44 :                     if (m_diag_arg)
    1062            8 :                       pp_printf (&pp,
    1063              :                                  "write of %E bytes at offset %qE exceeds %qE",
    1064              :                                  m_num_bytes, m_offset, m_diag_arg);
    1065              :                     else
    1066           36 :                       pp_printf (&pp,
    1067              :                                  "write of %E bytes at offset %qE exceeds"
    1068              :                                  " the buffer",
    1069              :                                  m_num_bytes, m_offset);
    1070              :                   }
    1071              :               }
    1072              :             else
    1073              :               {
    1074              :                 /* Known offset, known symbolic size.  */
    1075           18 :                 if (m_diag_arg)
    1076            0 :                   pp_printf (&pp,
    1077              :                              "write of %qE bytes at offset %qE exceeds %qE",
    1078              :                              m_num_bytes, m_offset, m_diag_arg);
    1079              :                 else
    1080           18 :                   pp_printf (&pp,
    1081              :                              "write of %qE bytes at offset %qE exceeds"
    1082              :                              " the buffer",
    1083              :                              m_num_bytes, m_offset);
    1084              :               }
    1085              :           }
    1086              :         else
    1087              :           {
    1088              :             /* Known offset, unknown size.  */
    1089            6 :             if (m_diag_arg)
    1090            0 :               pp_printf (&pp,
    1091              :                          "write at offset %qE exceeds %qE",
    1092              :                          m_offset, m_diag_arg);
    1093              :             else
    1094            6 :               pp_printf (&pp,
    1095              :                          "write at offset %qE exceeds the buffer",
    1096              :                          m_offset);
    1097              :           }
    1098              :       }
    1099              :     else
    1100              :       {
    1101              :         /* Unknown offset.  */
    1102           34 :         if (m_diag_arg)
    1103            0 :           pp_printf (&pp,
    1104              :                      "out-of-bounds write on %qE",
    1105              :                      m_diag_arg);
    1106              :         else
    1107           34 :           pp_printf (&pp, "out-of-bounds write");
    1108              :       }
    1109          222 :     return true;
    1110              :   }
    1111              : 
    1112          111 :   enum access_direction get_dir () const final override { return access_direction::write; }
    1113              : };
    1114              : 
    1115              : /* Concrete subclass to complain about over-reads with symbolic values.  */
    1116              : 
    1117              : class symbolic_buffer_over_read : public symbolic_past_the_end
    1118              : {
    1119              : public:
    1120           37 :   symbolic_buffer_over_read (const region_model &model,
    1121              :                              const region *reg, tree diag_arg, tree offset,
    1122              :                              tree num_bytes, tree capacity)
    1123              :   : symbolic_past_the_end (model, reg, diag_arg, offset, num_bytes, capacity,
    1124           37 :                            nullptr)
    1125              :   {
    1126              :   }
    1127              : 
    1128          527 :   const char *get_kind () const final override
    1129              :   {
    1130          527 :     return "symbolic_buffer_over_read";
    1131              :   }
    1132              : 
    1133           21 :   bool emit (diagnostic_emission_context &ctxt) final override
    1134              :   {
    1135           21 :     ctxt.add_cwe (126);
    1136           21 :     bool warned;
    1137           21 :     switch (get_memory_space ())
    1138              :       {
    1139            0 :       default:
    1140            0 :         ctxt.add_cwe (787);
    1141            0 :         warned = ctxt.warn ("buffer over-read");
    1142            0 :         break;
    1143           21 :       case MEMSPACE_STACK:
    1144           21 :         ctxt.add_cwe (121);
    1145           21 :         warned = ctxt.warn ("stack-based buffer over-read");
    1146           21 :         break;
    1147            0 :       case MEMSPACE_HEAP:
    1148            0 :         ctxt.add_cwe (122);
    1149            0 :         warned = ctxt.warn ("heap-based buffer over-read");
    1150            0 :         break;
    1151              :       }
    1152           21 :     if (warned)
    1153           21 :       maybe_show_notes (ctxt);
    1154           21 :     return warned;
    1155              :   }
    1156              : 
    1157              :   bool
    1158           42 :   describe_final_event (pretty_printer &pp,
    1159              :                         const evdesc::final_event &) final override
    1160              :   {
    1161           42 :     if (m_offset)
    1162              :       {
    1163              :         /* Known offset.  */
    1164           42 :         if (m_num_bytes)
    1165              :           {
    1166              :             /* Known offset, known size.  */
    1167           42 :             if (TREE_CODE (m_num_bytes) == INTEGER_CST)
    1168              :               {
    1169              :                 /* Known offset, known constant size.  */
    1170           24 :                 if (pending_diagnostic::same_tree_p (m_num_bytes,
    1171              :                                                      integer_one_node))
    1172              :                   {
    1173              :                     /* Singular m_num_bytes.  */
    1174            0 :                     if (m_diag_arg)
    1175            0 :                       pp_printf (&pp,
    1176              :                                  "read of %E byte at offset %qE exceeds %qE",
    1177              :                                  m_num_bytes, m_offset, m_diag_arg);
    1178              :                     else
    1179            0 :                       pp_printf (&pp,
    1180              :                                  "read of %E byte at offset %qE exceeds"
    1181              :                                  " the buffer",
    1182              :                                  m_num_bytes, m_offset);
    1183              :                   }
    1184              :                 else
    1185              :                   {
    1186              :                     /* Plural m_num_bytes.  */
    1187           24 :                     if (m_diag_arg)
    1188            8 :                       pp_printf (&pp,
    1189              :                                  "read of %E bytes at offset %qE exceeds %qE",
    1190              :                                  m_num_bytes, m_offset, m_diag_arg);
    1191              :                     else
    1192           16 :                       pp_printf (&pp,
    1193              :                                  "read of %E bytes at offset %qE exceeds"
    1194              :                                  " the buffer",
    1195              :                                  m_num_bytes, m_offset);
    1196              :                   }
    1197              :               }
    1198              :             else
    1199              :               {
    1200              :                 /* Known offset, known symbolic size.  */
    1201           18 :                 if (m_diag_arg)
    1202            0 :                   pp_printf (&pp,
    1203              :                              "read of %qE bytes at offset %qE exceeds %qE",
    1204              :                              m_num_bytes, m_offset, m_diag_arg);
    1205              :                 else
    1206           18 :                   pp_printf (&pp,
    1207              :                              "read of %qE bytes at offset %qE exceeds"
    1208              :                              " the buffer",
    1209              :                              m_num_bytes, m_offset);
    1210              :               }
    1211              :           }
    1212              :         else
    1213              :           {
    1214              :             /* Known offset, unknown size.  */
    1215            0 :             if (m_diag_arg)
    1216            0 :               pp_printf (&pp,
    1217              :                          "read at offset %qE exceeds %qE",
    1218              :                          m_offset, m_diag_arg);
    1219              :             else
    1220            0 :               pp_printf (&pp,
    1221              :                          "read at offset %qE exceeds the buffer",
    1222              :                          m_offset);
    1223              :           }
    1224              :       }
    1225              :     else
    1226              :       {
    1227              :         /* Unknown offset.  */
    1228            0 :         if (m_diag_arg)
    1229            0 :           pp_printf (&pp,
    1230              :                      "out-of-bounds read on %qE",
    1231              :                      m_diag_arg);
    1232              :         else
    1233            0 :           pp_printf (&pp,
    1234              :                      "out-of-bounds read");
    1235              :       }
    1236           42 :     return true;
    1237              :   }
    1238              : 
    1239           21 :   enum access_direction get_dir () const final override { return access_direction::read; }
    1240              : };
    1241              : 
    1242              : const svalue *
    1243       142400 : strip_types (const svalue *sval,
    1244              :              region_model_manager &mgr)
    1245              : {
    1246       154206 :   switch (sval->get_kind ())
    1247              :     {
    1248            0 :     default:
    1249            0 :       gcc_unreachable ();
    1250           18 :     case SK_REGION:
    1251           18 :       {
    1252           18 :         const region_svalue *region_sval = (const region_svalue *)sval;
    1253           18 :         return mgr.get_ptr_svalue (NULL_TREE, region_sval->get_pointee ());
    1254              :       }
    1255              :     case SK_CONSTANT:
    1256              :       return sval;
    1257        52817 :     case SK_UNKNOWN:
    1258        52817 :       return mgr.get_or_create_unknown_svalue (NULL_TREE);
    1259            8 :     case SK_POISONED:
    1260            8 :       {
    1261            8 :         const poisoned_svalue *poisoned_sval = (const poisoned_svalue *)sval;
    1262            8 :         return mgr.get_or_create_poisoned_svalue
    1263            8 :           (poisoned_sval->get_poison_kind (),
    1264            8 :            NULL_TREE);
    1265              :       }
    1266              :     case SK_SETJMP:
    1267              :       return sval;
    1268              :     case SK_INITIAL:
    1269              :       return sval;
    1270        12022 :     case SK_UNARYOP:
    1271        12022 :       {
    1272        12022 :         const unaryop_svalue *unaryop_sval = (const unaryop_svalue *)sval;
    1273        12022 :         const enum tree_code op = unaryop_sval->get_op ();
    1274        12022 :         if (op == VIEW_CONVERT_EXPR || op == NOP_EXPR)
    1275        11806 :           return strip_types (unaryop_sval->get_arg (), mgr);
    1276          216 :         return mgr.get_or_create_unaryop
    1277          216 :           (NULL_TREE,
    1278              :            op,
    1279          216 :            strip_types (unaryop_sval->get_arg (), mgr));
    1280              :       }
    1281        20097 :     case SK_BINOP:
    1282        20097 :       {
    1283        20097 :         const binop_svalue *binop_sval = (const binop_svalue *)sval;
    1284        20097 :         const enum tree_code op = binop_sval->get_op ();
    1285        20097 :         return mgr.get_or_create_binop
    1286        20097 :           (NULL_TREE,
    1287              :            op,
    1288              :            strip_types (binop_sval->get_arg0 (), mgr),
    1289        20097 :            strip_types (binop_sval->get_arg1 (), mgr));
    1290              :       }
    1291          431 :     case SK_SUB:
    1292          431 :       {
    1293          431 :         const sub_svalue *sub_sval = (const sub_svalue *)sval;
    1294          431 :         return mgr.get_or_create_sub_svalue
    1295          431 :           (NULL_TREE,
    1296              :            strip_types (sub_sval->get_parent (), mgr),
    1297          431 :            sub_sval->get_subregion ());
    1298              :       }
    1299           12 :     case SK_REPEATED:
    1300           12 :       {
    1301           12 :         const repeated_svalue *repeated_sval = (const repeated_svalue *)sval;
    1302           12 :         return mgr.get_or_create_repeated_svalue
    1303           12 :           (NULL_TREE,
    1304              :            strip_types (repeated_sval->get_outer_size (), mgr),
    1305           12 :            strip_types (repeated_sval->get_inner_svalue (), mgr));
    1306              :       }
    1307            4 :     case SK_BITS_WITHIN:
    1308            4 :       {
    1309            4 :         const bits_within_svalue *bits_within_sval
    1310              :           = (const bits_within_svalue *)sval;
    1311            4 :         return mgr.get_or_create_bits_within
    1312            4 :           (NULL_TREE,
    1313              :            bits_within_sval->get_bits (),
    1314            4 :            strip_types (bits_within_sval->get_inner_svalue (), mgr));
    1315              :       }
    1316            0 :     case SK_UNMERGEABLE:
    1317            0 :       {
    1318            0 :         const unmergeable_svalue *unmergeable_sval
    1319              :           = (const unmergeable_svalue *)sval;
    1320            0 :         return mgr.get_or_create_unmergeable
    1321            0 :           (strip_types (unmergeable_sval->get_arg (), mgr));
    1322              :       }
    1323              :     case SK_PLACEHOLDER:
    1324              :       return sval;
    1325         1428 :     case SK_WIDENING:
    1326         1428 :       {
    1327         1428 :         const widening_svalue *widening_sval = (const widening_svalue *)sval;
    1328         1428 :         return mgr.get_or_create_widening_svalue
    1329         1428 :           (NULL_TREE,
    1330              :            widening_sval->get_snode (),
    1331              :            strip_types (widening_sval->get_base_svalue (), mgr),
    1332         1428 :            strip_types (widening_sval->get_iter_svalue (), mgr));
    1333              :       }
    1334            0 :     case SK_COMPOUND:
    1335            0 :       {
    1336            0 :         const compound_svalue *compound_sval = (const compound_svalue *)sval;
    1337            0 :         binding_map typeless_map (*mgr.get_store_manager ());
    1338            0 :         for (auto iter : compound_sval->get_map ())
    1339              :           {
    1340            0 :             const binding_key *key = iter.m_key;
    1341            0 :             const svalue *bound_sval = iter.m_sval;
    1342            0 :             typeless_map.put (key, strip_types (bound_sval, mgr));
    1343              :           }
    1344            0 :         return mgr.get_or_create_compound_svalue (NULL_TREE, typeless_map);
    1345            0 :       }
    1346              :     case SK_CONJURED:
    1347              :       return sval;
    1348           70 :     case SK_ASM_OUTPUT:
    1349           70 :       {
    1350           70 :         const asm_output_svalue *asm_output_sval
    1351              :           = (const asm_output_svalue *)sval;
    1352           70 :         auto_vec<const svalue *> typeless_inputs
    1353           70 :           (asm_output_sval->get_num_inputs ());
    1354          210 :         for (unsigned idx = 0; idx < asm_output_sval->get_num_inputs (); idx++)
    1355          140 :           typeless_inputs.quick_push
    1356          140 :             (strip_types (asm_output_sval->get_input (idx),
    1357              :                           mgr));
    1358           70 :         return mgr.get_or_create_asm_output_svalue
    1359           70 :           (NULL_TREE,
    1360              :            asm_output_sval->get_asm_string (),
    1361              :            asm_output_sval->get_output_idx (),
    1362              :            asm_output_sval->get_num_outputs (),
    1363              :            typeless_inputs);
    1364           70 :       }
    1365           64 :     case SK_CONST_FN_RESULT:
    1366           64 :       {
    1367           64 :         const const_fn_result_svalue *const_fn_result_sval
    1368              :           = (const const_fn_result_svalue *)sval;
    1369           64 :         auto_vec<const svalue *> typeless_inputs
    1370           64 :           (const_fn_result_sval->get_num_inputs ());
    1371           64 :         for (unsigned idx = 0;
    1372          128 :              idx < const_fn_result_sval->get_num_inputs ();
    1373              :              idx++)
    1374           64 :           typeless_inputs.quick_push
    1375           64 :             (strip_types (const_fn_result_sval->get_input (idx),
    1376              :                           mgr));
    1377           64 :         return mgr.get_or_create_const_fn_result_svalue
    1378           64 :           (NULL_TREE,
    1379              :            const_fn_result_sval->get_fndecl (),
    1380              :            typeless_inputs);
    1381           64 :       }
    1382              :     }
    1383              : }
    1384              : 
    1385              : /* Check whether an access is past the end of the BASE_REG.
    1386              :   Return TRUE if the access was valid, FALSE otherwise.  */
    1387              : 
    1388              : bool
    1389        48797 : region_model::check_symbolic_bounds (const region *base_reg,
    1390              :                                      const svalue *sym_byte_offset,
    1391              :                                      const svalue *num_bytes_sval,
    1392              :                                      const svalue *capacity,
    1393              :                                      enum access_direction dir,
    1394              :                                      const svalue *sval_hint,
    1395              :                                      region_model_context *ctxt) const
    1396              : {
    1397        48797 :   gcc_assert (ctxt);
    1398              : 
    1399        48797 :   const svalue *next_byte
    1400        48797 :     = m_mgr->get_or_create_binop (NULL_TREE, PLUS_EXPR,
    1401              :                                   sym_byte_offset, num_bytes_sval);
    1402              : 
    1403        48797 :   next_byte = strip_types (next_byte, *m_mgr);
    1404        48797 :   capacity = strip_types (capacity, *m_mgr);
    1405              : 
    1406        48797 :   if (eval_condition (next_byte, GT_EXPR, capacity).is_true ())
    1407              :     {
    1408          149 :       tree diag_arg = get_representative_tree (base_reg);
    1409          149 :       tree offset_tree = get_representative_tree (sym_byte_offset);
    1410          149 :       tree num_bytes_tree = get_representative_tree (num_bytes_sval);
    1411          149 :       tree capacity_tree = get_representative_tree (capacity);
    1412          149 :       const region *offset_reg = m_mgr->get_offset_region (base_reg,
    1413              :                                                            NULL_TREE,
    1414              :                                                            sym_byte_offset);
    1415          149 :       const region *sized_offset_reg = m_mgr->get_sized_region (offset_reg,
    1416              :                                                                 NULL_TREE,
    1417          149 :                                                                 num_bytes_sval);
    1418          149 :       switch (dir)
    1419              :         {
    1420            0 :         default:
    1421            0 :           gcc_unreachable ();
    1422           37 :           break;
    1423           37 :         case access_direction::read:
    1424           37 :           gcc_assert (sval_hint == nullptr);
    1425           37 :           ctxt->warn
    1426           37 :             (std::make_unique<symbolic_buffer_over_read> (*this,
    1427              :                                                           sized_offset_reg,
    1428              :                                                           diag_arg,
    1429              :                                                           offset_tree,
    1430              :                                                           num_bytes_tree,
    1431              :                                                           capacity_tree));
    1432           37 :           return false;
    1433          112 :           break;
    1434          112 :         case access_direction::write:
    1435          112 :           ctxt->warn
    1436          112 :             (std::make_unique<symbolic_buffer_overflow> (*this,
    1437              :                                                          sized_offset_reg,
    1438              :                                                          diag_arg,
    1439              :                                                          offset_tree,
    1440              :                                                          num_bytes_tree,
    1441              :                                                          capacity_tree,
    1442              :                                                          sval_hint));
    1443          112 :           return false;
    1444          149 :           break;
    1445              :         }
    1446              :     }
    1447              :   return true;
    1448              : }
    1449              : 
    1450              : static tree
    1451      1673992 : maybe_get_integer_cst_tree (const svalue *sval)
    1452              : {
    1453      1673992 :   tree cst_tree = sval->maybe_get_constant ();
    1454      1673992 :   if (cst_tree && TREE_CODE (cst_tree) == INTEGER_CST)
    1455      1616274 :     return cst_tree;
    1456              : 
    1457              :   return NULL_TREE;
    1458              : }
    1459              : 
    1460              : /* May complain when the access on REG is out-of-bounds.
    1461              :    Return TRUE if the access was valid, FALSE otherwise.  */
    1462              : 
    1463              : bool
    1464       837030 : region_model::check_region_bounds (const region *reg,
    1465              :                                    enum access_direction dir,
    1466              :                                    const svalue *sval_hint,
    1467              :                                    region_model_context *ctxt) const
    1468              : {
    1469       837030 :   gcc_assert (ctxt);
    1470              : 
    1471              :   /* Get the offset.  */
    1472       837030 :   region_offset reg_offset = reg->get_offset (m_mgr);
    1473       837030 :   const region *base_reg = reg_offset.get_base_region ();
    1474              : 
    1475              :   /* Find out how many bits were accessed.  */
    1476       837030 :   const svalue *num_bits_sval = reg->get_bit_size_sval (m_mgr);
    1477       837030 :   tree num_bits_tree = maybe_get_integer_cst_tree (num_bits_sval);
    1478              :   /* Bail out if 0 bits are accessed.  */
    1479       837030 :   if (num_bits_tree && zerop (num_bits_tree))
    1480              :           return true;
    1481              : 
    1482              :   /* Get the capacity of the buffer (in bytes).  */
    1483       836962 :   const svalue *byte_capacity = get_capacity (base_reg);
    1484       836962 :   tree cst_byte_capacity_tree = maybe_get_integer_cst_tree (byte_capacity);
    1485              : 
    1486              :   /* The constant offset from a pointer is represented internally as a sizetype
    1487              :      but should be interpreted as a signed value here.  The statement below
    1488              :      converts the offset from bits to bytes and then to a signed integer with
    1489              :      the same precision the sizetype has on the target system.
    1490              : 
    1491              :      For example, this is needed for out-of-bounds-3.c test1 to pass when
    1492              :      compiled with a 64-bit gcc build targeting 32-bit systems.  */
    1493       836962 :   bit_offset_t bit_offset;
    1494       836962 :   if (!reg_offset.symbolic_p ())
    1495       827362 :     bit_offset = wi::sext (reg_offset.get_bit_offset (),
    1496       827362 :                            TYPE_PRECISION (size_type_node));
    1497              : 
    1498              :   /* If any of the base region, the offset, or the number of bytes accessed
    1499              :      are symbolic, we have to reason about symbolic values.  */
    1500       836962 :   if (base_reg->symbolic_p () || reg_offset.symbolic_p () || !num_bits_tree)
    1501              :     {
    1502        48797 :       const svalue* byte_offset_sval;
    1503        48797 :       if (!reg_offset.symbolic_p ())
    1504              :         {
    1505        39197 :           tree byte_offset_tree
    1506        78394 :             = wide_int_to_tree (integer_type_node,
    1507        39197 :                                 bit_offset >> LOG2_BITS_PER_UNIT);
    1508        39197 :           byte_offset_sval
    1509        39197 :             = m_mgr->get_or_create_constant_svalue (byte_offset_tree);
    1510              :         }
    1511              :       else
    1512         9600 :         byte_offset_sval = reg_offset.get_symbolic_byte_offset ();
    1513        48797 :       const svalue *num_bytes_sval = reg->get_byte_size_sval (m_mgr);
    1514        48797 :       return check_symbolic_bounds (base_reg, byte_offset_sval, num_bytes_sval,
    1515        48797 :                                     byte_capacity, dir, sval_hint, ctxt);
    1516              :     }
    1517              : 
    1518              :   /* Otherwise continue to check with concrete values.  */
    1519       788165 :   bit_range bits_outside (0, 0);
    1520       788165 :   bool oob_safe = true;
    1521              :   /* NUM_BITS_TREE should always be interpreted as unsigned.  */
    1522       788165 :   bit_offset_t num_bits_unsigned = wi::to_offset (num_bits_tree);
    1523       788165 :   bit_range read_bits (bit_offset, num_bits_unsigned);
    1524              :   /* If read_bits has a subset < 0, we do have an underwrite.  */
    1525       788165 :   if (read_bits.falls_short_of_p (0, &bits_outside))
    1526              :     {
    1527          192 :       tree diag_arg = get_representative_tree (base_reg);
    1528          192 :       switch (dir)
    1529              :         {
    1530            0 :         default:
    1531            0 :           gcc_unreachable ();
    1532          138 :           break;
    1533          138 :         case access_direction::read:
    1534          138 :           gcc_assert (sval_hint == nullptr);
    1535          138 :           ctxt->warn
    1536          138 :             (std::make_unique<concrete_buffer_under_read> (*this, reg,
    1537              :                                                            diag_arg,
    1538              :                                                            bits_outside));
    1539          138 :           oob_safe = false;
    1540          138 :           break;
    1541           54 :         case access_direction::write:
    1542           54 :           ctxt->warn
    1543           54 :             (std::make_unique<concrete_buffer_underwrite> (*this,
    1544              :                                                            reg, diag_arg,
    1545              :                                                            bits_outside,
    1546              :                                                            sval_hint));
    1547           54 :           oob_safe = false;
    1548           54 :           break;
    1549              :         }
    1550              :     }
    1551              : 
    1552              :   /* For accesses past the end, we do need a concrete capacity.  No need to
    1553              :      do a symbolic check here because the inequality check does not reason
    1554              :      whether constants are greater than symbolic values.  */
    1555       788165 :   if (!cst_byte_capacity_tree)
    1556              :     return oob_safe;
    1557              : 
    1558       784618 :   bit_range buffer (0, wi::to_offset (cst_byte_capacity_tree) * BITS_PER_UNIT);
    1559              :   /* If READ_BITS exceeds BUFFER, we do have an overflow.  */
    1560       784618 :   if (read_bits.exceeds_p (buffer, &bits_outside))
    1561              :     {
    1562          429 :       tree bit_bound = wide_int_to_tree (size_type_node,
    1563          429 :                                          buffer.get_next_bit_offset ());
    1564          429 :       tree diag_arg = get_representative_tree (base_reg);
    1565              : 
    1566          429 :       switch (dir)
    1567              :         {
    1568            0 :         default:
    1569            0 :           gcc_unreachable ();
    1570          251 :           break;
    1571          251 :         case access_direction::read:
    1572          251 :           gcc_assert (sval_hint == nullptr);
    1573          251 :           ctxt->warn
    1574          251 :             (std::make_unique<concrete_buffer_over_read> (*this,
    1575              :                                                           reg, diag_arg,
    1576              :                                                           bits_outside,
    1577              :                                                           bit_bound));
    1578          251 :           oob_safe = false;
    1579          251 :           break;
    1580          178 :         case access_direction::write:
    1581          178 :           ctxt->warn
    1582          178 :             (std::make_unique<concrete_buffer_overflow> (*this,
    1583              :                                                          reg, diag_arg,
    1584              :                                                          bits_outside,
    1585              :                                                          bit_bound,
    1586              :                                                          sval_hint));
    1587          178 :           oob_safe = false;
    1588          178 :           break;
    1589              :         }
    1590              :     }
    1591              :   return oob_safe;
    1592              : }
    1593              : 
    1594              : } // namespace ana
    1595              : 
    1596              : #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.