gas: initialisation of expressionS in operand()

Message ID aMjB0J0oVsED5MZ2@squeak.grove.modra.org
State New
Headers
Series gas: initialisation of expressionS in operand() |

Commit Message

Alan Modra Sept. 16, 2025, 1:48 a.m. UTC
  This patch removes clean_up_expression which runs just before operand()
returns.  clean_up_expression sets as yet uninitialised fields of
expressionS.  Well, it sets fields based on the value of X_op,
trusting that others have been written, and has one notable exception:
X_md is not initialised.

Instead initialise expressionS fully inside operand(), which is called
at the start of expr(), and introduce md_expr_init for the odd
backends that want to mess with X_md.

This is in response to an oss-fuzz report that read.c:pseudo_set calls
expr() leaving exp.X_md uninitialised and can copy that to a symbol
via symbol_set_value_expression.  tc-i386-intel.c:565 is one place
that later tests the uninitialised X_md.

	* config/tc-z80.h (md_expr_init, md_expr_init_rest): Define.
	* config/tc-microblaze.h: Likewise.
	* expr.c (clean_up_expression): Delete.
	(operand): Init expression early.
	(expr): Use md_expr_init_rest to init X_md when necessary.
  

Patch

diff --git a/gas/config/tc-microblaze.h b/gas/config/tc-microblaze.h
index 6ac1b4a8235..1d0b56af033 100644
--- a/gas/config/tc-microblaze.h
+++ b/gas/config/tc-microblaze.h
@@ -57,6 +57,12 @@  extern bfd_reloc_code_real_type parse_cons_expression_microblaze
 #define tc_fix_adjustable(X)  tc_microblaze_fix_adjustable(X)
 extern int tc_microblaze_fix_adjustable (struct fix *);
 
+/* X_md is managed by the backend.  */
+#define md_expr_init(exp) \
+  do memset ((exp), 0, offsetof (expressionS, X_md)); while (0)
+#define md_expr_init_rest(exp) \
+  do (exp)->X_md = 0; while (0)
+
 extern const struct relax_type md_relax_table[];
 #define TC_GENERIC_RELAX_TABLE md_relax_table
 
diff --git a/gas/config/tc-z80.h b/gas/config/tc-z80.h
index 25f58e607c6..50e09124ad6 100644
--- a/gas/config/tc-z80.h
+++ b/gas/config/tc-z80.h
@@ -39,6 +39,12 @@ 
    will point to the start of the expression.  */
 #define md_operand(x)
 
+/* X_md is managed by the backend.  */
+#define md_expr_init(exp) \
+  do memset ((exp), 0, offsetof (expressionS, X_md)); while (0)
+#define md_expr_init_rest(exp) \
+  do (exp)->X_md = 0; while (0)
+
 /* This should just call either `number_to_chars_bigendian' or
    `number_to_chars_littleendian', whichever is appropriate.  On
    targets like the MIPS which support options to change the
diff --git a/gas/expr.c b/gas/expr.c
index aaad288a8c8..b75cce9f781 100644
--- a/gas/expr.c
+++ b/gas/expr.c
@@ -35,8 +35,6 @@ 
 
 bool literal_prefix_dollar_hex = false;
 
-static void clean_up_expression (expressionS * expressionP);
-
 /* We keep a mapping of expression symbols to file positions, so that
    we can provide better error messages.  */
 
@@ -801,14 +799,19 @@  operand (expressionS *expressionP, enum expr_mode mode)
   segT segment;
   operatorT op = O_absent; /* For unary operators.  */
 
+#ifdef md_expr_init
+  md_expr_init (expressionP);
+#else
+  memset (expressionP, 0, sizeof (*expressionP));
+#endif
+
   /* All integers are regarded as unsigned unless they are negated.
      This is because the only thing which cares whether a number is
      unsigned is the code in emit_expr which extends constants into
      bignums.  It should only sign extend negative numbers, so that
      something like ``.quad 0x80000000'' is not sign extended even
      though it appears negative if valueT is 32 bits.  */
-  expressionP->X_unsigned = 1;
-  expressionP->X_extrabit = 0;
+  expressionP->X_unsigned = 1;				\
 
   /* Digits, assume it is a bignum.  */
 
@@ -1451,9 +1454,6 @@  operand (expressionS *expressionP, enum expr_mode mode)
       break;
     }
 
-  /* It is more 'efficient' to clean up the expressionS when they are
-     created.  Doing it here saves lines of code.  */
-  clean_up_expression (expressionP);
   SKIP_ALL_WHITESPACE ();		/* -> 1st char after operand.  */
   know (!is_whitespace (*input_line_pointer));
 
@@ -1480,39 +1480,6 @@  operand (expressionS *expressionP, enum expr_mode mode)
     }
 }
 
-/* Internal.  Simplify a struct expression for use by expr ().  */
-
-/* In:	address of an expressionS.
-	The X_op field of the expressionS may only take certain values.
-	Elsewise we waste time special-case testing. Sigh. Ditto SEG_ABSENT.
-
-   Out:	expressionS may have been modified:
-	Unused fields zeroed to help expr ().  */
-
-static void
-clean_up_expression (expressionS *expressionP)
-{
-  switch (expressionP->X_op)
-    {
-    case O_illegal:
-    case O_absent:
-      expressionP->X_add_number = 0;
-      /* Fall through.  */
-    case O_big:
-    case O_constant:
-    case O_register:
-      expressionP->X_add_symbol = NULL;
-      /* Fall through.  */
-    case O_symbol:
-    case O_uminus:
-    case O_bit_not:
-      expressionP->X_op_symbol = NULL;
-      break;
-    default:
-      break;
-    }
-}
-
 /* Expression parser.  */
 
 /* We allow an empty expression, and just assume (absolute,0) silently.
@@ -1888,7 +1855,9 @@  expr (int rankarg,		/* Larger # is higher rank.  */
 
       input_line_pointer += op_chars;	/* -> after operator.  */
 
-      right.X_md = 0;
+#ifdef md_expr_init_rest
+      md_expr_init_rest (&right);
+#endif
       rightseg = expr (op_rank[op_left], &right, mode);
       if (right.X_op == O_absent)
 	{