https://gcc.gnu.org/g:13b58ca5d1a56517932f80ce07bd947d7f4f917e

commit r16-5194-g13b58ca5d1a56517932f80ce07bd947d7f4f917e
Author: Andre Vieira <[email protected]>
Date:   Wed Nov 12 10:33:09 2025 +0000

    arm: Fix out of bounds when using cmse with FP types in aggregates 
[PR122539]
    
    Skip partial register clearing logic when dealing with FP_REGS in 
aggregates as
    these are always fully cleared and the logic assumes a mask for each of the 
4
    argument GPR_REGS.
    
    gcc/ChangeLog:
    
            PR target/122539
            * config/arm/arm.cc (comp_not_to_clear_mask_str_un): Skip partial
            register clearing logic for FP_REGS.
            (compute_not_to_clear_mask): Likewise.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.target/arm/cmse/mainline/8m/hard/union-fp.c: New.
            * gcc.target/arm/cmse/baseline/union-4.c: New.
            * gcc.target/arm/cmse/mainline/8m/hard/union-4.c: New.
            * gcc.target/arm/cmse/mainline/8m/soft/union-4.c: New.
            * gcc.target/arm/cmse/mainline/8m/softfp/union-4.c: New.
            * gcc.target/arm/cmse/union-4.x: New.

Diff:
---
 gcc/config/arm/arm.cc                              | 60 ++++++++++++--------
 .../gcc.target/arm/cmse/baseline/union-4.c         | 20 +++++++
 .../gcc.target/arm/cmse/mainline/8m/hard/union-4.c | 27 +++++++++
 .../arm/cmse/mainline/8m/hard/union-fp.c           | 66 ++++++++++++++++++++++
 .../gcc.target/arm/cmse/mainline/8m/soft/union-4.c | 18 ++++++
 .../arm/cmse/mainline/8m/softfp/union-4.c          | 19 +++++++
 gcc/testsuite/gcc.target/arm/cmse/union-4.x        | 41 ++++++++++++++
 7 files changed, 229 insertions(+), 22 deletions(-)

diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc
index bd1f6e0133ab..9dbc3f9befaf 100644
--- a/gcc/config/arm/arm.cc
+++ b/gcc/config/arm/arm.cc
@@ -18498,6 +18498,9 @@ comp_not_to_clear_mask_str_un (tree arg_type, int * 
regno,
 
          if (*last_used_bit != offset)
            {
+             /* We never clear padding bits in any other registers than the
+                first 4 GPRs.  */
+             gcc_assert (*regno < 4);
              if (offset < *last_used_bit)
                {
                  /* This field's offset is before the 'last_used_bit', that
@@ -18590,19 +18593,25 @@ comp_not_to_clear_mask_str_un (tree arg_type, int * 
regno,
              last_used_bit_t = (starting_bit + field_size) % 32;
            }
 
-         for (i = *regno; i < regno_t; i++)
+         /* We only clear padding bits in the first 4 GPRs.  No need to check
+            regno_t, since there is no way where this field would have been
+            put into part GPR part FP reg.  */
+         if (*regno < 4)
            {
-             /* For all but the last register used by this field only keep the
-                padding bits that were padding bits in this field.  */
-             padding_bits_to_clear_res[i] &= padding_bits_to_clear_t[i];
-           }
+             for (i = *regno; i < regno_t; i++)
+               {
+                 /* For all but the last register used by this field only keep
+                    the padding bits that were padding bits in this field.  */
+                 padding_bits_to_clear_res[i] &= padding_bits_to_clear_t[i];
+               }
 
-           /* For the last register, keep all padding bits that were padding
-              bits in this field and any padding bits that are still valid
-              as padding bits but fall outside of this field's size.  */
-           mask = (((uint32_t) -1) - ((uint32_t) 1 << last_used_bit_t)) + 1;
-           padding_bits_to_clear_res[regno_t]
-             &= padding_bits_to_clear_t[regno_t] | mask;
+             /* For the last register, keep all padding bits that were padding
+                bits in this field and any padding bits that are still valid
+                as padding bits but fall outside of this field's size.  */
+             mask = (((uint32_t) -1) - ((uint32_t) 1 << last_used_bit_t)) + 1;
+             padding_bits_to_clear_res[regno_t]
+               &= padding_bits_to_clear_t[regno_t] | mask;
+           }
 
          /* Update the maximum size of the fields in terms of registers used
             ('max_reg') and the 'last_used_bit' in said register.  */
@@ -18617,16 +18626,22 @@ comp_not_to_clear_mask_str_un (tree arg_type, int * 
regno,
          field = TREE_CHAIN (field);
        }
 
-      /* Update the current padding_bits_to_clear using the intersection of the
-        padding bits of all the fields.  */
-      for (i=*regno; i < max_reg; i++)
-       padding_bits_to_clear[i] |= padding_bits_to_clear_res[i];
+      /* We only clear padding bits in the first 4 GPRs.  No need to check
+        regno_t, since there is no way where this field would have been
+        put into part GPR part FP reg.  */
+      if (*regno < 4)
+       {
+         /* Update the current padding_bits_to_clear using the intersection of 
the
+            padding bits of all the fields.  */
+         for (i=*regno; i < max_reg; i++)
+           padding_bits_to_clear[i] |= padding_bits_to_clear_res[i];
 
-      /* Do not keep trailing padding bits, we do not know yet whether this
-        is the end of the argument.  */
-      mask = ((uint32_t) 1 << max_bit) - 1;
-      padding_bits_to_clear[max_reg]
-       |= padding_bits_to_clear_res[max_reg] & mask;
+         /* Do not keep trailing padding bits, we do not know yet whether this
+            is the end of the argument.  */
+         mask = ((uint32_t) 1 << max_bit) - 1;
+         padding_bits_to_clear[max_reg]
+           |= padding_bits_to_clear_res[max_reg] & mask;
+       }
 
       for (int i = *regno; i < max_reg; ++i)
        not_to_clear_reg_mask |= HOST_WIDE_INT_1U << i;
@@ -18671,8 +18686,9 @@ compute_not_to_clear_mask (tree arg_type, rtx arg_rtx, 
int regno,
       /* If the 'last_used_bit' is not zero, that means we are still using a
         part of the last 'regno'.  In such cases we must clear the trailing
         bits.  Otherwise we are not using regno and we should mark it as to
-        clear.  */
-      if (last_used_bit != 0)
+        clear.  We only clear padding bits for scalar values that are passed
+        in registers, so regno is never 4 or higher.  */
+      if (regno < 4 && last_used_bit != 0)
        padding_bits_to_clear[regno]
          |= ((uint32_t)-1) - ((uint32_t) 1 << last_used_bit) + 1;
       else
diff --git a/gcc/testsuite/gcc.target/arm/cmse/baseline/union-4.c 
b/gcc/testsuite/gcc.target/arm/cmse/baseline/union-4.c
new file mode 100644
index 000000000000..58c06ceddda2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/baseline/union-4.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-options "-mcmse" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include "../union-4.x"
+
+/*
+** fn:
+** ...
+**     lsrs    r4, r4, #1
+**     lsls    r4, r4, #1
+**     mov     ip, r4
+**     movw    r4, #7939
+**     ands    r0, r4
+**     mov     r4, ip
+**     movs    r2, r4
+**     movs    r3, r4
+**     bl      __gnu_cmse_nonsecure_call
+** ...
+*/
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/hard/union-4.c 
b/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/hard/union-4.c
new file mode 100644
index 000000000000..671a4e830783
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/hard/union-4.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-mcmse -mfloat-abi=hard -mfpu=fpv5-d16" }  */
+/* { dg-skip-if "Incompatible float ABI" { *-*-* } { "-mfloat-abi=*" } { 
"-mfloat-abi=hard" } } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include "../../../union-4.x"
+
+/*
+** fn:
+** ...
+**     lsrs    r4, r4, #1
+**     lsls    r4, r4, #1
+**     movw    ip, #7939
+**     and     r0, r0, ip
+**     mov     r2, r4
+**     mov     r3, r4
+**     vmov.f64        d0, #1.0e\+0
+**     vmov.f64        d1, #1.0e\+0
+**     vmov.f64        d2, #1.0e\+0
+**     vmov.f64        d3, #1.0e\+0
+**     vmov.f64        d4, #1.0e\+0
+**     vmov.f64        d5, #1.0e\+0
+**     vmov.f64        d6, #1.0e\+0
+**     vmov.f64        d7, #1.0e\+0
+**     bl      __gnu_cmse_nonsecure_call
+** ...
+*/
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/hard/union-fp.c 
b/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/hard/union-fp.c
new file mode 100644
index 000000000000..3d45ec6bf77e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/hard/union-fp.c
@@ -0,0 +1,66 @@
+/* { dg-do compile } */
+/* { dg-options "-mcmse -mfloat-abi=hard -mfpu=fpv5-d16" }  */
+/* { dg-skip-if "Incompatible float ABI" { *-*-* } { "-mfloat-abi=*" } { 
"-mfloat-abi=hard" } } */
+/* { dg-skip-if "Skip these if testing single precision" {*-*-*} 
{"-mfpu=*-sp-*"} {""} } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+union u_t_0 {
+    float f;
+};
+union u_t_1 {
+    double d;
+};
+
+typedef void (__attribute__ ((cmse_nonsecure_call)) fn_t_0)(union u_t_0);
+typedef void (__attribute__ ((cmse_nonsecure_call)) fn_t_1)(union u_t_1);
+
+void fn_caller_0 (fn_t_0 *f_ptr) {
+    union u_t_0 x = {0.0f};
+    f_ptr (x);
+}
+
+/*
+** fn_caller_0:
+** ...
+**     lsrs    r4, r4, #1
+**     lsls    r4, r4, #1
+**     mov     r0, r4
+**     mov     r1, r4
+**     mov     r2, r4
+**     mov     r3, r4
+**     vmov.f32        s1, #1.0e\+0
+**     vmov.f64        d1, #1.0e\+0
+**     vmov.f64        d2, #1.0e\+0
+**     vmov.f64        d3, #1.0e\+0
+**     vmov.f64        d4, #1.0e\+0
+**     vmov.f64        d5, #1.0e\+0
+**     vmov.f64        d6, #1.0e\+0
+**     vmov.f64        d7, #1.0e\+0
+**     bl      __gnu_cmse_nonsecure_call
+** ...
+*/
+
+void fn_caller_1 (fn_t_1 *f_ptr) {
+    union u_t_1 x = {0.0};
+    f_ptr (x);
+}
+
+/*
+** fn_caller_1:
+** ...
+**     lsrs    r4, r4, #1
+**     lsls    r4, r4, #1
+**     mov     r0, r4
+**     mov     r1, r4
+**     mov     r2, r4
+**     mov     r3, r4
+**     vmov.f64        d1, #1.0e\+0
+**     vmov.f64        d2, #1.0e\+0
+**     vmov.f64        d3, #1.0e\+0
+**     vmov.f64        d4, #1.0e\+0
+**     vmov.f64        d5, #1.0e\+0
+**     vmov.f64        d6, #1.0e\+0
+**     vmov.f64        d7, #1.0e\+0
+**     bl      __gnu_cmse_nonsecure_call
+** ...
+*/
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/soft/union-4.c 
b/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/soft/union-4.c
new file mode 100644
index 000000000000..b0a83db05bf6
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/soft/union-4.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-mcmse" } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include "../../../union-4.x"
+
+/*
+** fn:
+** ...
+**     lsrs    r4, r4, #1
+**     lsls    r4, r4, #1
+**     movw    ip, #7939
+**     and     r0, r0, ip
+**     mov     r2, r4
+**     mov     r3, r4
+**     bl      __gnu_cmse_nonsecure_call
+** ...
+*/
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/softfp/union-4.c 
b/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/softfp/union-4.c
new file mode 100644
index 000000000000..9842a0fee001
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/8m/softfp/union-4.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+/* { dg-options "-mcmse -mfloat-abi=softfp -mfpu=fpv5-d16" }  */
+/* { dg-skip-if "Incompatible float ABI" { *-*-* } { "-mfloat-abi=*" } { 
"-mfloat-abi=softfp" } } */
+/* { dg-final { check-function-bodies "**" "" } } */
+
+#include "../../../union-4.x"
+
+/*
+** fn:
+** ...
+**     lsrs    r4, r4, #1
+**     lsls    r4, r4, #1
+**     movw    ip, #7939
+**     and     r0, r0, ip
+**     mov     r2, r4
+**     mov     r3, r4
+**     bl      __gnu_cmse_nonsecure_call
+** ...
+*/
diff --git a/gcc/testsuite/gcc.target/arm/cmse/union-4.x 
b/gcc/testsuite/gcc.target/arm/cmse/union-4.x
new file mode 100644
index 000000000000..20678296157b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/union-4.x
@@ -0,0 +1,41 @@
+typedef struct
+{
+  unsigned char          a :2;
+  unsigned char            :0;
+  unsigned short  b :5;
+  float                  f;
+} test_st_1;
+
+typedef union
+{
+  test_st_1 st_1;
+}test_un;
+
+typedef union
+{
+  test_un un;
+  struct
+    {
+      unsigned int v1;
+      unsigned int v2;
+      unsigned int v3;
+      unsigned int v4;
+    }values;
+} read_un;
+
+
+typedef void __attribute__ ((cmse_nonsecure_call)) (*foo_ns) (test_un);
+
+int
+fn (foo_ns fptr)
+{
+  read_un r;
+
+  r.values.v1 = 0xFFFFFFFF;
+  r.values.v2 = 0xFFFFFFFF;
+  r.values.v3 = 0xFFFFFFFF;
+  r.values.v4 = 0xFFFFFFFF;
+
+  fptr (r.un);
+  return 0;
+}

Reply via email to