https://gcc.gnu.org/g:9b3c24577e75356efabcdfe0beaec2a4f0bc6686
commit r16-7934-g9b3c24577e75356efabcdfe0beaec2a4f0bc6686 Author: Qing Zhao <[email protected]> Date: Fri Mar 6 18:58:41 2026 +0000 Fix [PR124230] For a pointer array reference that is annotated with counted_by attribute, such as: struct annotated { int *c __attribute__ ((counted_by (b))); int b; }; struct annotated *p = setup (10); p->c[12] = 2; //out of bound access the IR for p->c[12] is: (.ACCESS_WITH_SIZE (p->c, &p->b, 0B, 4) + 48) = 2; The current routine get_index_from_offset in c-family/c-ubsan.cc cannot handle the integer constant offset "48" correctly. The fix is to enhance "get_index_from_offset" to correctly handle the constant offset. PR c/124230 gcc/c-family/ChangeLog: * c-ubsan.cc (get_index_from_offset): Handle the special case when the offset is an integer constant. gcc/testsuite/ChangeLog: * gcc.dg/ubsan/pointer-counted-by-bounds-124230-char.c: New test. * gcc.dg/ubsan/pointer-counted-by-bounds-124230-float.c: New test. * gcc.dg/ubsan/pointer-counted-by-bounds-124230-struct.c: New test. * gcc.dg/ubsan/pointer-counted-by-bounds-124230-union.c: New test. * gcc.dg/ubsan/pointer-counted-by-bounds-124230.c: New test. Diff: --- gcc/c-family/c-ubsan.cc | 15 +++++- .../ubsan/pointer-counted-by-bounds-124230-char.c | 9 ++++ .../ubsan/pointer-counted-by-bounds-124230-float.c | 9 ++++ .../pointer-counted-by-bounds-124230-struct.c | 61 ++++++++++++++++++++++ .../ubsan/pointer-counted-by-bounds-124230-union.c | 61 ++++++++++++++++++++++ .../ubsan/pointer-counted-by-bounds-124230.c | 59 +++++++++++++++++++++ 6 files changed, 213 insertions(+), 1 deletion(-) diff --git a/gcc/c-family/c-ubsan.cc b/gcc/c-family/c-ubsan.cc index 155af5e952c9..ce688550a6ed 100644 --- a/gcc/c-family/c-ubsan.cc +++ b/gcc/c-family/c-ubsan.cc @@ -659,6 +659,9 @@ get_factors_from_mul_expr (tree mult_expr, tree parent, ELEMENT_SIZE: (sizetype) SAVE_EXPR <n> * 4 get the index as (long unsigned int) m, and return it. + One special case is when the OFFSET is an integer constant, and the + element_size is also an integer constant, we should get the index + as OFFSET/element_size. The INDEX_P holds the pointer to the parent tree of the index, INDEX_N holds the position of the index in its parent. */ @@ -666,9 +669,19 @@ static tree get_index_from_offset (tree offset, tree *index_p, int *index_n, tree element_size) { - if (TREE_CODE (offset) != MULT_EXPR) + if (TREE_CODE (offset) != MULT_EXPR + && TREE_CODE (offset) != INTEGER_CST) return NULL_TREE; + if (TREE_CODE (offset) == INTEGER_CST + && TREE_CODE (element_size) != INTEGER_CST) + return NULL_TREE; + + if (TREE_CODE (offset) == INTEGER_CST + && TREE_CODE (element_size) == INTEGER_CST) + return fold_build2 (EXACT_DIV_EXPR, TREE_TYPE (offset), + offset, element_size); + auto_vec<factor_t> e_factors, o_factors; get_factors_from_mul_expr (element_size, NULL, -1, &e_factors); get_factors_from_mul_expr (offset, *index_p, *index_n, &o_factors); diff --git a/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-124230-char.c b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-124230-char.c new file mode 100644 index 000000000000..b58bfa2c58b2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-124230-char.c @@ -0,0 +1,9 @@ +/* Test the attribute counted_by for pointer fields and its usage in + bounds sanitizer. */ +/* { dg-do run } */ +/* { dg-options "-fsanitize=bounds" } */ +/* { dg-output "index 10 out of bounds for type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 11 out of bounds for type 'char \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ + +#define PTR_TYPE char +#include "pointer-counted-by-bounds-124230.c" diff --git a/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-124230-float.c b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-124230-float.c new file mode 100644 index 000000000000..6bf686fe6ea2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-124230-float.c @@ -0,0 +1,9 @@ +/* Test the attribute counted_by for pointer fields and its usage in + bounds sanitizer. */ +/* { dg-do run } */ +/* { dg-options "-fsanitize=bounds" } */ +/* { dg-output "index 10 out of bounds for type 'float \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 11 out of bounds for type 'float \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ + +#define PTR_TYPE float +#include "pointer-counted-by-bounds-124230.c" diff --git a/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-124230-struct.c b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-124230-struct.c new file mode 100644 index 000000000000..41b0b7d3d281 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-124230-struct.c @@ -0,0 +1,61 @@ +/* Test the attribute counted_by for pointer fields and its usage in + bounds sanitizer. */ +/* { dg-do run } */ +/* { dg-options "-fsanitize=bounds" } */ +/* { dg-output "index 10 out of bounds for type 'A \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 11 out of bounds for type 'A \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ + +#include <stdlib.h> + +struct A { + int a; + char *b; +}; +#define PTR_TYPE struct A +struct annotated { + int b; + PTR_TYPE *c __attribute__ ((counted_by (b))); +} *p_array_annotated; + +struct nested_annotated { + PTR_TYPE *c __attribute__ ((counted_by (b))); + struct { + union { + int b; + float f; + }; + int n; + }; +} *p_array_nested_annotated; + +void __attribute__((__noinline__)) setup (int annotated_count) +{ + p_array_annotated + = (struct annotated *) malloc (sizeof (struct annotated)); + p_array_annotated->c = (PTR_TYPE *) malloc (annotated_count * sizeof (PTR_TYPE)); + p_array_annotated->b = annotated_count; + + p_array_nested_annotated + = (struct nested_annotated *) malloc (sizeof (struct nested_annotated)); + p_array_nested_annotated->c = (PTR_TYPE *) malloc (sizeof (PTR_TYPE) * annotated_count); + p_array_nested_annotated->b = annotated_count; + + return; +} + +void cleanup () +{ + free (p_array_annotated->c); + free (p_array_annotated); + free (p_array_nested_annotated->c); + free (p_array_nested_annotated); +} + +int main(int argc, char *argv[]) +{ + setup (10); + p_array_annotated->c[10].a = 2; + p_array_nested_annotated->c[11].a = 3; + cleanup (); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-124230-union.c b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-124230-union.c new file mode 100644 index 000000000000..7157c98d782f --- /dev/null +++ b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-124230-union.c @@ -0,0 +1,61 @@ +/* Test the attribute counted_by for pointer fields and its usage in + bounds sanitizer. */ +/* { dg-do run } */ +/* { dg-options "-fsanitize=bounds" } */ +/* { dg-output "index 10 out of bounds for type 'A \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 11 out of bounds for type 'A \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ + +#include <stdlib.h> + +union A { + int a; + float b; +}; +#define PTR_TYPE union A +struct annotated { + int b; + PTR_TYPE *c __attribute__ ((counted_by (b))); +} *p_array_annotated; + +struct nested_annotated { + PTR_TYPE *c __attribute__ ((counted_by (b))); + struct { + union { + int b; + float f; + }; + int n; + }; +} *p_array_nested_annotated; + +void __attribute__((__noinline__)) setup (int annotated_count) +{ + p_array_annotated + = (struct annotated *) malloc (sizeof (struct annotated)); + p_array_annotated->c = (PTR_TYPE *) malloc (annotated_count * sizeof (PTR_TYPE)); + p_array_annotated->b = annotated_count; + + p_array_nested_annotated + = (struct nested_annotated *) malloc (sizeof (struct nested_annotated)); + p_array_nested_annotated->c = (PTR_TYPE *) malloc (sizeof (PTR_TYPE) * annotated_count); + p_array_nested_annotated->b = annotated_count; + + return; +} + +void cleanup () +{ + free (p_array_annotated->c); + free (p_array_annotated); + free (p_array_nested_annotated->c); + free (p_array_nested_annotated); +} + +int main(int argc, char *argv[]) +{ + setup (10); + p_array_annotated->c[10].a = 2; + p_array_nested_annotated->c[11].a = 3; + cleanup (); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-124230.c b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-124230.c new file mode 100644 index 000000000000..26aef7f1f5a2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/ubsan/pointer-counted-by-bounds-124230.c @@ -0,0 +1,59 @@ +/* Test the attribute counted_by for pointer fields and its usage in + bounds sanitizer. */ +/* { dg-do run } */ +/* { dg-options "-fsanitize=bounds" } */ +/* { dg-output "index 10 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output "\[^\n\r]*index 11 out of bounds for type 'int \\\[\\\*\\\]'\[^\n\r]*(\n|\r\n|\r)" } */ + +#include <stdlib.h> + +#ifndef PTR_TYPE +#define PTR_TYPE int +#endif +struct annotated { + int b; + PTR_TYPE *c __attribute__ ((counted_by (b))); +} *p_array_annotated; + +struct nested_annotated { + PTR_TYPE *c __attribute__ ((counted_by (b))); + struct { + union { + int b; + float f; + }; + int n; + }; +} *p_array_nested_annotated; + +void __attribute__((__noinline__)) setup (int annotated_count) +{ + p_array_annotated + = (struct annotated *) malloc (sizeof (struct annotated)); + p_array_annotated->c = (PTR_TYPE *) malloc (annotated_count * sizeof (PTR_TYPE)); + p_array_annotated->b = annotated_count; + + p_array_nested_annotated + = (struct nested_annotated *) malloc (sizeof (struct nested_annotated)); + p_array_nested_annotated->c = (PTR_TYPE *) malloc (sizeof (PTR_TYPE) * annotated_count); + p_array_nested_annotated->b = annotated_count; + + return; +} + +void cleanup () +{ + free (p_array_annotated->c); + free (p_array_annotated); + free (p_array_nested_annotated->c); + free (p_array_nested_annotated); +} + +int main(int argc, char *argv[]) +{ + setup (10); + p_array_annotated->c[10] = 2; + p_array_nested_annotated->c[11] = 3; + cleanup (); + return 0; +}
