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 <m...@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