[PATCH 1/6] x86: restrict use of register aliases

Jan Beulich jbeulich@suse.com
Wed Jun 3 10:15:11 GMT 2020


Register aliases (created e.g. via .set) check their target register at
the time of creation of the alias. While this makes sense, it's not
enough: The underlying register must also be "visible" at the time of
use. Wrong use of such aliases would lead to internal errors in e.g.
add_prefix() or build_modrm_byte().

Split the checking part of parse_real_register() into a new helper
function and use it also from the latter part of parse_register() (at
the same time replacing a minor open coded part of it).

Since parse_register() returning NULL already has a meaning, a fake new
"bad register" indicator gets added, which all callers need to check
for.

gas/
2020-06-XX  Jan Beulich  <jbeulich@suse.com>

	* config/tc-i386.c (bad_reg): New.
	(check_VecOperations, i386_att_operand, i386_parse_name): Check
	for it.
	(check_register): New, broken out from ...
	(parse_real_register): ... here. Call it.
	(parse_register): Call it, and error upon failure.
	* testsuite/gas/i386/equ-bad.s, testsuite/gas/i386/equ-bad.l,
	testsuite/gas/i386/x86-64-equ-bad.s,
	testsuite/gas/i386/x86-64-equ-bad.l: New.
	* testsuite/gas/i386/i386.exp: Run new tests:

opcodes/
2020-06-XX  Jan Beulich  <jbeulich@suse.com>

	* i386-opc.h (reg_entry): Const-qualify reg_name field.

--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -210,6 +210,10 @@ static unsigned int x86_used_note = DEFA
 
 static const char *default_arch = DEFAULT_ARCH;
 
+/* parse_register() returns this when a register alias cannot be used.  */
+static const reg_entry bad_reg = { "<bad>", OPERAND_TYPE_NONE, 0, 0,
+				   { Dw2Inval, Dw2Inval } };
+
 /* This struct describes rounding control and SAE in the instruction.  */
 struct RC_Operation
 {
@@ -10176,6 +10180,9 @@ check_VecOperations (char *op_string, ch
 	  /* Check masking operation.  */
 	  else if ((mask = parse_register (op_string, &end_op)) != NULL)
 	    {
+	      if (mask == &bad_reg)
+		return NULL;
+
 	      /* k0 can't be used for write mask.  */
 	      if (mask->reg_type.bitfield.class != RegMask || !mask->reg_num)
 		{
@@ -11035,6 +11042,9 @@ i386_att_operand (char *operand_string)
     {
       i386_operand_type temp;
 
+      if (r == &bad_reg)
+	return 0;
+
       /* Check for a segment override by searching for ':' after a
 	 segment register.  */
       op_string = end_op;
@@ -11211,6 +11221,8 @@ i386_att_operand (char *operand_string)
 
 	      if (i.base_reg)
 		{
+		  if (i.base_reg == &bad_reg)
+		    return 0;
 		  base_string = end_op;
 		  if (is_space_char (*base_string))
 		    ++base_string;
@@ -11226,6 +11238,8 @@ i386_att_operand (char *operand_string)
 		  if ((i.index_reg = parse_register (base_string, &end_op))
 		      != NULL)
 		    {
+		      if (i.index_reg == &bad_reg)
+			return 0;
 		      base_string = end_op;
 		      if (is_space_char (*base_string))
 			++base_string;
@@ -12331,6 +12345,73 @@ output_invalid (int c)
   return output_invalid_buf;
 }
 
+/* Verify that @r can be used in the current context.  */
+
+static bfd_boolean check_register (const reg_entry *r)
+{
+  if (allow_pseudo_reg)
+    return TRUE;
+
+  if (operand_type_all_zero (&r->reg_type))
+    return FALSE;
+
+  if ((r->reg_type.bitfield.dword
+       || (r->reg_type.bitfield.class == SReg && r->reg_num > 3)
+       || r->reg_type.bitfield.class == RegCR
+       || r->reg_type.bitfield.class == RegDR
+       || r->reg_type.bitfield.class == RegTR)
+      && !cpu_arch_flags.bitfield.cpui386)
+    return FALSE;
+
+  if (r->reg_type.bitfield.class == RegMMX && !cpu_arch_flags.bitfield.cpummx)
+    return FALSE;
+
+  if (!cpu_arch_flags.bitfield.cpuavx512f)
+    {
+      if (r->reg_type.bitfield.zmmword
+	  || r->reg_type.bitfield.class == RegMask)
+	return FALSE;
+
+      if (!cpu_arch_flags.bitfield.cpuavx)
+	{
+	  if (r->reg_type.bitfield.ymmword)
+	    return FALSE;
+
+	  if (!cpu_arch_flags.bitfield.cpusse && r->reg_type.bitfield.xmmword)
+	    return FALSE;
+	}
+    }
+
+  if (r->reg_type.bitfield.class == RegBND && !cpu_arch_flags.bitfield.cpumpx)
+    return FALSE;
+
+  /* Don't allow fake index register unless allow_index_reg isn't 0. */
+  if (!allow_index_reg && r->reg_num == RegIZ)
+    return FALSE;
+
+  /* Upper 16 vector registers are only available with VREX in 64bit
+     mode, and require EVEX encoding.  */
+  if (r->reg_flags & RegVRex)
+    {
+      if (!cpu_arch_flags.bitfield.cpuavx512f
+	  || flag_code != CODE_64BIT)
+	return FALSE;
+
+      i.vec_encoding = vex_encoding_evex;
+    }
+
+  if (((r->reg_flags & (RegRex64 | RegRex)) || r->reg_type.bitfield.qword)
+      && (!cpu_arch_flags.bitfield.cpulm || r->reg_type.bitfield.class != RegCR)
+      && flag_code != CODE_64BIT)
+    return FALSE;
+
+  if (r->reg_type.bitfield.class == SReg && r->reg_num == RegFlat
+      && !intel_syntax)
+    return FALSE;
+
+  return TRUE;
+}
+
 /* REG_STRING starts *before* REGISTER_PREFIX.  */
 
 static const reg_entry *
@@ -12400,67 +12481,7 @@ parse_real_register (char *reg_string, c
 	}
     }
 
-  if (r == NULL || allow_pseudo_reg)
-    return r;
-
-  if (operand_type_all_zero (&r->reg_type))
-    return (const reg_entry *) NULL;
-
-  if ((r->reg_type.bitfield.dword
-       || (r->reg_type.bitfield.class == SReg && r->reg_num > 3)
-       || r->reg_type.bitfield.class == RegCR
-       || r->reg_type.bitfield.class == RegDR
-       || r->reg_type.bitfield.class == RegTR)
-      && !cpu_arch_flags.bitfield.cpui386)
-    return (const reg_entry *) NULL;
-
-  if (r->reg_type.bitfield.class == RegMMX && !cpu_arch_flags.bitfield.cpummx)
-    return (const reg_entry *) NULL;
-
-  if (!cpu_arch_flags.bitfield.cpuavx512f)
-    {
-      if (r->reg_type.bitfield.zmmword
-	  || r->reg_type.bitfield.class == RegMask)
-	return (const reg_entry *) NULL;
-
-      if (!cpu_arch_flags.bitfield.cpuavx)
-	{
-	  if (r->reg_type.bitfield.ymmword)
-	    return (const reg_entry *) NULL;
-
-	  if (!cpu_arch_flags.bitfield.cpusse && r->reg_type.bitfield.xmmword)
-	    return (const reg_entry *) NULL;
-	}
-    }
-
-  if (r->reg_type.bitfield.class == RegBND && !cpu_arch_flags.bitfield.cpumpx)
-    return (const reg_entry *) NULL;
-
-  /* Don't allow fake index register unless allow_index_reg isn't 0. */
-  if (!allow_index_reg && r->reg_num == RegIZ)
-    return (const reg_entry *) NULL;
-
-  /* Upper 16 vector registers are only available with VREX in 64bit
-     mode, and require EVEX encoding.  */
-  if (r->reg_flags & RegVRex)
-    {
-      if (!cpu_arch_flags.bitfield.cpuavx512f
-	  || flag_code != CODE_64BIT)
-	return (const reg_entry *) NULL;
-
-      i.vec_encoding = vex_encoding_evex;
-    }
-
-  if (((r->reg_flags & (RegRex64 | RegRex)) || r->reg_type.bitfield.qword)
-      && (!cpu_arch_flags.bitfield.cpulm || r->reg_type.bitfield.class != RegCR)
-      && flag_code != CODE_64BIT)
-    return (const reg_entry *) NULL;
-
-  if (r->reg_type.bitfield.class == SReg && r->reg_num == RegFlat
-      && !intel_syntax)
-    return (const reg_entry *) NULL;
-
-  return r;
+  return r && check_register (r) ? r : NULL;
 }
 
 /* REG_STRING starts *before* REGISTER_PREFIX.  */
@@ -12491,8 +12512,12 @@ parse_register (char *reg_string, char *
 	  know (e->X_add_number >= 0
 		&& (valueT) e->X_add_number < i386_regtab_size);
 	  r = i386_regtab + e->X_add_number;
-	  if ((r->reg_flags & RegVRex))
-	    i.vec_encoding = vex_encoding_evex;
+	  if (!check_register (r))
+	    {
+	      as_bad (_("register '%s%s' cannot be used here"),
+		      register_prefix, r->reg_name);
+	      r = &bad_reg;
+	    }
 	  *end_op = input_line_pointer;
 	}
       *input_line_pointer = c;
@@ -12513,8 +12538,13 @@ i386_parse_name (char *name, expressionS
     {
       *nextcharP = *input_line_pointer;
       *input_line_pointer = 0;
-      e->X_op = O_register;
-      e->X_add_number = r - i386_regtab;
+      if (r != &bad_reg)
+	{
+	  e->X_op = O_register;
+	  e->X_add_number = r - i386_regtab;
+	}
+      else
+	  e->X_op = O_illegal;
       return 1;
     }
   input_line_pointer = end;
--- /dev/null
+++ b/gas/testsuite/gas/i386/equ-bad.l
@@ -0,0 +1,3 @@
+.*: Assembler messages:
+.*:8: Error: .*%ebx.*
+.*:9: Error: .*%ebx.*
--- /dev/null
+++ b/gas/testsuite/gas/i386/equ-bad.s
@@ -0,0 +1,9 @@
+	.text
+	.arch generic32
+equ:
+	.set	xBX, %ebx
+
+	.code16
+	.arch i286
+	inc	xBX
+	incb	(xBX)
--- a/gas/testsuite/gas/i386/i386.exp
+++ b/gas/testsuite/gas/i386/i386.exp
@@ -91,6 +91,7 @@ if [expr ([istarget "i*86-*-*"] ||  [ist
     run_list_test "suffix-bad"
     run_dump_test "immed32"
     run_dump_test "equ"
+    run_list_test "equ-bad"
     run_dump_test "divide"
     run_dump_test "padlock"
     run_dump_test "crx"
@@ -924,6 +925,7 @@ if [expr ([istarget "i*86-*-*"] || [ista
     run_dump_test "x86-64-prefetchwt1-intel"
     run_dump_test "x86-64-se1"
     run_dump_test "x86-64-equ"
+    run_list_test "x86-64-equ-bad"
     run_dump_test "x86-64-avx512f_vl-intel"
     run_dump_test "x86-64-avx512f_vl-opts-intel"
     run_dump_test "x86-64-avx512f_vl-opts"
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-equ-bad.l
@@ -0,0 +1,8 @@
+.*: Assembler messages:
+.*:11: Error: .*'%xmm18'.*
+.*:13: Error: .*'%dil'.*
+.*:14: Error: .*'%rdi'.*
+.*:15: Error: .*'%r8'.*
+.*:16: Error: .*'%r9d'.*
+.*:18: Error: .*'%r8'.*
+.*:19: Error: .*'%r9d'.*
--- /dev/null
+++ b/gas/testsuite/gas/i386/x86-64-equ-bad.s
@@ -0,0 +1,19 @@
+	.text
+	.code64
+equ:
+	.set R18, %xmm18
+	.set lDI, %dil
+	.set xDI, %rdi
+	.set x8, %r8
+	.set x9, %r9d
+
+	.code32
+	vmovaps %xmm0, R18
+
+	inc	lDI
+	incl	(xDI)
+	inc	x8
+	inc	x9
+
+	shlx	x8, x8, x8
+	shlx	x9, x9, x9
--- a/opcodes/i386-opc.h
+++ b/opcodes/i386-opc.h
@@ -906,7 +906,7 @@ extern const insn_template i386_optab[];
 /* these are for register name --> number & type hash lookup */
 typedef struct
 {
-  char *reg_name;
+  const char *reg_name;
   i386_operand_type reg_type;
   unsigned char reg_flags;
 #define RegRex	    0x1  /* Extended register.  */



More information about the Binutils mailing list