From patchwork Sun Oct 8 22:58:54 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 1844987 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256 header.s=mimecast20190719 header.b=J78APUhm; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4S3d0W33dbz1yq1 for ; Mon, 9 Oct 2023 09:59:55 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E363D3831381 for ; Sun, 8 Oct 2023 22:59:49 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id DDB8E385703F for ; Sun, 8 Oct 2023 22:58:57 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org DDB8E385703F Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1696805937; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=h5K2L+0jV8xCyWd/fBA7i/Ad5+H8kCwMXcLnqp7AGZs=; b=J78APUhmUEwADdIJBkDL5CTWMST2NBSrYjez/aVfnqP2ZxDK59SwBvt0qwn/NLbgqt4CSn SvMDDbQa6v4u2sKFCIXMmSP4oNZv00H6e3/dmHvI4AcWLfm8j8sXEUURv6ikLj4Lewy+e6 jmOOYCrLxAFutEiHxS9FPz9KNAFdP8I= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-190-D5FRllZxP6qscdL-FYQZ7Q-1; Sun, 08 Oct 2023 18:58:56 -0400 X-MC-Unique: D5FRllZxP6qscdL-FYQZ7Q-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.rdu2.redhat.com [10.11.54.5]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id CD8AE811E7B for ; Sun, 8 Oct 2023 22:58:55 +0000 (UTC) Received: from t14s.localdomain.com (unknown [10.22.18.119]) by smtp.corp.redhat.com (Postfix) with ESMTP id 72635175B5; Sun, 8 Oct 2023 22:58:55 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [pushed] analyzer: improvements to out-of-bounds diagrams [PR111155] Date: Sun, 8 Oct 2023 18:58:54 -0400 Message-Id: <20231008225854.783706-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.5 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-9.9 required=5.0 tests=BAYES_00, BODY_8BITS, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_FILL_THIS_FORM_SHORT autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org Update out-of-bounds diagrams to show existing string values, and the initial write index within a string buffer. For example, given the out-of-bounds write in strcat in: void test (void) { char buf[10]; strcpy (buf, "hello"); strcat (buf, " world!"); } the diagram improves from: ┌─────┬─────┬────┬────┬────┐┌─────┬─────┬─────┐ │ [0] │ [1] │[2] │[3] │[4] ││ [5] │ [6] │ [7] │ ├─────┼─────┼────┼────┼────┤├─────┼─────┼─────┤ │ ' ' │ 'w' │'o' │'r' │'l' ││ 'd' │ '!' │ NUL │ ├─────┴─────┴────┴────┴────┴┴─────┴─────┴─────┤ │ string literal (type: 'char[8]') │ └─────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ v v v v v v v v ┌─────┬────────────────────────────────────────┬────┐┌─────────────────┐ │ [0] │ ... │[9] ││ │ ├─────┴────────────────────────────────────────┴────┤│after valid range│ │ 'buf' (type: 'char[10]') ││ │ └───────────────────────────────────────────────────┘└─────────────────┘ ├─────────────────────────┬─────────────────────────┤├────────┬────────┤ │ │ ╭─────────┴────────╮ ╭─────────┴─────────╮ │capacity: 10 bytes│ │overflow of 3 bytes│ ╰──────────────────╯ ╰───────────────────╯ to: ┌────┬────┬────┬────┬────┐┌─────┬─────┬─────┐ │[0] │[1] │[2] │[3] │[4] ││ [5] │ [6] │ [7] │ ├────┼────┼────┼────┼────┤├─────┼─────┼─────┤ │' ' │'w' │'o' │'r' │'l' ││ 'd' │ '!' │ NUL │ ├────┴────┴────┴────┴────┴┴─────┴─────┴─────┤ │ string literal (type: 'char[8]') │ └───────────────────────────────────────────┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ v v v v v v v v ┌─────┬────────────────────┬────┬──────────────┬────┐┌─────────────────┐ │ [0] │ ... │[5] │ ... │[9] ││ │ ├─────┼────┬────┬────┬────┬┼────┼──────────────┴────┘│ │ │ 'h' │'e' │'l' │'l' │'o' ││NUL │ │after valid range│ ├─────┴────┴────┴────┴────┴┴────┴───────────────────┐│ │ │ 'buf' (type: 'char[10]') ││ │ └───────────────────────────────────────────────────┘└─────────────────┘ ├─────────────────────────┬─────────────────────────┤├────────┬────────┤ │ │ ╭─────────┴────────╮ ╭─────────┴─────────╮ │capacity: 10 bytes│ │overflow of 3 bytes│ ╰──────────────────╯ ╰───────────────────╯ Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. Pushed to trunk as r14-4477-gb365e9d57ad445. gcc/analyzer/ChangeLog: PR analyzer/111155 * access-diagram.cc (boundaries::boundaries): Add logger param (boundaries::add): Add logging. (boundaries::get_hard_boundaries_in_range): New. (boundaries::m_logger): New field. (boundaries::get_table_x_for_offset): Make public. (class svalue_spatial_item): New. (class compound_svalue_spatial_item): New. (add_ellipsis_to_gaps): New. (valid_region_spatial_item::valid_region_spatial_item): Add theme param. Initialize m_boundaries, m_existing_sval, and m_existing_sval_spatial_item. (valid_region_spatial_item::add_boundaries): Set m_boundaries. Add boundaries for any m_existing_sval_spatial_item. (valid_region_spatial_item::add_array_elements_to_table): Rewrite creation of min/max index in terms of maybe_add_array_index_to_table. Rewrite ellipsis code using add_ellipsis_to_gaps. Add index values for any hard boundaries within the valid region. (valid_region_spatial_item::maybe_add_array_index_to_table): New, based on code formerly in add_array_elements_to_table. (valid_region_spatial_item::make_table): Make use of m_existing_sval_spatial_item, if any. (valid_region_spatial_item::m_boundaries): New field. (valid_region_spatial_item::m_existing_sval): New field. (valid_region_spatial_item::m_existing_sval_spatial_item): New field. (class svalue_spatial_item): Rename to... (class written_svalue_spatial_item): ...this. (class string_region_spatial_item): Rename to.. (class string_literal_spatial_item): ...this. Add "kind". (string_literal_spatial_item::add_boundaries): Use m_kind to determine kind of boundary. Update for renaming of m_actual_bits to m_bits. (string_literal_spatial_item::make_table): Likewise. Support not displaying a row for byte indexes, and not displaying a row for the type. (string_literal_spatial_item::add_column_for_byte): Make byte index row optional. (svalue_spatial_item::make): Convert to... (make_written_svalue_spatial_item): ...this. (make_existing_svalue_spatial_item): New. (access_diagram_impl::access_diagram_impl): Pass theme to m_valid_region_spatial_item ctor. Update for renaming of m_svalue_spatial_item. (access_diagram_impl::find_boundaries): Pass logger to boundaries. Update for renaming of... (access_diagram_impl::m_svalue_spatial_item): Rename to... (access_diagram_impl::m_written_svalue_spatial_item): ...this. gcc/testsuite/ChangeLog: PR analyzer/111155 * c-c++-common/analyzer/out-of-bounds-diagram-strcat-2.c: New test. * c-c++-common/analyzer/out-of-bounds-diagram-strcat.c: New test. * gcc.dg/analyzer/out-of-bounds-diagram-17.c: Update expected result to show the existing content of "buf" and the index at which the write starts. * gcc.dg/analyzer/out-of-bounds-diagram-18.c: Likewise. * gcc.dg/analyzer/out-of-bounds-diagram-19.c: Likewise. * gcc.dg/analyzer/out-of-bounds-diagram-6.c: Update expected output. gcc/ChangeLog: PR analyzer/111155 * text-art/table.cc (table::maybe_set_cell_span): New. (table::add_other_table): New. * text-art/table.h (class table::cell_placement): Add class table as a friend. (table::add_rows): New. (table::add_row): Reimplement in terms of add_rows. (table::maybe_set_cell_span): New decl. (table::add_other_table): New decl. * text-art/types.h (operator+): New operator for rect + coord. --- gcc/analyzer/access-diagram.cc | 430 ++++++++++++++---- .../analyzer/out-of-bounds-diagram-strcat-2.c | 74 +++ .../analyzer/out-of-bounds-diagram-strcat.c | 66 +++ .../analyzer/out-of-bounds-diagram-17.c | 28 +- .../analyzer/out-of-bounds-diagram-18.c | 54 ++- .../analyzer/out-of-bounds-diagram-19.c | 42 +- .../gcc.dg/analyzer/out-of-bounds-diagram-6.c | 68 +-- gcc/text-art/table.cc | 35 ++ gcc/text-art/table.h | 21 +- gcc/text-art/types.h | 7 + 10 files changed, 644 insertions(+), 181 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/analyzer/out-of-bounds-diagram-strcat-2.c create mode 100644 gcc/testsuite/c-c++-common/analyzer/out-of-bounds-diagram-strcat.c diff --git a/gcc/analyzer/access-diagram.cc b/gcc/analyzer/access-diagram.cc index a51d594b5b2..2197ec63f53 100644 --- a/gcc/analyzer/access-diagram.cc +++ b/gcc/analyzer/access-diagram.cc @@ -630,8 +630,8 @@ class boundaries public: enum class kind { HARD, SOFT}; - boundaries (const region &base_reg) - : m_base_reg (base_reg) + boundaries (const region &base_reg, logger *logger) + : m_base_reg (base_reg), m_logger (logger) { } @@ -646,6 +646,15 @@ public: { add (range.m_start, kind); add (range.m_next, kind); + if (m_logger) + { + m_logger->start_log_line (); + m_logger->log_partial ("added access_range: "); + range.dump_to_pp (m_logger->get_printer (), true); + m_logger->log_partial (" (%s)", + (kind == kind::HARD) ? "HARD" : "soft"); + m_logger->end_log_line (); + } } void add (const region ®, region_model_manager *mgr, enum kind kind) @@ -714,8 +723,30 @@ public: return m_all_offsets.size (); } + std::vector + get_hard_boundaries_in_range (byte_offset_t min_offset, + byte_offset_t max_offset) const + { + std::vector result; + for (auto &offset : m_hard_offsets) + { + if (!offset.concrete_p ()) + continue; + byte_offset_t byte; + if (!offset.get_concrete_byte_offset (&byte)) + continue; + if (byte < min_offset) + continue; + if (byte > max_offset) + continue; + result.push_back (offset); + } + return result; + } + private: const region &m_base_reg; + logger *m_logger; std::set m_all_offsets; std::set m_hard_offsets; }; @@ -1085,7 +1116,6 @@ public: logger.dec_indent (); } -private: int get_table_x_for_offset (region_offset offset) const { auto slot = m_table_x_for_offset.find (offset); @@ -1097,6 +1127,7 @@ private: return slot->second; } +private: int get_table_x_for_prev_offset (region_offset offset) const { auto slot = m_table_x_for_prev_offset.find (offset); @@ -1132,6 +1163,124 @@ public: style_manager &sm) const = 0; }; +/* A spatial_item that involves showing an svalue at a particular offset. */ + +class svalue_spatial_item : public spatial_item +{ +public: + enum class kind + { + WRITTEN, + EXISTING + }; +protected: + svalue_spatial_item (const svalue &sval, + access_range bits, + enum kind kind) + : m_sval (sval), m_bits (bits), m_kind (kind) + { + } + + const svalue &m_sval; + access_range m_bits; + enum kind m_kind; +}; + +static std::unique_ptr +make_existing_svalue_spatial_item (const svalue *sval, + const access_range &bits, + const theme &theme); + +class compound_svalue_spatial_item : public svalue_spatial_item +{ +public: + compound_svalue_spatial_item (const compound_svalue &sval, + const access_range &bits, + enum kind kind, + const theme &theme) + : svalue_spatial_item (sval, bits, kind), + m_compound_sval (sval) + { + const binding_map &map = m_compound_sval.get_map (); + auto_vec binding_keys; + for (auto iter : map) + { + const binding_key *key = iter.first; + const svalue *bound_sval = iter.second; + if (const concrete_binding *concrete_key + = key->dyn_cast_concrete_binding ()) + { + access_range range (nullptr, + concrete_key->get_bit_range ()); + if (std::unique_ptr child + = make_existing_svalue_spatial_item (bound_sval, + range, + theme)) + m_children.push_back (std::move (child)); + } + } + } + + void add_boundaries (boundaries &out, logger *logger) const final override + { + LOG_SCOPE (logger); + for (auto &iter : m_children) + iter->add_boundaries (out, logger); + } + + table make_table (const bit_to_table_map &btm, + style_manager &sm) const final override + { + std::vector child_tables; + int max_rows = 0; + for (auto &iter : m_children) + { + table child_table (iter->make_table (btm, sm)); + max_rows = MAX (max_rows, child_table.get_size ().h); + child_tables.push_back (std::move (child_table)); + } + table t (table::size_t (btm.get_num_columns (), max_rows)); + for (auto &&child_table : child_tables) + t.add_other_table (std::move (child_table), + table::coord_t (0, 0)); + return t; + } + +private: + const compound_svalue &m_compound_sval; + std::vector> m_children; +}; + +/* Loop through the TABLE_X_RANGE columns of T, adding + cells containing "..." in any unoccupied ranges of table cell. */ + +static void +add_ellipsis_to_gaps (table &t, + style_manager &sm, + const table::range_t &table_x_range, + const table::range_t &table_y_range) +{ + int table_x = table_x_range.get_min (); + while (table_x < table_x_range.get_next ()) + { + /* Find a run of unoccupied table cells. */ + const int start_table_x = table_x; + while (table_x < table_x_range.get_next () + && !t.get_placement_at (table::coord_t (table_x, + table_y_range.get_min ()))) + table_x++; + const table::range_t unoccupied_x_range (start_table_x, table_x); + if (unoccupied_x_range.get_size () > 0) + t.set_cell_span (table::rect_t (unoccupied_x_range, table_y_range), + styled_string (sm, "...")); + /* Skip occupied table cells. */ + while (table_x < table_x_range.get_next () + && t.get_placement_at (table::coord_t (table_x, + table_y_range.get_min ()))) + table_x++; + } +} + /* Subclass of spatial_item for visualizing the region of memory that's valid to access relative to the base region of region accessed in the operation. */ @@ -1140,14 +1289,23 @@ class valid_region_spatial_item : public spatial_item { public: valid_region_spatial_item (const access_operation &op, - diagnostic_event_id_t region_creation_event_id) + diagnostic_event_id_t region_creation_event_id, + const theme &theme) : m_op (op), - m_region_creation_event_id (region_creation_event_id) - {} + m_region_creation_event_id (region_creation_event_id), + m_boundaries (nullptr), + m_existing_sval (op.m_model.get_store_value (op.m_base_region, nullptr)), + m_existing_sval_spatial_item + (make_existing_svalue_spatial_item (m_existing_sval, + op.get_valid_bits (), + theme)) + { + } void add_boundaries (boundaries &out, logger *logger) const final override { LOG_SCOPE (logger); + m_boundaries = &out; access_range valid_bits = m_op.get_valid_bits (); if (logger) { @@ -1158,6 +1316,18 @@ public: } out.add (valid_bits, boundaries::kind::HARD); + if (m_existing_sval_spatial_item) + { + if (logger) + { + logger->start_log_line (); + logger->log_partial ("existing svalue: "); + m_existing_sval->dump_to_pp (logger->get_printer (), true); + logger->end_log_line (); + } + m_existing_sval_spatial_item->add_boundaries (out, logger); + } + /* Support for showing first and final element in array types. */ if (tree base_type = m_op.m_base_region->get_type ()) if (TREE_CODE (base_type) == ARRAY_TYPE) @@ -1193,65 +1363,102 @@ public: { tree base_type = m_op.m_base_region->get_type (); gcc_assert (TREE_CODE (base_type) == ARRAY_TYPE); + gcc_assert (m_boundaries != nullptr); tree domain = TYPE_DOMAIN (base_type); if (!(TYPE_MIN_VALUE (domain) && TYPE_MAX_VALUE (domain))) return; - region_model_manager * const mgr = m_op.get_manager (); const int table_y = 0; const int table_h = 1; const table::range_t table_y_range (table_y, table_y + table_h); t.add_row (); - const svalue *min_idx_sval - = mgr->get_or_create_constant_svalue (TYPE_MIN_VALUE (domain)); - const region *min_element = mgr->get_element_region (m_op.m_base_region, - TREE_TYPE (base_type), - min_idx_sval); - const access_range min_element_range (*min_element, mgr); - const table::range_t min_element_x_range - = btm.get_table_x_for_range (min_element_range); - - t.set_cell_span (table::rect_t (min_element_x_range, - table_y_range), - fmt_styled_string (sm, "[%E]", - TYPE_MIN_VALUE (domain))); - - const svalue *max_idx_sval - = mgr->get_or_create_constant_svalue (TYPE_MAX_VALUE (domain)); - const region *max_element = mgr->get_element_region (m_op.m_base_region, + + const table::range_t min_x_range + = maybe_add_array_index_to_table (t, btm, sm, table_y_range, + TYPE_MIN_VALUE (domain)); + const table::range_t max_x_range + = maybe_add_array_index_to_table (t, btm, sm, table_y_range, + TYPE_MAX_VALUE (domain)); + + if (TREE_TYPE (base_type) == char_type_node) + { + /* For a char array,: if there are any hard boundaries in + m_boundaries that are *within* the valid region, + then show those index values. */ + std::vector hard_boundaries + = m_boundaries->get_hard_boundaries_in_range + (tree_to_shwi (TYPE_MIN_VALUE (domain)), + tree_to_shwi (TYPE_MAX_VALUE (domain))); + for (auto &offset : hard_boundaries) + { + const int table_x = btm.get_table_x_for_offset (offset); + if (!offset.concrete_p ()) + continue; + byte_offset_t byte; + if (!offset.get_concrete_byte_offset (&byte)) + continue; + table::range_t table_x_range (table_x, table_x + 1); + t.maybe_set_cell_span (table::rect_t (table_x_range, + table_y_range), + fmt_styled_string (sm, "[%wi]", + byte.to_shwi ())); + } + } + + add_ellipsis_to_gaps (t, sm, + table::range_t (min_x_range.get_next (), + max_x_range.get_min ()), + table_y_range); + } + + table::range_t + maybe_add_array_index_to_table (table &t, + const bit_to_table_map &btm, + style_manager &sm, + const table::range_t table_y_range, + tree idx_cst) const + { + region_model_manager * const mgr = m_op.get_manager (); + tree base_type = m_op.m_base_region->get_type (); + const svalue *idx_sval + = mgr->get_or_create_constant_svalue (idx_cst); + const region *element_reg = mgr->get_element_region (m_op.m_base_region, TREE_TYPE (base_type), - max_idx_sval); - if (min_element == max_element) - return; // 1-element array + idx_sval); + const access_range element_range (*element_reg, mgr); + const table::range_t element_x_range + = btm.get_table_x_for_range (element_range); - const access_range max_element_range (*max_element, mgr); - const table::range_t max_element_x_range - = btm.get_table_x_for_range (max_element_range); - t.set_cell_span (table::rect_t (max_element_x_range, - table_y_range), - fmt_styled_string (sm, "[%E]", - TYPE_MAX_VALUE (domain))); + t.maybe_set_cell_span (table::rect_t (element_x_range, + table_y_range), + fmt_styled_string (sm, "[%E]", idx_cst)); - const table::range_t other_elements_x_range (min_element_x_range.next, - max_element_x_range.start); - if (other_elements_x_range.get_size () > 0) - t.set_cell_span (table::rect_t (other_elements_x_range, table_y_range), - styled_string (sm, "...")); + return element_x_range; } table make_table (const bit_to_table_map &btm, style_manager &sm) const final override { - table t (table::size_t (btm.get_num_columns (), 1)); + table t (table::size_t (btm.get_num_columns (), 0)); if (tree base_type = m_op.m_base_region->get_type ()) if (TREE_CODE (base_type) == ARRAY_TYPE) add_array_elements_to_table (t, btm, sm); + /* Make use of m_existing_sval_spatial_item, if any. */ + if (m_existing_sval_spatial_item) + { + table table_for_existing + = m_existing_sval_spatial_item->make_table (btm, sm); + const int table_y = t.add_rows (table_for_existing.get_size ().h); + t.add_other_table (std::move (table_for_existing), + table::coord_t (0, table_y)); + } + access_range valid_bits = m_op.get_valid_bits (); - const int table_y = t.get_size ().h - 1; + const int table_y = t.add_row (); const int table_h = 1; table::rect_t rect = btm.get_table_rect (valid_bits, table_y, table_h); styled_string s; @@ -1306,6 +1513,9 @@ public: private: const access_operation &m_op; diagnostic_event_id_t m_region_creation_event_id; + mutable const boundaries *m_boundaries; + const svalue *m_existing_sval; + std::unique_ptr m_existing_sval_spatial_item; }; /* Subclass of spatial_item for visualizing the region of memory @@ -1362,15 +1572,10 @@ private: to the accessed region. Can be subclassed to give visualizations of specific kinds of svalue. */ -class svalue_spatial_item : public spatial_item +class written_svalue_spatial_item : public spatial_item { public: - static std::unique_ptr make (const access_operation &op, - const svalue &sval, - access_range actual_bits, - const theme &theme); - - svalue_spatial_item (const access_operation &op, + written_svalue_spatial_item (const access_operation &op, const svalue &sval, access_range actual_bits) : m_op (op), m_sval (sval), m_actual_bits (actual_bits) @@ -1479,15 +1684,15 @@ protected: └──────────────────────────────────────────────────────────────────────┘ */ -class string_region_spatial_item : public svalue_spatial_item +class string_literal_spatial_item : public svalue_spatial_item { public: - string_region_spatial_item (const access_operation &op, - const svalue &sval, - access_range actual_bits, - const string_region &string_reg, - const theme &theme) - : svalue_spatial_item (op, sval, actual_bits), + string_literal_spatial_item (const svalue &sval, + access_range actual_bits, + const string_region &string_reg, + const theme &theme, + enum kind kind) + : svalue_spatial_item (sval, actual_bits, kind), m_string_reg (string_reg), m_theme (theme), m_ellipsis_threshold (param_analyzer_text_art_string_ellipsis_threshold), @@ -1501,16 +1706,18 @@ public: void add_boundaries (boundaries &out, logger *logger) const override { LOG_SCOPE (logger); - out.add (m_actual_bits, boundaries::kind::HARD); + out.add (m_bits, m_kind == svalue_spatial_item::kind::WRITTEN + ? boundaries::kind::HARD + : boundaries::kind::SOFT); tree string_cst = get_string_cst (); /* TREE_STRING_LENGTH is sizeof, not strlen. */ if (m_show_full_string) - out.add_all_bytes_in_range (m_actual_bits); + out.add_all_bytes_in_range (m_bits); else { byte_range bytes (0, 0); - bool valid = m_actual_bits.as_concrete_byte_range (&bytes); + bool valid = m_bits.as_concrete_byte_range (&bytes); gcc_assert (valid); byte_range head_of_string (bytes.get_start_byte_offset (), m_ellipsis_head_len); @@ -1532,11 +1739,13 @@ public: { table t (table::size_t (btm.get_num_columns (), 0)); - const int byte_idx_table_y = t.add_row (); + const int byte_idx_table_y = (m_kind == svalue_spatial_item::kind::WRITTEN + ? t.add_row () + : -1); const int byte_val_table_y = t.add_row (); byte_range bytes (0, 0); - bool valid = m_actual_bits.as_concrete_byte_range (&bytes); + bool valid = m_bits.as_concrete_byte_range (&bytes); gcc_assert (valid); tree string_cst = get_string_cst (); if (m_show_full_string) @@ -1616,14 +1825,17 @@ public: byte_idx, byte_idx_table_y, byte_val_table_y); - /* Ellipsis (two rows high). */ + /* Ellipsis. */ const byte_range ellipsis_bytes (m_ellipsis_head_len + bytes.get_start_byte_offset (), TREE_STRING_LENGTH (string_cst) - (m_ellipsis_head_len + m_ellipsis_tail_len)); const table::rect_t table_rect - = btm.get_table_rect (&m_string_reg, ellipsis_bytes, - byte_idx_table_y, 2); + = ((byte_idx_table_y != -1) + ? btm.get_table_rect (&m_string_reg, ellipsis_bytes, + byte_idx_table_y, 2) + : btm.get_table_rect (&m_string_reg, ellipsis_bytes, + byte_val_table_y, 1)); t.set_cell_span(table_rect, styled_string (sm, "...")); /* Tail of string. */ @@ -1637,12 +1849,15 @@ public: byte_idx_table_y, byte_val_table_y); } - const int summary_table_y = t.add_row (); - t.set_cell_span (btm.get_table_rect (&m_string_reg, bytes, - summary_table_y, 1), - fmt_styled_string (sm, - _("string literal (type: %qT)"), - TREE_TYPE (string_cst))); + if (m_kind == svalue_spatial_item::kind::WRITTEN) + { + const int summary_table_y = t.add_row (); + t.set_cell_span (btm.get_table_rect (&m_string_reg, bytes, + summary_table_y, 1), + fmt_styled_string (sm, + _("string literal (type: %qT)"), + TREE_TYPE (string_cst))); + } return t; } @@ -1687,7 +1902,7 @@ private: gcc_assert (byte_idx_within_string < TREE_STRING_LENGTH (string_cst)); const byte_range bytes (byte_idx_within_cluster, 1); - if (1) // show_byte_indices + if (byte_idx_table_y != -1) { const table::rect_t idx_table_rect = btm.get_table_rect (&m_string_reg, bytes, byte_idx_table_y, 1); @@ -1729,18 +1944,54 @@ private: const bool m_show_utf8; }; -std::unique_ptr -svalue_spatial_item::make (const access_operation &op, - const svalue &sval, - access_range actual_bits, - const theme &theme) +static std::unique_ptr +make_written_svalue_spatial_item (const access_operation &op, + const svalue &sval, + access_range actual_bits, + const theme &theme) { if (const initial_svalue *initial_sval = sval.dyn_cast_initial_svalue ()) if (const string_region *string_reg = initial_sval->get_region ()->dyn_cast_string_region ()) - return make_unique (op, sval, actual_bits, - *string_reg, theme); - return make_unique (op, sval, actual_bits); + return make_unique + (sval, actual_bits, + *string_reg, theme, + svalue_spatial_item::kind::WRITTEN); + return make_unique (op, sval, actual_bits); +} + +static std::unique_ptr +make_existing_svalue_spatial_item (const svalue *sval, + const access_range &bits, + const theme &theme) +{ + if (!sval) + return nullptr; + + switch (sval->get_kind ()) + { + default: + return nullptr; + + case SK_INITIAL: + { + const initial_svalue *initial_sval = (const initial_svalue *)sval; + if (const string_region *string_reg + = initial_sval->get_region ()->dyn_cast_string_region ()) + return make_unique + (*sval, bits, + *string_reg, theme, + svalue_spatial_item::kind::EXISTING); + return nullptr; + } + + case SK_COMPOUND: + return make_unique + (*((const compound_svalue *)sval), + bits, + svalue_spatial_item::kind::EXISTING, + theme); + } } /* Widget subclass implementing access diagrams. */ @@ -1759,7 +2010,7 @@ public: m_theme (theme), m_logger (logger), m_invalid (false), - m_valid_region_spatial_item (op, region_creation_event_id), + m_valid_region_spatial_item (op, region_creation_event_id, theme), m_accessed_region_spatial_item (op), m_btm (), m_calc_req_size_called (false) @@ -1800,10 +2051,11 @@ public: if (op.m_sval_hint) { access_range actual_bits = m_op.get_actual_bits (); - m_svalue_spatial_item = svalue_spatial_item::make (m_op, - *op.m_sval_hint, - actual_bits, - m_theme); + m_written_svalue_spatial_item + = make_written_svalue_spatial_item (m_op, + *op.m_sval_hint, + actual_bits, + m_theme); } /* Two passes: @@ -1856,9 +2108,9 @@ public: add_aligned_child_table (std::move (t_headings)); } - if (m_svalue_spatial_item) + if (m_written_svalue_spatial_item) { - table t_sval (m_svalue_spatial_item->make_table (m_btm, m_sm)); + table t_sval (m_written_svalue_spatial_item->make_table (m_btm, m_sm)); add_aligned_child_table (std::move (t_sval)); } else @@ -1942,12 +2194,12 @@ private: find_boundaries () const { std::unique_ptr result - = make_unique (*m_op.m_base_region); + = make_unique (*m_op.m_base_region, m_logger); m_valid_region_spatial_item.add_boundaries (*result, m_logger); m_accessed_region_spatial_item.add_boundaries (*result, m_logger); - if (m_svalue_spatial_item) - m_svalue_spatial_item->add_boundaries (*result, m_logger); + if (m_written_svalue_spatial_item) + m_written_svalue_spatial_item->add_boundaries (*result, m_logger); return result; } @@ -2324,7 +2576,7 @@ private: valid_region_spatial_item m_valid_region_spatial_item; accessed_region_spatial_item m_accessed_region_spatial_item; - std::unique_ptr m_svalue_spatial_item; + std::unique_ptr m_written_svalue_spatial_item; std::unique_ptr m_boundaries; diff --git a/gcc/testsuite/c-c++-common/analyzer/out-of-bounds-diagram-strcat-2.c b/gcc/testsuite/c-c++-common/analyzer/out-of-bounds-diagram-strcat-2.c new file mode 100644 index 00000000000..b129518d759 --- /dev/null +++ b/gcc/testsuite/c-c++-common/analyzer/out-of-bounds-diagram-strcat-2.c @@ -0,0 +1,74 @@ +/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode" } */ +/* { dg-skip-if "" { powerpc-ibm-aix* } } */ + +#include + +#define LOREM_IPSUM \ + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod" \ + " tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim" \ + " veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea" \ + " commodo consequat. Duis aute irure dolor in reprehenderit in voluptate" \ + " velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint" \ + " occaecat cupidatat non proident, sunt in culpa qui officia deserunt" \ + " mollit anim id est laborum." + +void test (void) +{ + char buf[500]; + strcpy (buf, LOREM_IPSUM); + strcat (buf, LOREM_IPSUM); /* { dg-warning "stack-based buffer overflow" } */ +} + +/* { dg-begin-multiline-output "" } + + ┌─────┬───┬───┬───┬───┬───┬────────┬─────┬─────┬─────┬─────┬─────┬─────┐ + │ [0] │[1]│[2]│[3]│[4]│[5]│ │[440]│[441]│[442]│[443]│[444]│[445]│ + ├─────┼───┼───┼───┼───┼───┤ ... ├─────┼─────┼─────┼─────┼─────┼─────┤ + │ 'L' │'o'│'r'│'e'│'m'│' '│ │ 'o' │ 'r' │ 'u' │ 'm' │ '.' │ NUL │ + ├─────┴───┴───┴───┴───┴───┴────────┴─────┴─────┴─────┴─────┴─────┴─────┤ + │ string literal (type: 'char[446]') │ + └──────────────────────────────────────────────────────────────────────┘ + │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ + │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ + v v v v v v v v v v v v v v v + ┌───┬────────────────────────────────────────────┬─────┬────────────────────┬─────┐┌────────────────────────────────────┐ + │[0]│ ... │[445]│ ... │[499]││ │ + ├───┼───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬┼─────┼────────────────────┴─────┘│ │ + │'L'│'o'│'r'│'e'│'m'│' '│...│'o'│'r'│'u'│'m'│'.'││ NUL │ │ after valid range │ + ├───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴┴─────┴──────────────────────────┐│ │ + │ 'buf' (type: 'char[500]') ││ │ + └─────────────────────────────────────────────────────────────────────────────────┘└────────────────────────────────────┘ + ├────────────────────────────────────────┬────────────────────────────────────────┤├─────────────────┬──────────────────┤ + │ │ + ╭─────────┴─────────╮ ╭──────────┴──────────╮ + │capacity: 500 bytes│ │overflow of 391 bytes│ + ╰───────────────────╯ ╰─────────────────────╯ + + { dg-end-multiline-output "" { target c } } */ + +/* { dg-begin-multiline-output "" } + + ┌─────┬───┬───┬───┬───┬───┬────────┬─────┬─────┬─────┬─────┬─────┬─────┐ + │ [0] │[1]│[2]│[3]│[4]│[5]│ │[440]│[441]│[442]│[443]│[444]│[445]│ + ├─────┼───┼───┼───┼───┼───┤ ... ├─────┼─────┼─────┼─────┼─────┼─────┤ + │ 'L' │'o'│'r'│'e'│'m'│' '│ │ 'o' │ 'r' │ 'u' │ 'm' │ '.' │ NUL │ + ├─────┴───┴───┴───┴───┴───┴────────┴─────┴─────┴─────┴─────┴─────┴─────┤ + │ string literal (type: 'const char[446]') │ + └──────────────────────────────────────────────────────────────────────┘ + │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ + │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ + v v v v v v v v v v v v v v v + ┌───┬────────────────────────────────────────────┬─────┬────────────────────┬─────┐┌────────────────────────────────────┐ + │[0]│ ... │[445]│ ... │[499]││ │ + ├───┼───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬┼─────┼────────────────────┴─────┘│ │ + │'L'│'o'│'r'│'e'│'m'│' '│...│'o'│'r'│'u'│'m'│'.'││ NUL │ │ after valid range │ + ├───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴┴─────┴──────────────────────────┐│ │ + │ 'char buf [500]' (type: 'char[500]') ││ │ + └─────────────────────────────────────────────────────────────────────────────────┘└────────────────────────────────────┘ + ├────────────────────────────────────────┬────────────────────────────────────────┤├─────────────────┬──────────────────┤ + │ │ + ╭─────────┴─────────╮ ╭──────────┴──────────╮ + │capacity: 500 bytes│ │overflow of 391 bytes│ + ╰───────────────────╯ ╰─────────────────────╯ + + { dg-end-multiline-output "" { target c++ } } */ diff --git a/gcc/testsuite/c-c++-common/analyzer/out-of-bounds-diagram-strcat.c b/gcc/testsuite/c-c++-common/analyzer/out-of-bounds-diagram-strcat.c new file mode 100644 index 00000000000..53c128a4fa1 --- /dev/null +++ b/gcc/testsuite/c-c++-common/analyzer/out-of-bounds-diagram-strcat.c @@ -0,0 +1,66 @@ +/* { dg-additional-options "-fdiagnostics-text-art-charset=unicode -Wno-stringop-overflow" } */ +/* { dg-skip-if "" { powerpc-ibm-aix* } } */ + +#include + +void test (void) +{ + char buf[10]; + strcpy (buf, "foo"); + strcat (buf, " bar"); + strcat (buf, " baz!"); /* { dg-warning "stack-based buffer overflow" } */ +} + +/* { dg-begin-multiline-output "" } + + ┌────┬────┬────┐ ┌────┬────┬───────┐ + │[0] │[1] │[2] │ │[3] │[4] │ [5] │ + ├────┼────┼────┤ ├────┼────┼───────┤ + │' ' │'b' │'a' │ │'z' │'!' │ NUL │ + ├────┴────┴────┴─┴────┴────┴───────┤ + │ string literal (type: 'char[6]') │ + └──────────────────────────────────┘ + │ │ │ │ │ │ + │ │ │ │ │ │ + v v v v v v + ┌─────┬─────────────────────────────┬────┬────┬────┐ ┌─────────────────┐ + │ [0] │ ... │[7] │... │[9] │ │ │ + └─────┴────────┬────┬────┬────┬────┬┼────┼────┴────┘ │ │ + │' ' │'b' │'a' │'r' ││NUL │ │after valid range│ + ┌──────────────┴────┴────┴────┴────┴┴────┴─────────┐ │ │ + │ 'buf' (type: 'char[10]') │ │ │ + └──────────────────────────────────────────────────┘ └─────────────────┘ + ├────────────────────────┬─────────────────────────┤ ├────────┬────────┤ + │ │ + ╭─────────┴────────╮ ╭─────────┴─────────╮ + │capacity: 10 bytes│ │overflow of 3 bytes│ + ╰──────────────────╯ ╰───────────────────╯ + + { dg-end-multiline-output "" { target c } } */ + +/* { dg-begin-multiline-output "" } + + ┌─────┬─────┬─────┐ ┌─────┬─────┬─────┐ + │ [0] │ [1] │ [2] │ │ [3] │ [4] │ [5] │ + ├─────┼─────┼─────┤ ├─────┼─────┼─────┤ + │ ' ' │ 'b' │ 'a' │ │ 'z' │ '!' │ NUL │ + ├─────┴─────┴─────┴──┴─────┴─────┴─────┤ + │string literal (type: 'const char[6]')│ + └──────────────────────────────────────┘ + │ │ │ │ │ │ + │ │ │ │ │ │ + v v v v v v + ┌────┬──────────────────────────┬─────┬─────┬─────┐ ┌─────────────────┐ + │[0] │ ... │ [7] │ ... │ [9] │ │ │ + └────┴───────┬────┬────┬───┬───┬┼─────┼─────┴─────┘ │ │ + │' ' │'b' │'a'│'r'││ NUL │ │after valid range│ + ┌────────────┴────┴────┴───┴───┴┴─────┴───────────┐ │ │ + │ 'char buf [10]' (type: 'char[10]') │ │ │ + └─────────────────────────────────────────────────┘ └─────────────────┘ + ├────────────────────────┬────────────────────────┤ ├────────┬────────┤ + │ │ + ╭─────────┴────────╮ ╭─────────┴─────────╮ + │capacity: 10 bytes│ │overflow of 3 bytes│ + ╰──────────────────╯ ╰───────────────────╯ + + { dg-end-multiline-output "" { target c++ } } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-17.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-17.c index 6920e8c776f..d46159e56d6 100644 --- a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-17.c +++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-17.c @@ -11,19 +11,21 @@ void test (void) } /* { dg-begin-multiline-output "" } - ┌─────┬─────┬────┬────┬────┐┌─────┬─────┬─────┐ - │ [0] │ [1] │[2] │[3] │[4] ││ [5] │ [6] │ [7] │ - ├─────┼─────┼────┼────┼────┤├─────┼─────┼─────┤ - │ ' ' │ 'w' │'o' │'r' │'l' ││ 'd' │ '!' │ NUL │ - ├─────┴─────┴────┴────┴────┴┴─────┴─────┴─────┤ - │ string literal (type: 'char[8]') │ - └─────────────────────────────────────────────┘ - │ │ │ │ │ │ │ │ - │ │ │ │ │ │ │ │ - v v v v v v v v - ┌─────┬────────────────────────────────────────┬────┐┌─────────────────┐ - │ [0] │ ... │[9] ││ │ - ├─────┴────────────────────────────────────────┴────┤│after valid range│ + ┌────┬────┬────┬────┬────┐┌─────┬─────┬─────┐ + │[0] │[1] │[2] │[3] │[4] ││ [5] │ [6] │ [7] │ + ├────┼────┼────┼────┼────┤├─────┼─────┼─────┤ + │' ' │'w' │'o' │'r' │'l' ││ 'd' │ '!' │ NUL │ + ├────┴────┴────┴────┴────┴┴─────┴─────┴─────┤ + │ string literal (type: 'char[8]') │ + └───────────────────────────────────────────┘ + │ │ │ │ │ │ │ │ + │ │ │ │ │ │ │ │ + v v v v v v v v + ┌─────┬────────────────────┬────┬──────────────┬────┐┌─────────────────┐ + │ [0] │ ... │[5] │ ... │[9] ││ │ + ├─────┼────┬────┬────┬────┬┼────┼──────────────┴────┘│ │ + │ 'h' │'e' │'l' │'l' │'o' ││NUL │ │after valid range│ + ├─────┴────┴────┴────┴────┴┴────┴───────────────────┐│ │ │ 'buf' (type: 'char[10]') ││ │ └───────────────────────────────────────────────────┘└─────────────────┘ ├─────────────────────────┬─────────────────────────┤├────────┬────────┤ diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-18.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-18.c index ea0b88019cd..f54cd80c338 100644 --- a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-18.c +++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-18.c @@ -11,28 +11,34 @@ void test (void) } /* { dg-begin-multiline-output "" } - ┌─────┬─────────┐┌────┬────┬────┬────┬──────┐ - │ [0] │ [1] ││[2] │[3] │[4] │[5] │ [6] │ - ├─────┼─────────┤├────┼────┼────┼────┼──────┤ - │0xe3 │ 0x83 ││0xa1│0xe3│0x82│0xa4│ 0x00 │ - ├─────┴─────────┴┴────┼────┴────┴────┼──────┤ - │ U+30e1 │ U+30a4 │U+0000│ - ├─────────────────────┼──────────────┼──────┤ - │ メ │ イ │ NUL │ - ├─────────────────────┴──────────────┴──────┤ - │ string literal (type: 'char[7]') │ - └───────────────────────────────────────────┘ - │ │ │ │ │ │ │ - │ │ │ │ │ │ │ - v v v v v v v - ┌────┬───────────────────────────┬─────────┐┌──────────────────────────┐ - │[0] │ ... │ [10] ││ │ - ├────┴───────────────────────────┴─────────┤│ after valid range │ - │ 'buf' (type: 'char[11]') ││ │ - └──────────────────────────────────────────┘└──────────────────────────┘ - ├────────────────────┬─────────────────────┤├────────────┬─────────────┤ - │ │ - ╭─────────┴────────╮ ╭─────────┴─────────╮ - │capacity: 11 bytes│ │overflow of 5 bytes│ - ╰──────────────────╯ ╰───────────────────╯ + ┌──────┬────┐┌────┬────┬────┬────┬──────┐ + │ [0] │[1] ││[2] │[3] │[4] │[5] │ [6] │ + ├──────┼────┤├────┼────┼────┼────┼──────┤ + │ 0xe3 │0x83││0xa1│0xe3│0x82│0xa4│ 0x00 │ + ├──────┴────┴┴────┼────┴────┴────┼──────┤ + │ U+30e1 │ U+30a4 │U+0000│ + ├─────────────────┼──────────────┼──────┤ + │ メ │ イ │ NUL │ + ├─────────────────┴──────────────┴──────┤ + │ string literal (type: 'char[7]') │ + └───────────────────────────────────────┘ + │ │ │ │ │ │ │ + │ │ │ │ │ │ │ + v v v v v v v + ┌────┬────────────────────────────────────────┬──────┬────┐┌──────────────────────────┐ + │[0] │ ... │ [9] │[10]││ │ + ├────┼────┬────┬────┬────┬────┬────┬────┬────┬┼──────┼────┘│ │ + │0xe3│0x82│0xb5│0xe3│0x83│0x84│0xe3│0x82│0xad││ 0x00 │ │ │ + ├────┴────┴────┼────┴────┴────┼────┴────┴────┤├──────┤ │ │ + │ U+30b5 │ U+30c4 │ U+30ad ││U+0000│ │ after valid range │ + ├──────────────┼──────────────┼──────────────┤├──────┤ │ │ + │ サ │ ツ │ キ ││ NUL │ │ │ + ├──────────────┴──────────────┴──────────────┴┴──────┴────┐│ │ + │ 'buf' (type: 'char[11]') ││ │ + └─────────────────────────────────────────────────────────┘└──────────────────────────┘ + ├────────────────────────────┬────────────────────────────┤├────────────┬─────────────┤ + │ │ + ╭─────────┴────────╮ ╭─────────┴─────────╮ + │capacity: 11 bytes│ │overflow of 5 bytes│ + ╰──────────────────╯ ╰───────────────────╯ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-19.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-19.c index 35ab72b6efc..6af5c0f301b 100644 --- a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-19.c +++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-19.c @@ -22,24 +22,26 @@ test_long_string () } /* { dg-begin-multiline-output "" } - ┌───┬───┬───┬───┬───┬───┬───────┬─────┬─────┬─────┬─────┬─────┬─────┐ - │[0]│[1]│[2]│[3]│[4]│[5]│ │[440]│[441]│[442]│[443]│[444]│[445]│ - ├───┼───┼───┼───┼───┼───┤ ... ├─────┼─────┼─────┼─────┼─────┼─────┤ - │'L'│'o'│'r'│'e'│'m'│' '│ │ 'o' │ 'r' │ 'u' │ 'm' │ '.' │ NUL │ - ├───┴───┴───┴───┴───┴───┴───────┴─────┴─────┴─────┴─────┴─────┴─────┤ - │ string literal (type: 'char[446]') │ - └───────────────────────────────────────────────────────────────────┘ - │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ - │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ - v v v v v v v v v v v v v v v - ┌───┬──────────────────────────┬────┐┌────────────────────────────────────┐ - │[0]│ ... │[99]││ │ - ├───┴──────────────────────────┴────┤│ after valid range │ - │ 'buf' (type: 'char[100]') ││ │ - └───────────────────────────────────┘└────────────────────────────────────┘ - ├─────────────────┬─────────────────┤├─────────────────┬──────────────────┤ - │ │ - ╭─────────┴─────────╮ ╭──────────┴──────────╮ - │capacity: 100 bytes│ │overflow of 350 bytes│ - ╰───────────────────╯ ╰─────────────────────╯ + ┌───┬───┬───┬───┬───┬───┬───────┬─────┬─────┬─────┬─────┬─────┬─────┐ + │[0]│[1]│[2]│[3]│[4]│[5]│ │[440]│[441]│[442]│[443]│[444]│[445]│ + ├───┼───┼───┼───┼───┼───┤ ... ├─────┼─────┼─────┼─────┼─────┼─────┤ + │'L'│'o'│'r'│'e'│'m'│' '│ │ 'o' │ 'r' │ 'u' │ 'm' │ '.' │ NUL │ + ├───┴───┴───┴───┴───┴───┴───────┴─────┴─────┴─────┴─────┴─────┴─────┤ + │ string literal (type: 'char[446]') │ + └───────────────────────────────────────────────────────────────────┘ + │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ + │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ + v v v v v v v v v v v v v v v + ┌───┬────────────┬───┬────────────────────┬────┐┌────────────────────────────────────┐ + │[0]│ ... │[4]│ ... │[99]││ │ + ├───┼───┬───┬───┬┼───┼────────────────────┴────┘│ │ + │'a'│'b'│'c'│' '││NUL│ │ after valid range │ + ├───┴───┴───┴───┴┴───┴─────────────────────────┐│ │ + │ 'buf' (type: 'char[100]') ││ │ + └──────────────────────────────────────────────┘└────────────────────────────────────┘ + ├──────────────────────┬───────────────────────┤├─────────────────┬──────────────────┤ + │ │ + ╭─────────┴─────────╮ ╭──────────┴──────────╮ + │capacity: 100 bytes│ │overflow of 350 bytes│ + ╰───────────────────╯ ╰─────────────────────╯ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-6.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-6.c index 25bf9d53b2b..ad320964057 100644 --- a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-6.c +++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-diagram-6.c @@ -33,22 +33,24 @@ test_bad_memcpy () /* { dg-begin-multiline-output "" } - ┌─────────────────────────────────────────────────────────────────────────┐ - │ read of 4096 bytes │ - └─────────────────────────────────────────────────────────────────────────┘ - ^ ^ ^ ^ ^ - │ │ │ │ │ - │ │ │ │ │ - ┌──────────────────┐┌──────────┬──────────┬────────────┐┌─────────────────┐ - │ ││ [0] │ ... │ [445] ││ │ - │before valid range│├──────────┴──────────┴────────────┤│after valid range│ - │ ││string literal (type: 'char[446]')││ │ - └──────────────────┘└──────────────────────────────────┘└─────────────────┘ - ├────────┬─────────┤├────────────────┬─────────────────┤├────────┬────────┤ - │ │ │ - ╭────────┴──────────────╮ ╭───────┴───────╮ ╭───────────┴───────────╮ - │under-read of 100 bytes│ │size: 446 bytes│ │over-read of 3550 bytes│ - ╰───────────────────────╯ ╰───────────────╯ ╰───────────────────────╯ + ┌────────────────────────────────────────────────────────────────────────────────────────────┐ + │ read of 4096 bytes │ + └────────────────────────────────────────────────────────────────────────────────────────────┘ + ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ + │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ + │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ + ┌──────────────────┐┌───┬───────────────────────────────────────────┬─────┐┌─────────────────┐ + │ ││[0]│ ... │[445]││ │ + │ │├───┼───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┼─────┤│ │ + │before valid range││'L'│'o'│'r'│'e'│'m'│' '│...│'o'│'r'│'u'│'m'│'.'│ NUL ││after valid range│ + │ │├───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴─────┤│ │ + │ ││ string literal (type: 'char[446]') ││ │ + └──────────────────┘└─────────────────────────────────────────────────────┘└─────────────────┘ + ├────────┬─────────┤├──────────────────────────┬──────────────────────────┤├────────┬────────┤ + │ │ │ + ╭────────┴──────────────╮ ╭───────┴───────╮ ╭───────────┴───────────╮ + │under-read of 100 bytes│ │size: 446 bytes│ │over-read of 3550 bytes│ + ╰───────────────────────╯ ╰───────────────╯ ╰───────────────────────╯ { dg-end-multiline-output "" } */ @@ -81,22 +83,24 @@ test_bad_memcpy () /* { dg-begin-multiline-output "" } - ┌─────────────────────────────────────────────────────────────────────────┐ - │ read of 4096 bytes │ - └─────────────────────────────────────────────────────────────────────────┘ - ^ ^ ^ ^ ^ - │ │ │ │ │ - │ │ │ │ │ - ┌──────────────────┐┌──────────┬──────────┬────────────┐┌─────────────────┐ - │ ││ [0] │ ... │ [445] ││ │ - │before valid range│├──────────┴──────────┴────────────┤│after valid range│ - │ ││string literal (type: 'char[446]')││ │ - └──────────────────┘└──────────────────────────────────┘└─────────────────┘ - ├────────┬─────────┤├────────────────┬─────────────────┤├────────┬────────┤ - │ │ │ - ╭────────┴──────────────╮ ╭───────┴───────╮ ╭───────────┴───────────╮ - │under-read of 100 bytes│ │size: 446 bytes│ │over-read of 3550 bytes│ - ╰───────────────────────╯ ╰───────────────╯ ╰───────────────────────╯ + ┌────────────────────────────────────────────────────────────────────────────────────────────┐ + │ read of 4096 bytes │ + └────────────────────────────────────────────────────────────────────────────────────────────┘ + ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ + │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ + │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ + ┌──────────────────┐┌───┬───────────────────────────────────────────┬─────┐┌─────────────────┐ + │ ││[0]│ ... │[445]││ │ + │ │├───┼───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┼─────┤│ │ + │before valid range││'L'│'o'│'r'│'e'│'m'│' '│...│'o'│'r'│'u'│'m'│'.'│ NUL ││after valid range│ + │ │├───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴─────┤│ │ + │ ││ string literal (type: 'char[446]') ││ │ + └──────────────────┘└─────────────────────────────────────────────────────┘└─────────────────┘ + ├────────┬─────────┤├──────────────────────────┬──────────────────────────┤├────────┬────────┤ + │ │ │ + ╭────────┴──────────────╮ ╭───────┴───────╮ ╭───────────┴───────────╮ + │under-read of 100 bytes│ │size: 446 bytes│ │over-read of 3550 bytes│ + ╰───────────────────────╯ ╰───────────────╯ ╰───────────────────────╯ { dg-end-multiline-output "" } */ diff --git a/gcc/text-art/table.cc b/gcc/text-art/table.cc index 2f857a0e2a7..2ea0da3a3d8 100644 --- a/gcc/text-art/table.cc +++ b/gcc/text-art/table.cc @@ -150,6 +150,26 @@ table::set_cell_span (rect_t span, } } +/* If SPAN is unoccuped, set it to CONTENT. + Otherwise, discard CONTENT. */ + +void +table::maybe_set_cell_span (rect_t span, + table_cell_content &&content, + enum x_align x_align, + enum y_align y_align) +{ + gcc_assert (span.m_size.w > 0); + gcc_assert (span.m_size.h > 0); + for (int y = span.get_min_y (); y < span.get_next_y (); y++) + for (int x = span.get_min_x (); x < span.get_next_x (); x++) + { + if (m_occupancy.get (coord_t (x, y)) != -1) + return; + } + set_cell_span (span, std::move (content), x_align, y_align); +} + canvas table::to_canvas (const theme &theme, const style_manager &sm) const { @@ -189,6 +209,21 @@ table::debug () const canvas.debug (false); } +/* Move OTHER's content this table, starting at OFFSET. */ + +void +table::add_other_table (table &&other, + table::coord_t offset) +{ + for (auto &&placement : other.m_placements) + { + set_cell_span (placement.m_rect + offset, + std::move (placement.m_content), + placement.m_x_align, + placement.m_y_align); + } +} + const table::cell_placement * table::get_placement_at (coord_t coord) const { diff --git a/gcc/text-art/table.h b/gcc/text-art/table.h index 17eda912f1a..5d5d4bdde1f 100644 --- a/gcc/text-art/table.h +++ b/gcc/text-art/table.h @@ -115,6 +115,7 @@ class table const table_cell_content &get_content () const { return m_content; } private: + friend class table; friend class table_cell_sizes; rect_t m_rect; table_cell_content m_content; @@ -130,11 +131,18 @@ class table const size_t &get_size () const { return m_size; } + int add_rows (unsigned num) + { + int topmost_new_row = m_size.h; + m_size.h += num; + for (unsigned i = 0; i < num; i++) + m_occupancy.add_row (-1); + return topmost_new_row; + } + int add_row () { - m_size.h++; - m_occupancy.add_row (-1); - return m_size.h - 1; // return the table_y of the newly-added row + return add_rows (1); } void set_cell (coord_t coord, @@ -147,6 +155,11 @@ class table enum x_align x_align = x_align::CENTER, enum y_align y_align = y_align::CENTER); + void maybe_set_cell_span (rect_t span, + table_cell_content &&content, + enum x_align x_align = x_align::CENTER, + enum y_align y_align = y_align::CENTER); + canvas to_canvas (const theme &theme, const style_manager &sm) const; void paint_to_canvas(canvas &canvas, @@ -156,6 +169,8 @@ class table void debug () const; + void add_other_table (table &&other, table::coord_t offset); + /* Self-test support. */ const cell_placement *get_placement_at (coord_t coord) const; diff --git a/gcc/text-art/types.h b/gcc/text-art/types.h index ea4ff4be8cc..d5394a92d6f 100644 --- a/gcc/text-art/types.h +++ b/gcc/text-art/types.h @@ -129,6 +129,13 @@ struct rect size m_size; }; +template +rect operator+ (rect r, + coord offset) +{ + return rect (r.m_top_left + offset, r.m_size); +} + template class array2 {