aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2022-02-09 19:06:15 -0500
committerDavid Malcolm <dmalcolm@redhat.com>2022-02-10 13:00:58 -0500
commit2ac7b19f1e9219f46ccf55f25d8acb3e02e9a2d4 (patch)
treef0b2beadb7c47003a45bf9cba95784980ed14165
parentb32305b41dcafc5fb6974c0da3ce2f62251afdbf (diff)
downloadgcc-2ac7b19f1e9219f46ccf55f25d8acb3e02e9a2d4.zip
gcc-2ac7b19f1e9219f46ccf55f25d8acb3e02e9a2d4.tar.gz
gcc-2ac7b19f1e9219f46ccf55f25d8acb3e02e9a2d4.tar.bz2
analyzer: handle more casts of string literals [PR98797]
gcc/analyzer/ChangeLog: PR analyzer/98797 * region-model-manager.cc (region_model_manager::maybe_fold_sub_svalue): Generalize getting individual chars of a STRING_CST from element_region to any subregion which is a concrete access of a single byte from its parent region. * region.cc (region::get_relative_concrete_byte_range): New. * region.h (region::get_relative_concrete_byte_range): New decl. gcc/testsuite/ChangeLog: PR analyzer/98797 * gcc.dg/analyzer/casts-1.c: Mark xfails as fixed; add further test coverage for casts of string literals. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
-rw-r--r--gcc/analyzer/region-model-manager.cc19
-rw-r--r--gcc/analyzer/region.cc28
-rw-r--r--gcc/analyzer/region.h6
-rw-r--r--gcc/testsuite/gcc.dg/analyzer/casts-1.c45
4 files changed, 84 insertions, 14 deletions
diff --git a/gcc/analyzer/region-model-manager.cc b/gcc/analyzer/region-model-manager.cc
index 010ad07..d7156c5 100644
--- a/gcc/analyzer/region-model-manager.cc
+++ b/gcc/analyzer/region-model-manager.cc
@@ -782,15 +782,22 @@ region_model_manager::maybe_fold_sub_svalue (tree type,
/* Handle getting individual chars from a STRING_CST. */
if (tree cst = parent_svalue->maybe_get_constant ())
if (TREE_CODE (cst) == STRING_CST)
- if (const element_region *element_reg
- = subregion->dyn_cast_element_region ())
- {
- const svalue *idx_sval = element_reg->get_index ();
- if (tree cst_idx = idx_sval->maybe_get_constant ())
+ {
+ /* If we have a concrete 1-byte access within the parent region... */
+ byte_range subregion_bytes (0, 0);
+ if (subregion->get_relative_concrete_byte_range (&subregion_bytes)
+ && subregion_bytes.m_size_in_bytes == 1)
+ {
+ /* ...then attempt to get that char from the STRING_CST. */
+ HOST_WIDE_INT hwi_start_byte
+ = subregion_bytes.m_start_byte_offset.to_shwi ();
+ tree cst_idx
+ = build_int_cst_type (size_type_node, hwi_start_byte);
if (const svalue *char_sval
= maybe_get_char_from_string_cst (cst, cst_idx))
return get_or_create_cast (type, char_sval);
- }
+ }
+ }
if (const initial_svalue *init_sval
= parent_svalue->dyn_cast_initial_svalue ())
diff --git a/gcc/analyzer/region.cc b/gcc/analyzer/region.cc
index 0adc75e57..5ac24fb 100644
--- a/gcc/analyzer/region.cc
+++ b/gcc/analyzer/region.cc
@@ -539,6 +539,34 @@ region::get_relative_concrete_offset (bit_offset_t *) const
return false;
}
+/* Attempt to get the position and size of this region expressed as a
+ concrete range of bytes relative to its parent.
+ If successful, return true and write to *OUT.
+ Otherwise return false. */
+
+bool
+region::get_relative_concrete_byte_range (byte_range *out) const
+{
+ /* We must have a concrete offset relative to the parent. */
+ bit_offset_t rel_bit_offset;
+ if (!get_relative_concrete_offset (&rel_bit_offset))
+ return false;
+ /* ...which must be a whole number of bytes. */
+ if (rel_bit_offset % BITS_PER_UNIT != 0)
+ return false;
+ byte_offset_t start_byte_offset = rel_bit_offset / BITS_PER_UNIT;
+
+ /* We must have a concrete size, which must be a whole number
+ of bytes. */
+ byte_size_t num_bytes;
+ if (!get_byte_size (&num_bytes))
+ return false;
+
+ /* Success. */
+ *out = byte_range (start_byte_offset, num_bytes);
+ return true;
+}
+
/* Dump a description of this region to stderr. */
DEBUG_FUNCTION void
diff --git a/gcc/analyzer/region.h b/gcc/analyzer/region.h
index 5311217..2f987e4 100644
--- a/gcc/analyzer/region.h
+++ b/gcc/analyzer/region.h
@@ -182,6 +182,12 @@ public:
Otherwise return false. */
virtual bool get_relative_concrete_offset (bit_offset_t *out) const;
+ /* Attempt to get the position and size of this region expressed as a
+ concrete range of bytes relative to its parent.
+ If successful, return true and write to *OUT.
+ Otherwise return false. */
+ bool get_relative_concrete_byte_range (byte_range *out) const;
+
void
get_subregions_for_binding (region_model_manager *mgr,
bit_offset_t start_bit_offset,
diff --git a/gcc/testsuite/gcc.dg/analyzer/casts-1.c b/gcc/testsuite/gcc.dg/analyzer/casts-1.c
index 15cd85f..7e4af38 100644
--- a/gcc/testsuite/gcc.dg/analyzer/casts-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/casts-1.c
@@ -13,6 +13,14 @@ struct s2
char arr[4];
};
+struct s3
+{
+ struct inner {
+ char a;
+ char b;
+ } arr[2];
+};
+
void test_1 ()
{
struct s1 x = {'A', 'B', 'C', 'D'};
@@ -24,10 +32,16 @@ void test_1 ()
__analyzer_eval (((struct s2 *)&x)->arr[1] == 'B'); /* { dg-warning "TRUE" } */
__analyzer_eval (((struct s2 *)&x)->arr[2] == 'C'); /* { dg-warning "TRUE" } */
__analyzer_eval (((struct s2 *)&x)->arr[3] == 'D'); /* { dg-warning "TRUE" } */
+ struct s3 *p3 = (struct s3 *)&x;
+ __analyzer_eval (p3->arr[0].a == 'A'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p3->arr[0].b == 'B'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p3->arr[1].a == 'C'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p3->arr[1].b == 'D'); /* { dg-warning "TRUE" } */
((struct s2 *)&x)->arr[1] = '#';
__analyzer_eval (((struct s2 *)&x)->arr[1] == '#'); /* { dg-warning "TRUE" } */
__analyzer_eval (x.b == '#'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p3->arr[0].b == '#'); /* { dg-warning "TRUE" } */
}
void test_2 ()
@@ -38,12 +52,27 @@ void test_2 ()
__analyzer_eval (x.arr[2] == 'C'); /* { dg-warning "TRUE" } */
__analyzer_eval (x.arr[3] == 'D'); /* { dg-warning "TRUE" } */
struct s1 *p = (struct s1 *)&x;
- __analyzer_eval (p->a == 'A'); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
- /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
- __analyzer_eval (p->b == 'B'); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
- /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
- __analyzer_eval (p->c == 'C'); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
- /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
- __analyzer_eval (p->d == 'D'); /* { dg-warning "TRUE" "true" { xfail *-*-* } } */
- /* { dg-bogus "UNKNOWN" "unknown" { xfail *-*-* } .-1 } */
+ __analyzer_eval (p->a == 'A'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p->b == 'B'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p->c == 'C'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p->d == 'D'); /* { dg-warning "TRUE" } */
+}
+
+void test_3 ()
+{
+ struct s3 x = {'A', 'B', 'C', 'D'};
+ __analyzer_eval (x.arr[0].a == 'A'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (x.arr[0].b == 'B'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (x.arr[1].a == 'C'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (x.arr[1].b == 'D'); /* { dg-warning "TRUE" } */
+ struct s1 *p1 = (struct s1 *)&x;
+ __analyzer_eval (p1->a == 'A'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p1->b == 'B'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p1->c == 'C'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p1->d == 'D'); /* { dg-warning "TRUE" } */
+ struct s2 *p2 = (struct s2 *)&x;
+ __analyzer_eval (p2->arr[0] == 'A'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p2->arr[1] == 'B'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p2->arr[2] == 'C'); /* { dg-warning "TRUE" } */
+ __analyzer_eval (p2->arr[3] == 'D'); /* { dg-warning "TRUE" } */
}