From: Tim Lange <mail@tim-lange.me>
To: gcc-patches@gcc.gnu.org
Cc: dmalcolm@redhat.com, Tim Lange <mail@tim-lange.me>
Subject: [PATCH] analyzer: consider empty ranges and zero byte accesses [PR106845]
Date: Sun, 11 Sep 2022 00:19:52 +0200 [thread overview]
Message-ID: <20220910221952.99541-1-mail@tim-lange.me> (raw)
Hi,
see my patch below for a fix of pr106845. I decided to allow bit_ranges
and byte_ranges to have a size of zero and rather only add an assertion
to the functions that assume a non-zero size. That way is more elegant in
the caller than restricting byte_range to only represent non-empty ranges.
- Tim
This patch adds handling of empty ranges in bit_range and byte_range and
adds an assertion to member functions that assume a positive size.
Further, the patch fixes an ICE caused by an empty byte_range passed to
byte_range::exceeds_p.
Regression-tested on Linux x86_64.
2022-09-10 Tim Lange <mail@tim-lange.me>
gcc/analyzer/ChangeLog:
PR analyzer/106845
* region-model.cc (region_model::check_region_bounds):
Bail out if 0 bytes were accessed.
* store.cc (byte_range::dump_to_pp):
Add special case for empty ranges.
(byte_range::exceeds_p): Restrict to non-empty ranges.
(byte_range::falls_short_of_p): Restrict to non-empty ranges.
* store.h (bit_range::empty_p): New function.
(bit_range::get_last_byte_offset): Restrict to non-empty ranges.
(byte_range::empty_p): New function.
(byte_range::get_last_byte_offset): Restrict to non-empty ranges.
gcc/testsuite/ChangeLog:
PR analyzer/106845
* gcc.dg/analyzer/out-of-bounds-zero.c: New test.
* gcc.dg/analyzer/pr106845.c: New test.
---
gcc/analyzer/region-model.cc | 3 +
gcc/analyzer/store.cc | 12 +++-
gcc/analyzer/store.h | 12 ++++
.../gcc.dg/analyzer/out-of-bounds-zero.c | 67 +++++++++++++++++++
gcc/testsuite/gcc.dg/analyzer/pr106845.c | 11 +++
5 files changed, 103 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/analyzer/out-of-bounds-zero.c
create mode 100644 gcc/testsuite/gcc.dg/analyzer/pr106845.c
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index d321e5b6479..82006405373 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -1826,6 +1826,9 @@ region_model::check_region_bounds (const region *reg,
/* 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))
+ return;
/* Get the capacity of the buffer. */
const svalue *capacity = get_capacity (base_reg);
diff --git a/gcc/analyzer/store.cc b/gcc/analyzer/store.cc
index ec5232cb055..1857d95f0b6 100644
--- a/gcc/analyzer/store.cc
+++ b/gcc/analyzer/store.cc
@@ -380,7 +380,11 @@ bit_range::as_byte_range (byte_range *out) const
void
byte_range::dump_to_pp (pretty_printer *pp) const
{
- if (m_size_in_bytes == 1)
+ if (m_size_in_bytes == 0)
+ {
+ pp_string (pp, "empty");
+ }
+ else if (m_size_in_bytes == 1)
{
pp_string (pp, "byte ");
pp_wide_int (pp, m_start_byte_offset, SIGNED);
@@ -455,7 +459,9 @@ bool
byte_range::exceeds_p (const byte_range &other,
byte_range *out_overhanging_byte_range) const
{
- if (other.get_last_byte_offset () < get_last_byte_offset ())
+ 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 (),
@@ -477,6 +483,8 @@ 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. */
diff --git a/gcc/analyzer/store.h b/gcc/analyzer/store.h
index ac8b6853f4b..d172ee756c8 100644
--- a/gcc/analyzer/store.h
+++ b/gcc/analyzer/store.h
@@ -237,6 +237,11 @@ struct bit_range
void dump_to_pp (pretty_printer *pp) const;
void dump () const;
+ bool empty_p () const
+ {
+ return m_size_in_bits == 0;
+ }
+
bit_offset_t get_start_bit_offset () const
{
return m_start_bit_offset;
@@ -247,6 +252,7 @@ struct bit_range
}
bit_offset_t get_last_bit_offset () const
{
+ gcc_assert (!empty_p ());
return get_next_bit_offset () - 1;
}
@@ -297,6 +303,11 @@ struct byte_range
void dump_to_pp (pretty_printer *pp) const;
void dump () const;
+ bool empty_p () const
+ {
+ return m_size_in_bytes == 0;
+ }
+
bool contains_p (byte_offset_t offset) const
{
return (offset >= get_start_byte_offset ()
@@ -329,6 +340,7 @@ struct byte_range
}
byte_offset_t get_last_byte_offset () const
{
+ gcc_assert (!empty_p ());
return m_start_byte_offset + m_size_in_bytes - 1;
}
diff --git a/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-zero.c b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-zero.c
new file mode 100644
index 00000000000..201ca00ebdb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/out-of-bounds-zero.c
@@ -0,0 +1,67 @@
+/* { dg-additional-options "-Wno-stringop-overflow"} */
+/* -Wstringop-overflow= triggers on test5. */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+void test1 (void)
+{
+ int32_t buf[1];
+ /* Zero bytes written on non-zero allocation. */
+ __builtin_memset (buf, 0, 0);
+}
+
+void test2 (void)
+{
+ /* ISO C forbids zero-size arrays but GCC compiles this to an
+ zero-sized array without -Wpedantic. */
+ int32_t buf[0];
+ /* Write on zero capacity. */
+ __builtin_memset (buf, 0, sizeof (int32_t)); /* { dg-line test2 } */
+
+ /* { dg-warning "overflow" "warning" { target *-*-* } test2 } */
+ /* { dg-message "from byte 0 till byte 3" "final event" { target *-*-* } test2 } */
+}
+
+void test3 (void)
+{
+ int32_t buf[0];
+ /* Zero bytes written on zero capacity. */
+ __builtin_memset (buf, 0, 0);
+}
+
+void test4 (void)
+{
+ int32_t *buf = malloc (sizeof (int32_t));
+ if (!buf)
+ return;
+
+ /* Zero bytes written on non-zero allocation. */
+ __builtin_memset (buf, 0, 0);
+ free (buf);
+}
+
+void test5 (void)
+{
+ int32_t *buf = malloc (0);
+ if (!buf)
+ return;
+
+ /* Write on zero capacity. */
+ __builtin_memset (buf, 0, sizeof (int32_t)); /* { dg-line test5 } */
+ free (buf);
+
+ /* { dg-warning "overflow" "warning" { target *-*-* } test5 } */
+ /* { dg-message "from byte 0 till byte 3" "final event" { target *-*-* } test5 } */
+}
+
+void test6 (void)
+{
+ int32_t *buf = malloc (0);
+ if (!buf)
+ return;
+
+ /* Zero bytes written on zero capacity. */
+ __builtin_memset (buf, 0, 0);
+ free (buf);
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pr106845.c b/gcc/testsuite/gcc.dg/analyzer/pr106845.c
new file mode 100644
index 00000000000..528c7b3ea9a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pr106845.c
@@ -0,0 +1,11 @@
+int buf_size;
+
+int
+main (void)
+{
+ char buf[buf_size];
+
+ __builtin_memset (&buf[1], 0, buf_size);
+
+ return 0;
+}
--
2.37.3
next reply other threads:[~2022-09-10 22:21 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-09-10 22:19 Tim Lange [this message]
2022-09-11 8:04 ` David Malcolm
2022-09-11 8:21 ` Bernhard Reutner-Fischer
2022-09-11 8:40 ` David Malcolm
2022-09-11 12:08 ` Tim Lange
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220910221952.99541-1-mail@tim-lange.me \
--to=mail@tim-lange.me \
--cc=dmalcolm@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).