https://gcc.gnu.org/g:a1fc8045881850df83c5143ea6d9311ce3a0c540

commit r17-1834-ga1fc8045881850df83c5143ea6d9311ce3a0c540
Author: Xi Ruoyao <[email protected]>
Date:   Sun May 24 21:24:37 2026 +0800

    MIPS: harden SSP set and test routines [PR 125077]
    
    Add the stack_protect_combined_{set,test} expanders to expand the
    routines as unsplitable insns which does not leave any sensitive data
    (the canary value, the canary address, and all the intermediate values
    used materializing the address) in a register.  This prevents the
    attacker from defeating SSP by probing the canary value from the
    register context or overwriting the address spilled onto the stack.
    
            PR target/125077
    
    gcc/
    
            * config/mips/predicates.md (ssp_gp_operand): New
            define_predicate.
            (ssp_operand): New define_predicate.
            (ssp_normal_operand): New define_predicate.
            * config/mips/constraints.md (ZA): New define_constraint.
            (ZB): New define_constraint.
            * config/mips/mips.md (UNSPEC_SSP): New unspec.
            (UNSPEC_SSP_GP): New unspec.
            (cbranch<mode>4): Add '@' to generate the helper function taking
            mode as a parameter.
            (@stack_protect_combined_set_normal_<mode>): New define_insn.
            (stack_protect_combined_set_abs64): New define_insn.
            (@stack_protect_combined_test_internal_<mode>): New define_insn.
            (stack_protect_combined_set): New define_expand.
            (stack_protect_combined_test): New define_expand.
            * config/mips/mips-protos.h (mips_canary_expose_gp_use):
            Declare.
            (mips_output_asm_load_canary): Declare.
            * config/mips/mips.cc (mips_small_data_pattern_1): Don't rewrite
            GP-relative symbol wrapped in UNSPEC_SSP_GP.
            (mips_canary_expose_gp_use): Implement.
            (mips_output_asm_load_canary): Implement.
            (mips_print_operand): Allow 'w' to print d/w for DImode/SImode.

Diff:
---
 gcc/config/mips/constraints.md |  6 +++
 gcc/config/mips/mips-protos.h  |  2 +
 gcc/config/mips/mips.cc        | 89 +++++++++++++++++++++++++++++++++++++++++-
 gcc/config/mips/mips.md        | 78 +++++++++++++++++++++++++++++++++++-
 gcc/config/mips/predicates.md  | 18 +++++++++
 5 files changed, 190 insertions(+), 3 deletions(-)

diff --git a/gcc/config/mips/constraints.md b/gcc/config/mips/constraints.md
index de3fe1eea3b5..aa9c7dba79ad 100644
--- a/gcc/config/mips/constraints.md
+++ b/gcc/config/mips/constraints.md
@@ -367,6 +367,12 @@
   (and (match_code "const_vector")
        (match_test "mips_const_vector_same_bytes_p (op, mode)")))
 
+(define_constraint "ZA" "" (match_operand 0 "ssp_normal_operand"))
+
+(define_constraint "ZB" ""
+  (and (match_operand 0 "ssp_operand")
+       (not (match_operand 0 "ssp_normal_operand"))))
+
 (define_memory_constraint "ZC"
   "A memory operand whose address is formed by a base register and offset
    that is suitable for use in instructions with the same addressing mode
diff --git a/gcc/config/mips/mips-protos.h b/gcc/config/mips/mips-protos.h
index 472baac8fdea..d8ca9594fdb3 100644
--- a/gcc/config/mips/mips-protos.h
+++ b/gcc/config/mips/mips-protos.h
@@ -223,6 +223,8 @@ extern void mips_split_msa_copy_d (rtx, rtx, rtx, rtx 
(*)(rtx, rtx, rtx));
 extern void mips_split_msa_insert_d (rtx, rtx, rtx, rtx);
 extern void mips_split_msa_fill_d (rtx, rtx);
 extern const char *mips_output_move (rtx, rtx);
+extern rtx mips_canary_expose_gp_use (rtx);
+extern void mips_output_asm_load_canary (rtx, rtx, rtx);
 extern bool mips_cfun_has_cprestore_slot_p (void);
 extern bool mips_cprestore_address_p (rtx, bool);
 extern void mips_save_gp_to_cprestore_slot (rtx, rtx, rtx, rtx);
diff --git a/gcc/config/mips/mips.cc b/gcc/config/mips/mips.cc
index 277ec4198260..86e2b31f08aa 100644
--- a/gcc/config/mips/mips.cc
+++ b/gcc/config/mips/mips.cc
@@ -3955,8 +3955,10 @@ mips_small_data_pattern_1 (rtx x, enum 
mips_symbol_context context)
 
       /* Ignore things like "g" constraints in asms.  We make no particular
         guarantee about which symbolic constants are acceptable as asm operands
-        versus which must be forced into a GPR.  */
-      if (GET_CODE (x) == LO_SUM || GET_CODE (x) == ASM_OPERANDS)
+        versus which must be forced into a GPR.  SSP canary GP use has
+        been exposed earlier in expand.  */
+      if (GET_CODE (x) == LO_SUM || GET_CODE (x) == ASM_OPERANDS
+         || (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_SSP_GP))
        iter.skip_subrtxes ();
       else if (MEM_P (x))
        {
@@ -5566,6 +5568,87 @@ mips_output_move (rtx dest, rtx src)
     }
   gcc_unreachable ();
 }
+
+rtx
+mips_canary_expose_gp_use (rtx canary)
+{
+  gcc_checking_assert (symbolic_operand (canary, VOIDmode));
+
+  switch (mips_classify_symbol (canary, SYMBOL_CONTEXT_LEA))
+    {
+    case SYMBOL_GOT_DISP:
+    case SYMBOL_GP_RELATIVE:
+      return gen_rtx_UNSPEC (Pmode, gen_rtvec (2, pic_offset_table_rtx,
+                                              canary),
+                            UNSPEC_SSP_GP);
+    default:
+      return canary;
+    }
+}
+
+void
+mips_output_asm_load_canary (rtx reg, rtx canary, rtx tmp)
+{
+  gcc_checking_assert (register_operand (reg, Pmode));
+
+  if (ssp_gp_operand (canary, VOIDmode))
+    canary = XVECEXP (canary, 0, 1);
+
+  rtx op[] = {reg, canary, tmp};
+
+  if (!TARGET_EXPLICIT_RELOCS)
+    {
+      gcc_checking_assert (!tmp);
+      output_asm_insn (mips_output_move (reg, canary), op);
+    }
+  else
+    {
+      auto symbol_class = mips_classify_symbol (canary, SYMBOL_CONTEXT_LEA);
+      bool need_hi = false;
+      switch (symbol_class)
+       {
+       case SYMBOL_ABSOLUTE:
+         if (ABI_HAS_64BIT_SYMBOLS)
+           {
+             gcc_checking_assert (tmp);
+             output_asm_insn ("lui\t%0,%%highest(%1)", op);
+             output_asm_insn ("lui\t%2,%%hi(%1)", op);
+             output_asm_insn ("daddiu\t%0,%0,%%higher(%1)", op);
+             output_asm_insn ("dsll\t%0,%0,32", op);
+             output_asm_insn ("daddu\t%2,%0,%2", op);
+             break;
+           }
+         need_hi = true;
+         break;
+       case SYMBOL_GOT_DISP:
+         op[1] = mips_unspec_address (op[1], SYMBOL_GOTOFF_DISP);
+         if (TARGET_XGOT)
+           {
+             need_hi = true;
+             break;
+           }
+         /* fall through */
+       case SYMBOL_GP_RELATIVE:
+         op[2] = pic_offset_table_rtx;
+         break;
+       default:
+         sorry ("%<-fstack-protector%> with unexpected canary symbol");
+         return;
+       }
+
+      if (need_hi)
+       {
+         output_asm_insn ("lui\t%0,%h1", op);
+         op[2] = op[0];
+       }
+
+      output_asm_insn ("l%v0\t%0,%R1(%2)", op);
+      if (symbol_class == SYMBOL_GOT_DISP)
+       output_asm_insn ("l%v0\t%0,(%0)", op);
+    }
+}
+
+
 
 /* Return true if CMP1 is a suitable second operand for integer ordering
    test CODE.  See also the *sCC patterns in mips.md.  */
@@ -9650,10 +9733,12 @@ mips_print_operand (FILE *file, rtx op, int letter)
          break;
        case E_V4SImode:
        case E_V4SFmode:
+       case E_SImode:
          fprintf (file, "w");
          break;
        case E_V2DImode:
        case E_V2DFmode:
+       case E_DImode:
          fprintf (file, "d");
          break;
        default:
diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md
index 18244e4abcc8..38eef4160793 100644
--- a/gcc/config/mips/mips.md
+++ b/gcc/config/mips/mips.md
@@ -168,6 +168,9 @@
   UNSPEC_INSN_PSEUDO
   UNSPEC_JRHB
 
+  UNSPEC_SSP
+  UNSPEC_SSP_GP
+
   VUNSPEC_SPECULATION_BARRIER
 ])
 
@@ -6234,7 +6237,7 @@
    bt%N1z\t%0"
   [(set_attr "type" "branch")])
 
-(define_expand "cbranch<mode>4"
+(define_expand "@cbranch<mode>4"
   [(set (pc)
        (if_then_else (match_operator 0 "comparison_operator"
                       [(match_operand:GPR 1 "register_operand")
@@ -8140,6 +8143,79 @@
                   (any_extend:SI (match_dup 3)))])]
   "")
 
+(define_insn "@stack_protect_combined_set_normal_<mode>"
+  [(set (match_operand:P 0 "memory_operand" "=m")
+       (unspec:P [(mem:P (match_operand:P 1 "ssp_normal_operand"))]
+                 UNSPEC_SSP))
+   (set (match_scratch:P 2 "=&d") (const_int 0))]
+  ""
+{
+  mips_output_asm_load_canary (operands[2], operands[1], NULL_RTX);
+  return "<store>\t%2,%0\n\tmove\t%2,$0";
+}
+  [(set_attr "insn_count" "5")])
+
+(define_insn "stack_protect_combined_set_abs64"
+  [(set (match_operand:DI 0 "memory_operand" "=m")
+       (unspec:DI [(mem:DI (match_operand:DI 1 "absolute_symbolic_operand"))]
+                 UNSPEC_SSP))
+   (set (match_scratch:DI 2 "=&d") (const_int 0))
+   (set (match_scratch:DI 3 "=&d") (const_int 0))]
+  "ABI_HAS_64BIT_SYMBOLS && TARGET_EXPLICIT_RELOCS"
+{
+  mips_output_asm_load_canary (operands[2], operands[1], operands[3]);
+  return "sd\t%2,%0\n\tmove\t%2,$0\n\tmove\t%3,$0";
+}
+  [(set_attr "insn_count" "9")])
+
+(define_insn "@stack_protect_combined_test_internal_<mode>"
+  [(set (match_operand:P 0 "register_operand" "=d,d")
+       (xor:P
+         (match_operand:P 1 "memory_operand" "m,m")
+         (unspec:P [(mem:P (match_operand:P 2 "ssp_operand" "ZA,ZB"))]
+                   UNSPEC_SSP)))
+   (set (match_scratch:P 3 "=&d,&d") (const_int 0))]
+  ""
+{
+  rtx t = (which_alternative ? operands[0] : NULL_RTX);
+  mips_output_asm_load_canary (operands[3], operands[2], t);
+  return "<load>\t%0,%1\n\txor\t%0,%0,%3\n\tmove\t%3,$0";
+}
+  [(set_attr "insn_count" "6,9")])
+
+(define_expand "stack_protect_combined_set"
+  [(match_operand 0 "memory_operand")
+   (match_operand 1 "memory_operand")]
+  ""
+{
+  rtx canary = mips_canary_expose_gp_use (XEXP (operands[1], 0));
+
+  if (ssp_normal_operand (canary, VOIDmode))
+    emit_insn (gen_stack_protect_combined_set_normal (Pmode, operands[0],
+                                                     canary));
+  else
+    emit_insn (gen_stack_protect_combined_set_abs64 (operands[0],
+                                                    canary));
+  DONE;
+})
+
+(define_expand "stack_protect_combined_test"
+  [(match_operand 0 "memory_operand")
+   (match_operand 1 "memory_operand")
+   (match_operand 2 "")]
+  ""
+{
+  rtx t = gen_reg_rtx (Pmode);
+  rtx canary = mips_canary_expose_gp_use (XEXP (operands[1], 0));
+
+  emit_insn (gen_stack_protect_combined_test_internal (Pmode, t,
+                                                      operands[0],
+                                                      canary));
+  rtx cond = gen_rtx_EQ (VOIDmode, t, const0_rtx);
+  emit_jump_insn (gen_cbranch4 (Pmode, cond, t, const0_rtx, operands[2]));
+  DONE;
+})
+
 
 ;; Synchronization instructions.
 
diff --git a/gcc/config/mips/predicates.md b/gcc/config/mips/predicates.md
index b0e2219e3126..ba2740667645 100644
--- a/gcc/config/mips/predicates.md
+++ b/gcc/config/mips/predicates.md
@@ -495,6 +495,24 @@
          && mips_hi_relocs[(int) type]);
 })
 
+(define_predicate "ssp_gp_operand"
+  (match_code "unspec")
+{
+  return (XINT (op, 1) == UNSPEC_SSP_GP
+         && XVECEXP (op, 0, 0) == pic_offset_table_rtx
+         && GET_CODE (XVECEXP (op, 0, 1)) == SYMBOL_REF);
+})
+
+(define_predicate "ssp_operand"
+  (ior (match_code "symbol_ref")
+       (match_operand 0 "ssp_gp_operand")))
+
+(define_predicate "ssp_normal_operand"
+  (and (match_operand 0 "ssp_operand")
+       (not (and (match_operand 0 "absolute_symbolic_operand")
+                (match_test "ABI_HAS_64BIT_SYMBOLS")
+                (match_test "TARGET_EXPLICIT_RELOCS")))))
+
 (define_predicate "force_to_mem_operand"
   (match_code "const,symbol_ref,label_ref")
 {

Reply via email to