From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 7884) id 423823858C83; Sun, 11 Sep 2022 22:02:30 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 423823858C83 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1662933750; bh=Dpqb1h9Q54AGXvflQTZprJ34MdCaCd1dH7weDDCmPs8=; h=From:To:Subject:Date:From; b=i7Vj7JHAE1gib5Ie24uR3J39Wzkc7252CI9WSseqdV5ax5cb4iNWufMdbo1NkAIxg bXgYTR/dM1fxuZquAKOLrqaeD3lnIu7ZJuG5M8zsx8tMvBucadCf55xs2CaRwI9RgV RC0o0kNB5wbQX11hzPva20MPIKaZ1liUUnTLd258= MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Tim Lange To: gcc-cvs@gcc.gnu.org Subject: [gcc r13-2582] analyzer: consider empty ranges and zero byte accesses [PR106845] X-Act-Checkin: gcc X-Git-Author: Tim Lange X-Git-Refname: refs/heads/master X-Git-Oldrev: 5b3496e2ea632463f4118928a87639454c87a859 X-Git-Newrev: 0ea5e3f4542832b8da016b152695e64a2a386309 Message-Id: <20220911220230.423823858C83@sourceware.org> Date: Sun, 11 Sep 2022 22:02:30 +0000 (GMT) List-Id: https://gcc.gnu.org/g:0ea5e3f4542832b8da016b152695e64a2a386309 commit r13-2582-g0ea5e3f4542832b8da016b152695e64a2a386309 Author: Tim Lange Date: Sat Sep 10 23:53:48 2022 +0200 analyzer: consider empty ranges and zero byte accesses [PR106845] 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 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. Diff: --- gcc/analyzer/region-model.cc | 3 + gcc/analyzer/store.cc | 12 +++- gcc/analyzer/store.h | 12 ++++ gcc/testsuite/gcc.dg/analyzer/out-of-bounds-zero.c | 67 ++++++++++++++++++++++ gcc/testsuite/gcc.dg/analyzer/pr106845.c | 11 ++++ 5 files changed, 103 insertions(+), 2 deletions(-) diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc index 6eeb684844d..13b8e3eaf1b 100644 --- a/gcc/analyzer/region-model.cc +++ b/gcc/analyzer/region-model.cc @@ -1828,6 +1828,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 +#include + +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; +}