]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gas: initialisation of expressionS in operand()
authorAlan Modra <amodra@gmail.com>
Fri, 3 Oct 2025 23:07:02 +0000 (08:37 +0930)
committerAlan Modra <amodra@gmail.com>
Sat, 4 Oct 2025 00:09:02 +0000 (09:39 +0930)
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.

gas/config/tc-microblaze.h
gas/config/tc-z80.h
gas/expr.c

index 6ac1b4a823542f197999daf5dae1076e7d4d04ac..1d0b56af033e9099ff0f13beef54a89519bab330 100644 (file)
@@ -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
 
index 25f58e607c656cb4d8409dee56ecb0af43fff244..50e09124ad60f5091351e797ca2208d2cd12db6d 100644 (file)
    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
index aaad288a8c81f44cf1eb7b8e7c9d01170f3abc65..b75cce9f7816130271d34443bbff18dd313a449e 100644 (file)
@@ -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)
     }
 }
 \f
-/* 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;
-    }
-}
-\f
 /* 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)
        {