From patchwork Sat Dec 16 14:05:44 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Malcolm X-Patchwork-Id: 179852 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:7300:24d3:b0:fb:cd0c:d3e with SMTP id r19csp239431dyi; Sat, 16 Dec 2023 06:06:18 -0800 (PST) X-Google-Smtp-Source: AGHT+IEiua2hs4VXvlwuiuohuHGB/26BnTvXlT+eugMxvwaDqZTTayIE1q8K5jsfg+U5NfvUgdpt X-Received: by 2002:a05:620a:66b:b0:77e:fba3:9d20 with SMTP id a11-20020a05620a066b00b0077efba39d20mr13436905qkh.132.1702735578321; Sat, 16 Dec 2023 06:06:18 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1702735578; cv=pass; d=google.com; s=arc-20160816; b=R+YzVORuHjNJ/OlhOPcCnS8DAAuNtVCGQ7iSsctyJsqIM1EAF4m/mtx1Tw8wljQOcx GI0jKMLout7adX2NQSa+vAeXzizt4r5WL6jLyt/J/8BisdMDBs78ufzpr/+9MWyZbiCm uVlXJeAdvVElkLnfDVrerPwS0eouhtqcaCIl98Ro4Z6YtmUgxOjGTeStEmSIatcv7chW K6BC6Eylufq7GctDAyolp58ogtUR9CQ5jrhpwAExSZrll0AUCmFsrRtbJU/j5i4CkUn1 daaDzrPc3nKQQwrr/Gq+NVN3qPo4efTgpA6GWUUTnSOXjZ7XJqFf2Mr8upNWtix+dZCp XXpg== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:message-id:date:subject:cc:to:from:dkim-signature :arc-filter:dmarc-filter:delivered-to; bh=EiiqyQjtOc26pNAlYhfkxBnTTotuF51xqQcLR5CpUEY=; fh=NXemEfxTRbZtBxUkxR2ehQUaYlcDfMdzPkO8MChVQE4=; b=DehAzOBO8RjyZpFvGz5giEtO5nBTXL2O0g555NS/3UH6xu3Rc8zU9qFBmYJNBV9wL8 5OqRBJGO5FRs5zhKtrquYinu1f8+AoFSxQrNzO4g7QNpuYshrVlr5jGUwJMcXVjYjwWb tgrBOYRsC93uZCF4lcJsUbe3lUoFTUe4UxA9dfV4x0bZt3sgaTJif+fhOO7lOTPqJoRz npNEYV9MZyKjEZYIs7kzSWWVsaM+F7m5pvnz2GyDj+iYGjO8Opf1CxFCh76rYUhqW/1l kwjrapsxyYwg/rVIqwgfWm2dR/43VRwRxPpobwnyMKmPSCt6f+jjchQJPV0J5hlIbAFz u2LQ== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=TBPSB0uI; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (server2.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id f19-20020a67ea93000000b00464866dec9fsi3733570vso.274.2023.12.16.06.06.18 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 16 Dec 2023 06:06:18 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=TBPSB0uI; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id EF8943858D28 for ; Sat, 16 Dec 2023 14:06:17 +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.129.124]) by sourceware.org (Postfix) with ESMTPS id C5E4B3858D28 for ; Sat, 16 Dec 2023 14:05:48 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C5E4B3858D28 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org C5E4B3858D28 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1702735553; cv=none; b=NNAIG9WqasriIRSi9eNdOX/jxsa7++hQtcNw3+Mfas9mEaDU/EaD7m+oD+RYfsXaXOfjJRarcBiez3CiWjgw58ZqgmxOzBC4g1Yda6vhGuAlSvMLxRo0LQAC9J1JzXDEUYVdVD23nCfD+L0Hhq/Kuba7sDg7Gya324NM3HKj5og= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1702735553; c=relaxed/simple; bh=KdzqBl22zm73FPmOifV1opSjBhQK6phc8ZT7vKed+1U=; h=DKIM-Signature:From:To:Subject:Date:Message-Id:MIME-Version; b=J8T0KhOO3oGHmp3Rukl2CrWOy3/5OBvEyvsAOyphJkqESjDKa7qW2C3UtR/OrwZn54FX55MxXh3uidWmY4XJA3Af0nYcj4Dva1H97zLCw3zh6mWOkYZAr6IBwNFf4LkjpGPlH/DgWT9Fljm4fzH8cHWn/Elb/vJpagDYArcQ1RQ= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1702735548; 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=EiiqyQjtOc26pNAlYhfkxBnTTotuF51xqQcLR5CpUEY=; b=TBPSB0uIE3Pf70rcx3sm2jnlBKTfgXPZ6pBBJ4mm9koG0UNCxj1XxcaKZ5meh0ZOnvt+mc T7N0l5gDifd02v7DjkEJiP/rBU4s/GG4OJjIiPqkFV5yvRtSMAYNApukzzYAwGREIeFMTP xLd0LPQwAo6pJAkmhFjKpjEEp7P12xI= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-17-pD0_lX5pOPWf4c_EwH5SFQ-1; Sat, 16 Dec 2023 09:05:46 -0500 X-MC-Unique: pD0_lX5pOPWf4c_EwH5SFQ-1 Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.rdu2.redhat.com [10.11.54.6]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 94FC92806402 for ; Sat, 16 Dec 2023 14:05:46 +0000 (UTC) Received: from t14s.localdomain.com (unknown [10.22.17.251]) by smtp.corp.redhat.com (Postfix) with ESMTP id 46DDA2166B31; Sat, 16 Dec 2023 14:05:46 +0000 (UTC) From: David Malcolm To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [pushed] analyzer: use bit-level granularity for concrete bounds-checking [PR112792] Date: Sat, 16 Dec 2023 09:05:44 -0500 Message-Id: <20231216140544.3424839-1-dmalcolm@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.6 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-10.9 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE, URIBL_BLACK 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+ouuuleilei=gmail.com@gcc.gnu.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1785447661683313834 X-GMAIL-MSGID: 1785447661683313834 PR analyzer/112792 reports false positives from -fanalyzer's bounds-checking on certain packed structs containing bitfields e.g. in the Linux kernel's drivers/dma/idxd/device.c: union msix_perm { struct { u32 rsvd2 : 8; u32 pasid : 20; }; u32 bits; } __attribute__((__packed__)); The root cause is that the bounds-checking is done using byte offsets and ranges; in the above, an access of "pasid" is treated as a 32-bit access starting one byte inside the union, thus accessing byte offsets 1-4 when only offsets 0-3 are valid. This patch updates the bounds-checking to use bit offsets and ranges wherever possible - for concrete offsets and capacities. In the above accessing "pasid" is treated as bits 8-27 of a 32-bit region, fixing the false positive. Symbolic offsets and ranges are still handled at byte granularity. Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. Successful run of analyzer integration tests on x86_64-pc-linux-gnu. Pushed to trunk as r14-6622-g5f1bed2a7af828. gcc/analyzer/ChangeLog: PR analyzer/112792 * bounds-checking.cc (out_of_bounds::oob_region_creation_event_capacity): Rename "capacity" to "byte_capacity". Layout fix. (out_of_bounds::::add_region_creation_events): Rename "capacity" to "byte_capacity". (class concrete_out_of_bounds): Rename m_out_of_bounds_range to m_out_of_bounds_bits and convert from a byte_range to a bit_range. (concrete_out_of_bounds::get_out_of_bounds_bytes): New. (concrete_past_the_end::concrete_past_the_end): Rename param "byte_bound" to "bit_bound". Initialize m_byte_bound. (concrete_past_the_end::subclass_equal_p): Update for renaming of m_byte_bound to m_bit_bound. (concrete_past_the_end::m_bit_bound): New field. (concrete_buffer_overflow::concrete_buffer_overflow): Convert param "range" from byte_range to bit_range. Rename param "byte_bound" to "bit_bound". (concrete_buffer_overflow::emit): Update for bits vs bytes. (concrete_buffer_overflow::describe_final_event): Split into... (concrete_buffer_overflow::describe_final_event_as_bytes): ...this (concrete_buffer_overflow::describe_final_event_as_bits): ...and this. (concrete_buffer_over_read::concrete_buffer_over_read): Convert param "range" from byte_range to bit_range. Rename param "byte_bound" to "bit_bound". (concrete_buffer_over_read::emit): Update for bits vs bytes. (concrete_buffer_over_read::describe_final_event): Split into... (concrete_buffer_over_read::describe_final_event_as_bytes): ...this (concrete_buffer_over_read::describe_final_event_as_bits): ...and this. (concrete_buffer_underwrite::concrete_buffer_underwrite): Convert param "range" from byte_range to bit_range. (concrete_buffer_underwrite::describe_final_event): Split into... (concrete_buffer_underwrite::describe_final_event_as_bytes): ...this (concrete_buffer_underwrite::describe_final_event_as_bits): ...and this. (concrete_buffer_under_read::concrete_buffer_under_read): Convert param "range" from byte_range to bit_range. (concrete_buffer_under_read::describe_final_event): Split into... (concrete_buffer_under_read::describe_final_event_as_bytes): ...this (concrete_buffer_under_read::describe_final_event_as_bits): ...and this. (region_model::check_region_bounds): Use bits for concrete values, and rename locals to indicate whether we're dealing with bits or bytes. Specifically, replace "num_bytes_sval" with "num_bits_sval", and get it from reg's "get_bit_size_sval". Replace "num_bytes_tree" with "num_bits_tree". Rename "capacity" to "byte_capacity". Rename "cst_capacity_tree" to "cst_byte_capacity_tree". Replace "offset" and "num_bytes_unsigned" with "bit_offset" and "num_bits_unsigned" respectively, converting from byte_offset_t to bit_offset_t. Replace "out" and "read_bytes" with "bits_outside" and "read_bits" respectively, converting from byte_range to bit_range. Convert "buffer" from byte_range to bit_range. Replace "byte_bound" with "bit_bound". * region.cc (region::get_bit_size_sval): New. (offset_region::get_bit_offset): New. (offset_region::get_bit_size_sval): New. (sized_region::get_bit_size_sval): New. (bit_range_region::get_bit_size_sval): New. * region.h (region::get_bit_size_sval): New vfunc. (offset_region::get_bit_offset): New decl. (offset_region::get_bit_size_sval): New decl. (sized_region::get_bit_size_sval): New decl. (bit_range_region::get_bit_size_sval): New decl. * store.cc (bit_range::intersects_p): New, based on byte_range::intersects_p. (bit_range::exceeds_p): New, based on byte_range::exceeds_p. (bit_range::falls_short_of_p): New, based on byte_range::falls_short_of_p. (byte_range::intersects_p): Delete. (byte_range::exceeds_p): Delete. (byte_range::falls_short_of_p): Delete. * store.h (bit_range::intersects_p): New overload. (bit_range::exceeds_p): New. (bit_range::falls_short_of_p): New. (byte_range::intersects_p): Delete. (byte_range::exceeds_p): Delete. (byte_range::falls_short_of_p): Delete. gcc/testsuite/ChangeLog: PR analyzer/112792 * c-c++-common/analyzer/out-of-bounds-pr112792.c: New test. Signed-off-by: David Malcolm --- gcc/analyzer/bounds-checking.cc | 435 ++++++++++++++---- gcc/analyzer/region.cc | 71 +++ gcc/analyzer/region.h | 12 +- gcc/analyzer/store.cc | 142 +++--- gcc/analyzer/store.h | 17 +- .../analyzer/out-of-bounds-pr112792.c | 18 + 6 files changed, 512 insertions(+), 183 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/analyzer/out-of-bounds-pr112792.c diff --git a/gcc/analyzer/bounds-checking.cc b/gcc/analyzer/bounds-checking.cc index cc43ecc54683..7cbfe91515f8 100644 --- a/gcc/analyzer/bounds-checking.cc +++ b/gcc/analyzer/bounds-checking.cc @@ -50,12 +50,12 @@ public: class oob_region_creation_event_capacity : public region_creation_event_capacity { public: - oob_region_creation_event_capacity (tree capacity, + oob_region_creation_event_capacity (tree byte_capacity, const event_loc_info &loc_info, out_of_bounds &oob) - : region_creation_event_capacity (capacity, - loc_info), - m_oob (oob) + : region_creation_event_capacity (byte_capacity, + loc_info), + m_oob (oob) { } void prepare_for_emission (checker_path *path, @@ -67,7 +67,7 @@ public: emission_id); m_oob.m_region_creation_event_id = emission_id; } - private: + private: out_of_bounds &m_oob; }; @@ -97,15 +97,16 @@ public: } void add_region_creation_events (const region *, - tree capacity, + tree byte_capacity, const event_loc_info &loc_info, checker_path &emission_path) override { /* The memory space is described in the diagnostic message itself, so we don't need an event for that. */ - if (capacity) + if (byte_capacity) emission_path.add_event - (make_unique (capacity, loc_info, + (make_unique (byte_capacity, + loc_info, *this)); } @@ -205,10 +206,10 @@ class concrete_out_of_bounds : public out_of_bounds public: concrete_out_of_bounds (const region_model &model, const region *reg, tree diag_arg, - byte_range out_of_bounds_range, + bit_range out_of_bounds_bits, const svalue *sval_hint) : out_of_bounds (model, reg, diag_arg, sval_hint), - m_out_of_bounds_range (out_of_bounds_range) + m_out_of_bounds_bits (out_of_bounds_bits) {} bool subclass_equal_p (const pending_diagnostic &base_other) const override @@ -216,11 +217,16 @@ public: const concrete_out_of_bounds &other (static_cast (base_other)); return (out_of_bounds::subclass_equal_p (other) - && m_out_of_bounds_range == other.m_out_of_bounds_range); + && m_out_of_bounds_bits == other.m_out_of_bounds_bits); + } + + bool get_out_of_bounds_bytes (byte_range *out) const + { + return m_out_of_bounds_bits.as_byte_range (out); } protected: - byte_range m_out_of_bounds_range; + bit_range m_out_of_bounds_bits; }; /* Abstract subclass to complaing about concrete out-of-bounds @@ -230,12 +236,18 @@ class concrete_past_the_end : public concrete_out_of_bounds { public: concrete_past_the_end (const region_model &model, - const region *reg, tree diag_arg, byte_range range, - tree byte_bound, + const region *reg, tree diag_arg, bit_range range, + tree bit_bound, const svalue *sval_hint) : concrete_out_of_bounds (model, reg, diag_arg, range, sval_hint), - m_byte_bound (byte_bound) - {} + m_bit_bound (bit_bound), + m_byte_bound (NULL_TREE) + { + if (m_bit_bound && TREE_CODE (m_bit_bound) == INTEGER_CST) + m_byte_bound + = wide_int_to_tree (size_type_node, + wi::to_offset (m_bit_bound) >> LOG2_BITS_PER_UNIT); + } bool subclass_equal_p (const pending_diagnostic &base_other) const final override @@ -243,8 +255,8 @@ public: const concrete_past_the_end &other (static_cast (base_other)); return (concrete_out_of_bounds::subclass_equal_p (other) - && pending_diagnostic::same_tree_p (m_byte_bound, - other.m_byte_bound)); + && pending_diagnostic::same_tree_p (m_bit_bound, + other.m_bit_bound)); } void add_region_creation_events (const region *, @@ -260,6 +272,7 @@ public: } protected: + tree m_bit_bound; tree m_byte_bound; }; @@ -270,9 +283,9 @@ class concrete_buffer_overflow : public concrete_past_the_end public: concrete_buffer_overflow (const region_model &model, const region *reg, tree diag_arg, - byte_range range, tree byte_bound, + bit_range range, tree bit_bound, const svalue *sval_hint) - : concrete_past_the_end (model, reg, diag_arg, range, byte_bound, sval_hint) + : concrete_past_the_end (model, reg, diag_arg, range, bit_bound, sval_hint) {} const char *get_kind () const final override @@ -301,23 +314,44 @@ public: if (warned) { - if (wi::fits_uhwi_p (m_out_of_bounds_range.m_size_in_bytes)) + if (wi::fits_uhwi_p (m_out_of_bounds_bits.m_size_in_bits)) { - unsigned HOST_WIDE_INT num_bad_bytes - = m_out_of_bounds_range.m_size_in_bytes.to_uhwi (); - if (m_diag_arg) - inform_n (ctxt.get_location (), - num_bad_bytes, - "write of %wu byte to beyond the end of %qE", - "write of %wu bytes to beyond the end of %qE", - num_bad_bytes, - m_diag_arg); + unsigned HOST_WIDE_INT num_bad_bits + = m_out_of_bounds_bits.m_size_in_bits.to_uhwi (); + if (num_bad_bits % BITS_PER_UNIT == 0) + { + unsigned HOST_WIDE_INT num_bad_bytes + = num_bad_bits / BITS_PER_UNIT; + if (m_diag_arg) + inform_n (ctxt.get_location (), + num_bad_bytes, + "write of %wu byte to beyond the end of %qE", + "write of %wu bytes to beyond the end of %qE", + num_bad_bytes, + m_diag_arg); + else + inform_n (ctxt.get_location (), + num_bad_bytes, + "write of %wu byte to beyond the end of the region", + "write of %wu bytes to beyond the end of the region", + num_bad_bytes); + } else - inform_n (ctxt.get_location (), - num_bad_bytes, - "write of %wu byte to beyond the end of the region", - "write of %wu bytes to beyond the end of the region", - num_bad_bytes); + { + if (m_diag_arg) + inform_n (ctxt.get_location (), + num_bad_bits, + "write of %wu bit to beyond the end of %qE", + "write of %wu bits to beyond the end of %qE", + num_bad_bits, + m_diag_arg); + else + inform_n (ctxt.get_location (), + num_bad_bits, + "write of %wu bit to beyond the end of the region", + "write of %wu bits to beyond the end of the region", + num_bad_bits); + } } else if (m_diag_arg) inform (ctxt.get_location (), @@ -331,10 +365,23 @@ public: } label_text describe_final_event (const evdesc::final_event &ev) - final override + final override { - byte_size_t start = m_out_of_bounds_range.get_start_byte_offset (); - byte_size_t end = m_out_of_bounds_range.get_last_byte_offset (); + if (m_byte_bound || !m_bit_bound) + { + byte_range out_of_bounds_bytes (0, 0); + if (get_out_of_bounds_bytes (&out_of_bounds_bytes)) + return describe_final_event_as_bytes (ev, out_of_bounds_bytes); + } + return describe_final_event_as_bits (ev); + } + + label_text + describe_final_event_as_bytes (const evdesc::final_event &ev, + const byte_range &out_of_bounds_bytes) + { + byte_size_t start = out_of_bounds_bytes.get_start_byte_offset (); + byte_size_t end = out_of_bounds_bytes.get_last_byte_offset (); char start_buf[WIDE_INT_PRINT_BUFFER_SIZE]; print_dec (start, start_buf, SIGNED); char end_buf[WIDE_INT_PRINT_BUFFER_SIZE]; @@ -345,10 +392,10 @@ public: if (m_diag_arg) return ev.formatted_print ("out-of-bounds write at byte %s but %qE" " ends at byte %E", start_buf, m_diag_arg, - m_byte_bound); + m_byte_bound); return ev.formatted_print ("out-of-bounds write at byte %s but region" " ends at byte %E", start_buf, - m_byte_bound); + m_byte_bound); } else { @@ -363,6 +410,38 @@ public: } } + label_text describe_final_event_as_bits (const evdesc::final_event &ev) + { + bit_size_t start = m_out_of_bounds_bits.get_start_bit_offset (); + bit_size_t end = m_out_of_bounds_bits.get_last_bit_offset (); + char start_buf[WIDE_INT_PRINT_BUFFER_SIZE]; + print_dec (start, start_buf, SIGNED); + char end_buf[WIDE_INT_PRINT_BUFFER_SIZE]; + print_dec (end, end_buf, SIGNED); + + if (start == end) + { + if (m_diag_arg) + return ev.formatted_print ("out-of-bounds write at bit %s but %qE" + " ends at bit %E", start_buf, m_diag_arg, + m_bit_bound); + return ev.formatted_print ("out-of-bounds write at bit %s but region" + " ends at bit %E", start_buf, + m_bit_bound); + } + else + { + if (m_diag_arg) + return ev.formatted_print ("out-of-bounds write from bit %s till" + " bit %s but %qE ends at bit %E", + start_buf, end_buf, m_diag_arg, + m_bit_bound); + return ev.formatted_print ("out-of-bounds write from bit %s till" + " bit %s but region ends at bit %E", + start_buf, end_buf, m_bit_bound); + } + } + enum access_direction get_dir () const final override { return DIR_WRITE; } }; @@ -373,8 +452,8 @@ class concrete_buffer_over_read : public concrete_past_the_end public: concrete_buffer_over_read (const region_model &model, const region *reg, tree diag_arg, - byte_range range, tree byte_bound) - : concrete_past_the_end (model, reg, diag_arg, range, byte_bound, NULL) + bit_range range, tree bit_bound) + : concrete_past_the_end (model, reg, diag_arg, range, bit_bound, NULL) {} const char *get_kind () const final override @@ -401,23 +480,44 @@ public: if (warned) { - if (wi::fits_uhwi_p (m_out_of_bounds_range.m_size_in_bytes)) + if (wi::fits_uhwi_p (m_out_of_bounds_bits.m_size_in_bits)) { - unsigned HOST_WIDE_INT num_bad_bytes - = m_out_of_bounds_range.m_size_in_bytes.to_uhwi (); - if (m_diag_arg) - inform_n (ctxt.get_location (), - num_bad_bytes, - "read of %wu byte from after the end of %qE", - "read of %wu bytes from after the end of %qE", - num_bad_bytes, - m_diag_arg); + unsigned HOST_WIDE_INT num_bad_bits + = m_out_of_bounds_bits.m_size_in_bits.to_uhwi (); + if (num_bad_bits % BITS_PER_UNIT == 0) + { + unsigned HOST_WIDE_INT num_bad_bytes + = num_bad_bits / BITS_PER_UNIT; + if (m_diag_arg) + inform_n (ctxt.get_location (), + num_bad_bytes, + "read of %wu byte from after the end of %qE", + "read of %wu bytes from after the end of %qE", + num_bad_bytes, + m_diag_arg); + else + inform_n (ctxt.get_location (), + num_bad_bytes, + "read of %wu byte from after the end of the region", + "read of %wu bytes from after the end of the region", + num_bad_bytes); + } else - inform_n (ctxt.get_location (), - num_bad_bytes, - "read of %wu byte from after the end of the region", - "read of %wu bytes from after the end of the region", - num_bad_bytes); + { + if (m_diag_arg) + inform_n (ctxt.get_location (), + num_bad_bits, + "read of %wu bit from after the end of %qE", + "read of %wu bits from after the end of %qE", + num_bad_bits, + m_diag_arg); + else + inform_n (ctxt.get_location (), + num_bad_bits, + "read of %wu bit from after the end of the region", + "read of %wu bits from after the end of the region", + num_bad_bits); + } } else if (m_diag_arg) inform (ctxt.get_location (), @@ -431,10 +531,23 @@ public: } label_text describe_final_event (const evdesc::final_event &ev) - final override + final override { - byte_size_t start = m_out_of_bounds_range.get_start_byte_offset (); - byte_size_t end = m_out_of_bounds_range.get_last_byte_offset (); + if (m_byte_bound || !m_bit_bound) + { + byte_range out_of_bounds_bytes (0, 0); + if (get_out_of_bounds_bytes (&out_of_bounds_bytes)) + return describe_final_event_as_bytes (ev, out_of_bounds_bytes); + } + return describe_final_event_as_bits (ev); + } + + label_text + describe_final_event_as_bytes (const evdesc::final_event &ev, + const byte_range &out_of_bounds_bytes) + { + byte_size_t start = out_of_bounds_bytes.get_start_byte_offset (); + byte_size_t end = out_of_bounds_bytes.get_last_byte_offset (); char start_buf[WIDE_INT_PRINT_BUFFER_SIZE]; print_dec (start, start_buf, SIGNED); char end_buf[WIDE_INT_PRINT_BUFFER_SIZE]; @@ -463,6 +576,38 @@ public: } } + label_text describe_final_event_as_bits (const evdesc::final_event &ev) + { + bit_size_t start = m_out_of_bounds_bits.get_start_bit_offset (); + bit_size_t end = m_out_of_bounds_bits.get_last_bit_offset (); + char start_buf[WIDE_INT_PRINT_BUFFER_SIZE]; + print_dec (start, start_buf, SIGNED); + char end_buf[WIDE_INT_PRINT_BUFFER_SIZE]; + print_dec (end, end_buf, SIGNED); + + if (start == end) + { + if (m_diag_arg) + return ev.formatted_print ("out-of-bounds read at bit %s but %qE" + " ends at bit %E", start_buf, m_diag_arg, + m_bit_bound); + return ev.formatted_print ("out-of-bounds read at bit %s but region" + " ends at bit %E", start_buf, + m_bit_bound); + } + else + { + if (m_diag_arg) + return ev.formatted_print ("out-of-bounds read from bit %s till" + " bit %s but %qE ends at bit %E", + start_buf, end_buf, m_diag_arg, + m_bit_bound); + return ev.formatted_print ("out-of-bounds read from bit %s till" + " bit %s but region ends at bit %E", + start_buf, end_buf, m_bit_bound); + } + } + enum access_direction get_dir () const final override { return DIR_READ; } }; @@ -473,7 +618,7 @@ class concrete_buffer_underwrite : public concrete_out_of_bounds public: concrete_buffer_underwrite (const region_model &model, const region *reg, tree diag_arg, - byte_range range, + bit_range range, const svalue *sval_hint) : concrete_out_of_bounds (model, reg, diag_arg, range, sval_hint) {} @@ -505,10 +650,20 @@ public: } label_text describe_final_event (const evdesc::final_event &ev) - final override + final override { - byte_size_t start = m_out_of_bounds_range.get_start_byte_offset (); - byte_size_t end = m_out_of_bounds_range.get_last_byte_offset (); + byte_range out_of_bounds_bytes (0, 0); + if (get_out_of_bounds_bytes (&out_of_bounds_bytes)) + return describe_final_event_as_bytes (ev, out_of_bounds_bytes); + return describe_final_event_as_bits (ev); + } + + label_text + describe_final_event_as_bytes (const evdesc::final_event &ev, + const byte_range &out_of_bounds_bytes) + { + byte_size_t start = out_of_bounds_bytes.get_start_byte_offset (); + byte_size_t end = out_of_bounds_bytes.get_last_byte_offset (); char start_buf[WIDE_INT_PRINT_BUFFER_SIZE]; print_dec (start, start_buf, SIGNED); char end_buf[WIDE_INT_PRINT_BUFFER_SIZE]; @@ -518,8 +673,8 @@ public: { if (m_diag_arg) return ev.formatted_print ("out-of-bounds write at byte %s but %qE" - " starts at byte 0", start_buf, - m_diag_arg); + " starts at byte 0", + start_buf, m_diag_arg); return ev.formatted_print ("out-of-bounds write at byte %s but region" " starts at byte 0", start_buf); } @@ -535,6 +690,37 @@ public: } } + label_text + describe_final_event_as_bits (const evdesc::final_event &ev) + { + bit_size_t start = m_out_of_bounds_bits.get_start_bit_offset (); + bit_size_t end = m_out_of_bounds_bits.get_last_bit_offset (); + char start_buf[WIDE_INT_PRINT_BUFFER_SIZE]; + print_dec (start, start_buf, SIGNED); + char end_buf[WIDE_INT_PRINT_BUFFER_SIZE]; + print_dec (end, end_buf, SIGNED); + + if (start == end) + { + if (m_diag_arg) + return ev.formatted_print ("out-of-bounds write at bit %s but %qE" + " starts at bit 0", + start_buf, m_diag_arg); + return ev.formatted_print ("out-of-bounds write at bit %s but region" + " starts at bit 0", start_buf); + } + else + { + if (m_diag_arg) + return ev.formatted_print ("out-of-bounds write from bit %s till" + " bit %s but %qE starts at bit 0", + start_buf, end_buf, m_diag_arg); + return ev.formatted_print ("out-of-bounds write from bit %s till" + " bit %s but region starts at bit 0", + start_buf, end_buf);; + } + } + enum access_direction get_dir () const final override { return DIR_WRITE; } }; @@ -545,7 +731,7 @@ class concrete_buffer_under_read : public concrete_out_of_bounds public: concrete_buffer_under_read (const region_model &model, const region *reg, tree diag_arg, - byte_range range) + bit_range range) : concrete_out_of_bounds (model, reg, diag_arg, range, NULL) {} @@ -576,10 +762,20 @@ public: } label_text describe_final_event (const evdesc::final_event &ev) - final override + final override { - byte_size_t start = m_out_of_bounds_range.get_start_byte_offset (); - byte_size_t end = m_out_of_bounds_range.get_last_byte_offset (); + byte_range out_of_bounds_bytes (0, 0); + if (get_out_of_bounds_bytes (&out_of_bounds_bytes)) + return describe_final_event_as_bytes (ev, out_of_bounds_bytes); + return describe_final_event_as_bits (ev); + } + + label_text + describe_final_event_as_bytes (const evdesc::final_event &ev, + const byte_range &out_of_bounds_bytes) + { + byte_size_t start = out_of_bounds_bytes.get_start_byte_offset (); + byte_size_t end = out_of_bounds_bytes.get_last_byte_offset (); char start_buf[WIDE_INT_PRINT_BUFFER_SIZE]; print_dec (start, start_buf, SIGNED); char end_buf[WIDE_INT_PRINT_BUFFER_SIZE]; @@ -606,6 +802,36 @@ public: } } + label_text describe_final_event_as_bits (const evdesc::final_event &ev) + { + bit_size_t start = m_out_of_bounds_bits.get_start_bit_offset (); + bit_size_t end = m_out_of_bounds_bits.get_last_bit_offset (); + char start_buf[WIDE_INT_PRINT_BUFFER_SIZE]; + print_dec (start, start_buf, SIGNED); + char end_buf[WIDE_INT_PRINT_BUFFER_SIZE]; + print_dec (end, end_buf, SIGNED); + + if (start == end) + { + if (m_diag_arg) + return ev.formatted_print ("out-of-bounds read at bit %s but %qE" + " starts at bit 0", start_buf, + m_diag_arg); + return ev.formatted_print ("out-of-bounds read at bit %s but region" + " starts at bit 0", start_buf); + } + else + { + if (m_diag_arg) + return ev.formatted_print ("out-of-bounds read from bit %s till" + " bit %s but %qE starts at bit 0", + start_buf, end_buf, m_diag_arg); + return ev.formatted_print ("out-of-bounds read from bit %s till" + " bit %s but region starts at bit 0", + start_buf, end_buf);; + } + } + enum access_direction get_dir () const final override { return DIR_READ; } }; @@ -955,16 +1181,16 @@ region_model::check_region_bounds (const region *reg, region_offset reg_offset = reg->get_offset (m_mgr); const region *base_reg = reg_offset.get_base_region (); - /* Find out how many bytes were accessed. */ - const svalue *num_bytes_sval = reg->get_byte_size_sval (m_mgr); - tree num_bytes_tree = maybe_get_integer_cst_tree (num_bytes_sval); - /* Bail out if 0 bytes are accessed. */ - if (num_bytes_tree && zerop (num_bytes_tree)) + /* Find out how many bits were accessed. */ + const svalue *num_bits_sval = reg->get_bit_size_sval (m_mgr); + tree num_bits_tree = maybe_get_integer_cst_tree (num_bits_sval); + /* Bail out if 0 bits are accessed. */ + if (num_bits_tree && zerop (num_bits_tree)) return true; - /* Get the capacity of the buffer. */ - const svalue *capacity = get_capacity (base_reg); - tree cst_capacity_tree = maybe_get_integer_cst_tree (capacity); + /* Get the capacity of the buffer (in bytes). */ + const svalue *byte_capacity = get_capacity (base_reg); + tree cst_byte_capacity_tree = maybe_get_integer_cst_tree (byte_capacity); /* The constant offset from a pointer is represented internally as a sizetype but should be interpreted as a signed value here. The statement below @@ -973,36 +1199,39 @@ region_model::check_region_bounds (const region *reg, For example, this is needed for out-of-bounds-3.c test1 to pass when compiled with a 64-bit gcc build targeting 32-bit systems. */ - byte_offset_t offset; + bit_offset_t bit_offset; if (!reg_offset.symbolic_p ()) - offset = wi::sext (reg_offset.get_bit_offset () >> LOG2_BITS_PER_UNIT, - TYPE_PRECISION (size_type_node)); + bit_offset = wi::sext (reg_offset.get_bit_offset (), + TYPE_PRECISION (size_type_node)); /* If any of the base region, the offset, or the number of bytes accessed are symbolic, we have to reason about symbolic values. */ - if (base_reg->symbolic_p () || reg_offset.symbolic_p () || !num_bytes_tree) + if (base_reg->symbolic_p () || reg_offset.symbolic_p () || !num_bits_tree) { const svalue* byte_offset_sval; if (!reg_offset.symbolic_p ()) { - tree offset_tree = wide_int_to_tree (integer_type_node, offset); + tree byte_offset_tree + = wide_int_to_tree (integer_type_node, + bit_offset >> LOG2_BITS_PER_UNIT); byte_offset_sval - = m_mgr->get_or_create_constant_svalue (offset_tree); + = m_mgr->get_or_create_constant_svalue (byte_offset_tree); } else byte_offset_sval = reg_offset.get_symbolic_byte_offset (); + const svalue *num_bytes_sval = reg->get_byte_size_sval (m_mgr); return check_symbolic_bounds (base_reg, byte_offset_sval, num_bytes_sval, - capacity, dir, sval_hint, ctxt); + byte_capacity, dir, sval_hint, ctxt); } /* Otherwise continue to check with concrete values. */ - byte_range out (0, 0); + bit_range bits_outside (0, 0); bool oob_safe = true; - /* NUM_BYTES_TREE should always be interpreted as unsigned. */ - byte_offset_t num_bytes_unsigned = wi::to_offset (num_bytes_tree); - byte_range read_bytes (offset, num_bytes_unsigned); - /* If read_bytes has a subset < 0, we do have an underwrite. */ - if (read_bytes.falls_short_of_p (0, &out)) + /* NUM_BITS_TREE should always be interpreted as unsigned. */ + bit_offset_t num_bits_unsigned = wi::to_offset (num_bits_tree); + bit_range read_bits (bit_offset, num_bits_unsigned); + /* If read_bits has a subset < 0, we do have an underwrite. */ + if (read_bits.falls_short_of_p (0, &bits_outside)) { tree diag_arg = get_representative_tree (base_reg); switch (dir) @@ -1014,13 +1243,13 @@ region_model::check_region_bounds (const region *reg, gcc_assert (sval_hint == nullptr); ctxt->warn (make_unique (*this, reg, diag_arg, - out)); + bits_outside)); oob_safe = false; break; case DIR_WRITE: ctxt->warn (make_unique (*this, reg, diag_arg, - out, + bits_outside, sval_hint)); oob_safe = false; break; @@ -1030,15 +1259,15 @@ region_model::check_region_bounds (const region *reg, /* For accesses past the end, we do need a concrete capacity. No need to do a symbolic check here because the inequality check does not reason whether constants are greater than symbolic values. */ - if (!cst_capacity_tree) - return oob_safe; + if (!cst_byte_capacity_tree) + return oob_safe; - byte_range buffer (0, wi::to_offset (cst_capacity_tree)); - /* If READ_BYTES exceeds BUFFER, we do have an overflow. */ - if (read_bytes.exceeds_p (buffer, &out)) + bit_range buffer (0, wi::to_offset (cst_byte_capacity_tree) * BITS_PER_UNIT); + /* If READ_BITS exceeds BUFFER, we do have an overflow. */ + if (read_bits.exceeds_p (buffer, &bits_outside)) { - tree byte_bound = wide_int_to_tree (size_type_node, - buffer.get_next_byte_offset ()); + tree bit_bound = wide_int_to_tree (size_type_node, + buffer.get_next_bit_offset ()); tree diag_arg = get_representative_tree (base_reg); switch (dir) @@ -1050,13 +1279,15 @@ region_model::check_region_bounds (const region *reg, gcc_assert (sval_hint == nullptr); ctxt->warn (make_unique (*this, reg, diag_arg, - out, byte_bound)); + bits_outside, + bit_bound)); oob_safe = false; break; case DIR_WRITE: ctxt->warn (make_unique (*this, reg, diag_arg, - out, byte_bound, + bits_outside, + bit_bound, sval_hint)); oob_safe = false; break; diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc index 9b27e8febcc9..d58d064f9ff1 100644 --- a/gcc/analyzer/region.cc +++ b/gcc/analyzer/region.cc @@ -757,6 +757,24 @@ int_size_in_bits (const_tree type, bit_size_t *out) return false; } +/* Base implementation of region::get_bit_size_sval vfunc. */ + +const svalue * +region::get_bit_size_sval (region_model_manager *mgr) const +{ + tree type = get_type (); + + /* Bail out e.g. for heap-allocated regions. */ + if (!type) + return mgr->get_or_create_unknown_svalue (size_type_node); + + bit_size_t bits; + if (!int_size_in_bits (type, &bits)) + return mgr->get_or_create_unknown_svalue (size_type_node); + + return mgr->get_or_create_int_cst (size_type_node, bits); +} + /* If the size of this region (in bits) is known statically, write it to *OUT and return true. Otherwise return false. */ @@ -1933,6 +1951,15 @@ offset_region::dump_to_pp (pretty_printer *pp, bool simple) const } } +const svalue * +offset_region::get_bit_offset (region_model_manager *mgr) const +{ + const svalue *bits_per_byte_sval + = mgr->get_or_create_int_cst (size_type_node, BITS_PER_UNIT); + return mgr->get_or_create_binop (size_type_node, MULT_EXPR, + m_byte_offset, bits_per_byte_sval); +} + /* Implementation of region::get_relative_concrete_offset vfunc for offset_region. */ @@ -1987,6 +2014,30 @@ offset_region::get_byte_size_sval (region_model_manager *mgr) const return region::get_byte_size_sval (mgr); } +/* Implementation of region::get_bit_size_sval vfunc for offset_region. */ + +const svalue * +offset_region::get_bit_size_sval (region_model_manager *mgr) const +{ + tree offset_cst = get_bit_offset (mgr)->maybe_get_constant (); + bit_size_t bit_size; + /* If the offset points in the middle of the region, + return the remaining bits. */ + if (get_bit_size (&bit_size) && offset_cst) + { + bit_size_t offset = wi::to_offset (offset_cst); + bit_range r (0, bit_size); + if (r.contains_p (offset)) + { + tree remaining_bit_size = wide_int_to_tree (size_type_node, + bit_size - offset); + return mgr->get_or_create_constant_svalue (remaining_bit_size); + } + } + + return region::get_bit_size_sval (mgr); +} + /* class sized_region : public region. */ /* Implementation of region::accept vfunc for sized_region. */ @@ -2047,6 +2098,17 @@ sized_region::get_bit_size (bit_size_t *out) const return true; } +/* Implementation of region::get_bit_size_sval vfunc for sized_region. */ + +const svalue * +sized_region::get_bit_size_sval (region_model_manager *mgr) const +{ + const svalue *bits_per_byte_sval + = mgr->get_or_create_int_cst (size_type_node, BITS_PER_UNIT); + return mgr->get_or_create_binop (size_type_node, MULT_EXPR, + m_byte_size_sval, bits_per_byte_sval); +} + /* class cast_region : public region. */ /* Implementation of region::accept vfunc for cast_region. */ @@ -2198,6 +2260,15 @@ bit_range_region::get_byte_size_sval (region_model_manager *mgr) const return mgr->get_or_create_int_cst (size_type_node, num_bytes); } +/* Implementation of region::get_bit_size_sval vfunc for bit_range_region. */ + +const svalue * +bit_range_region::get_bit_size_sval (region_model_manager *mgr) const +{ + return mgr->get_or_create_int_cst (size_type_node, + m_bits.m_size_in_bits); +} + /* Implementation of region::get_relative_concrete_offset vfunc for bit_range_region. */ diff --git a/gcc/analyzer/region.h b/gcc/analyzer/region.h index fee161cf8630..6b542094064c 100644 --- a/gcc/analyzer/region.h +++ b/gcc/analyzer/region.h @@ -199,6 +199,10 @@ public: (which could be "unknown"). */ virtual const svalue *get_byte_size_sval (region_model_manager *mgr) const; + /* Get a symbolic value describing the size of this region in bits + (which could be "unknown"). */ + virtual const svalue *get_bit_size_sval (region_model_manager *mgr) const; + /* Attempt to get the offset in bits of this region relative to its parent. If successful, return true and write to *OUT. Otherwise return false. */ @@ -969,13 +973,15 @@ public: void dump_to_pp (pretty_printer *pp, bool simple) const final override; const svalue *get_byte_offset () const { return m_byte_offset; } + const svalue *get_bit_offset (region_model_manager *mgr) const; bool get_relative_concrete_offset (bit_offset_t *out) const final override; const svalue *get_relative_symbolic_offset (region_model_manager *mgr) const final override; const svalue * get_byte_size_sval (region_model_manager *mgr) const final override; - + const svalue * get_bit_size_sval (region_model_manager *mgr) + const final override; private: const svalue *m_byte_offset; @@ -1070,6 +1076,9 @@ public: return m_byte_size_sval; } + const svalue * + get_bit_size_sval (region_model_manager *) const final override; + private: const svalue *m_byte_size_sval; }; @@ -1304,6 +1313,7 @@ public: bool get_byte_size (byte_size_t *out) const final override; bool get_bit_size (bit_size_t *out) const final override; const svalue *get_byte_size_sval (region_model_manager *mgr) const final override; + const svalue *get_bit_size_sval (region_model_manager *mgr) const final override; bool get_relative_concrete_offset (bit_offset_t *out) const final override; const svalue *get_relative_symbolic_offset (region_model_manager *mgr) const final override; diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc index be1802e1e5a8..32cb1d968b8a 100644 --- a/gcc/analyzer/store.cc +++ b/gcc/analyzer/store.cc @@ -285,6 +285,77 @@ bit_range::intersects_p (const bit_range &other, return false; } +/* Return true if THIS and OTHER intersect and write the number + of bits both buffers overlap to *OUT_NUM_OVERLAP_BITS. + + Otherwise return false. */ + +bool +bit_range::intersects_p (const bit_range &other, + bit_size_t *out_num_overlap_bits) const +{ + if (get_start_bit_offset () < other.get_next_bit_offset () + && other.get_start_bit_offset () < get_next_bit_offset ()) + { + bit_offset_t overlap_start = MAX (get_start_bit_offset (), + other.get_start_bit_offset ()); + bit_offset_t overlap_next = MIN (get_next_bit_offset (), + other.get_next_bit_offset ()); + gcc_assert (overlap_next > overlap_start); + *out_num_overlap_bits = overlap_next - overlap_start; + return true; + } + else + return false; +} + +/* Return true if THIS exceeds OTHER and write the overhanging + bit range to OUT_OVERHANGING_BIT_RANGE. */ + +bool +bit_range::exceeds_p (const bit_range &other, + bit_range *out_overhanging_bit_range) const +{ + gcc_assert (!empty_p ()); + + if (other.get_next_bit_offset () < get_next_bit_offset ()) + { + /* THIS definitely exceeds OTHER. */ + bit_offset_t start = MAX (get_start_bit_offset (), + other.get_next_bit_offset ()); + bit_offset_t size = get_next_bit_offset () - start; + gcc_assert (size > 0); + out_overhanging_bit_range->m_start_bit_offset = start; + out_overhanging_bit_range->m_size_in_bits = size; + return true; + } + else + return false; +} + +/* Return true if THIS falls short of OFFSET and write the + bit range fallen short to OUT_FALL_SHORT_BITS. */ + +bool +bit_range::falls_short_of_p (bit_offset_t offset, + bit_range *out_fall_short_bits) const +{ + gcc_assert (!empty_p ()); + + if (get_start_bit_offset () < offset) + { + /* THIS falls short of OFFSET. */ + bit_offset_t start = get_start_bit_offset (); + bit_offset_t size = MIN (offset, get_next_bit_offset ()) - start; + gcc_assert (size > 0); + out_fall_short_bits->m_start_bit_offset = start; + out_fall_short_bits->m_size_in_bits = size; + return true; + } + else + return false; +} + int bit_range::cmp (const bit_range &br1, const bit_range &br2) { @@ -431,77 +502,6 @@ byte_range::contains_p (const byte_range &other, byte_range *out) const return false; } -/* Return true if THIS and OTHER intersect and write the number - of bytes both buffers overlap to *OUT_NUM_OVERLAP_BYTES. - - Otherwise return false. */ - -bool -byte_range::intersects_p (const byte_range &other, - byte_size_t *out_num_overlap_bytes) const -{ - if (get_start_byte_offset () < other.get_next_byte_offset () - && other.get_start_byte_offset () < get_next_byte_offset ()) - { - byte_offset_t overlap_start = MAX (get_start_byte_offset (), - other.get_start_byte_offset ()); - byte_offset_t overlap_next = MIN (get_next_byte_offset (), - other.get_next_byte_offset ()); - gcc_assert (overlap_next > overlap_start); - *out_num_overlap_bytes = overlap_next - overlap_start; - return true; - } - else - return false; -} - -/* Return true if THIS exceeds OTHER and write the overhanging - byte range to OUT_OVERHANGING_BYTE_RANGE. */ - -bool -byte_range::exceeds_p (const byte_range &other, - byte_range *out_overhanging_byte_range) const -{ - gcc_assert (!empty_p ()); - - if (other.get_next_byte_offset () < get_next_byte_offset ()) - { - /* THIS definitely exceeds OTHER. */ - byte_offset_t start = MAX (get_start_byte_offset (), - other.get_next_byte_offset ()); - byte_offset_t size = get_next_byte_offset () - start; - gcc_assert (size > 0); - out_overhanging_byte_range->m_start_byte_offset = start; - out_overhanging_byte_range->m_size_in_bytes = size; - return true; - } - else - return false; -} - -/* Return true if THIS falls short of OFFSET and write the - byte range fallen short to OUT_FALL_SHORT_BYTES. */ - -bool -byte_range::falls_short_of_p (byte_offset_t offset, - byte_range *out_fall_short_bytes) const -{ - gcc_assert (!empty_p ()); - - if (get_start_byte_offset () < offset) - { - /* THIS falls short of OFFSET. */ - byte_offset_t start = get_start_byte_offset (); - byte_offset_t size = MIN (offset, get_next_byte_offset ()) - start; - gcc_assert (size > 0); - out_fall_short_bytes->m_start_byte_offset = start; - out_fall_short_bytes->m_size_in_bytes = size; - return true; - } - else - return false; -} - /* qsort comparator for byte ranges. */ int diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h index d75d69d0b7f4..da5c8b6ffaec 100644 --- a/gcc/analyzer/store.h +++ b/gcc/analyzer/store.h @@ -275,10 +275,18 @@ struct bit_range return (get_start_bit_offset () < other.get_next_bit_offset () && other.get_start_bit_offset () < get_next_bit_offset ()); } + bool intersects_p (const bit_range &other, + bit_size_t *out_num_overlap_bits) const; bool intersects_p (const bit_range &other, bit_range *out_this, bit_range *out_other) const; + bool exceeds_p (const bit_range &other, + bit_range *out_overhanging_bit_range) const; + + bool falls_short_of_p (bit_offset_t offset, + bit_range *out_fall_short_bits) const; + static int cmp (const bit_range &br1, const bit_range &br2); bit_range operator- (bit_offset_t offset) const; @@ -321,15 +329,6 @@ struct byte_range && m_size_in_bytes == other.m_size_in_bytes); } - bool intersects_p (const byte_range &other, - byte_size_t *out_num_overlap_bytes) const; - - bool exceeds_p (const byte_range &other, - byte_range *out_overhanging_byte_range) const; - - bool falls_short_of_p (byte_offset_t offset, - byte_range *out_fall_short_bytes) const; - byte_offset_t get_start_byte_offset () const { return m_start_byte_offset; diff --git a/gcc/testsuite/c-c++-common/analyzer/out-of-bounds-pr112792.c b/gcc/testsuite/c-c++-common/analyzer/out-of-bounds-pr112792.c new file mode 100644 index 000000000000..0bc7413ce5dd --- /dev/null +++ b/gcc/testsuite/c-c++-common/analyzer/out-of-bounds-pr112792.c @@ -0,0 +1,18 @@ +/* PR analyzer/112792 ("-Wanalyzer-out-of-bounds false positives seen on + Linux kernel with certain unions"). */ + +typedef unsigned int u32; + +union msix_perm { + struct { + u32 rsvd2 : 8; + u32 pasid : 20; + }; + u32 bits; +} __attribute__((__packed__)); + +union msix_perm mperm; + +void idxd_device_set_perm_entry(u32 pasid) { + mperm.pasid = pasid; /* { dg-bogus "buffer-overflow" } */ +}