From 8a6fb3f9bb5105e58f6800de9089a4bdb0cc0cd6 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Mon, 8 Jun 2020 08:37:47 +0200 Subject: [PATCH] x86: restrict use of register aliases 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/ChangeLog | 13 ++ gas/config/tc-i386.c | 160 ++++++++++++++---------- gas/testsuite/gas/i386/equ-bad.l | 3 + gas/testsuite/gas/i386/equ-bad.s | 9 ++ gas/testsuite/gas/i386/i386.exp | 2 + gas/testsuite/gas/i386/x86-64-equ-bad.l | 8 ++ gas/testsuite/gas/i386/x86-64-equ-bad.s | 19 +++ opcodes/ChangeLog | 4 + opcodes/i386-opc.h | 2 +- 9 files changed, 154 insertions(+), 66 deletions(-) create mode 100644 gas/testsuite/gas/i386/equ-bad.l create mode 100644 gas/testsuite/gas/i386/equ-bad.s create mode 100644 gas/testsuite/gas/i386/x86-64-equ-bad.l create mode 100644 gas/testsuite/gas/i386/x86-64-equ-bad.s diff --git a/gas/ChangeLog b/gas/ChangeLog index b532af959f..d244ab4c57 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,16 @@ +2020-06-08 Jan Beulich + + * 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. + 2020-06-06 Alan Modra * config/tc-ppc.c (md_show_usage): Mention -mpower10 and -mpwr10. diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c index ae1bd0d5bb..e34ff8568d 100644 --- a/gas/config/tc-i386.c +++ b/gas/config/tc-i386.c @@ -210,6 +210,10 @@ static unsigned int x86_used_note = DEFAULT_X86_USED_NOTE; static const char *default_arch = DEFAULT_ARCH; +/* parse_register() returns this when a register alias cannot be used. */ +static const reg_entry bad_reg = { "", 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, char *op_end) /* 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, char **end_op) } } - 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 **end_op) 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 *e, char *nextcharP) { *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; diff --git a/gas/testsuite/gas/i386/equ-bad.l b/gas/testsuite/gas/i386/equ-bad.l new file mode 100644 index 0000000000..47cda1afc1 --- /dev/null +++ b/gas/testsuite/gas/i386/equ-bad.l @@ -0,0 +1,3 @@ +.*: Assembler messages: +.*:8: Error: .*%ebx.* +.*:9: Error: .*%ebx.* diff --git a/gas/testsuite/gas/i386/equ-bad.s b/gas/testsuite/gas/i386/equ-bad.s new file mode 100644 index 0000000000..ca79b26f79 --- /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) diff --git a/gas/testsuite/gas/i386/i386.exp b/gas/testsuite/gas/i386/i386.exp index 3bacb80178..86dc1e4bd4 100644 --- a/gas/testsuite/gas/i386/i386.exp +++ b/gas/testsuite/gas/i386/i386.exp @@ -91,6 +91,7 @@ if [expr ([istarget "i*86-*-*"] || [istarget "x86_64-*-*"]) && [gas_32_check]] 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-*-*"] || [istarget "x86_64-*-*"]) && [gas_64_check]] t 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" diff --git a/gas/testsuite/gas/i386/x86-64-equ-bad.l b/gas/testsuite/gas/i386/x86-64-equ-bad.l new file mode 100644 index 0000000000..cf44d05ead --- /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'.* diff --git a/gas/testsuite/gas/i386/x86-64-equ-bad.s b/gas/testsuite/gas/i386/x86-64-equ-bad.s new file mode 100644 index 0000000000..483a1cf040 --- /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 diff --git a/opcodes/ChangeLog b/opcodes/ChangeLog index f09d599431..d405787a05 100644 --- a/opcodes/ChangeLog +++ b/opcodes/ChangeLog @@ -1,3 +1,7 @@ +2020-06-08 Jan Beulich + + * i386-opc.h (reg_entry): Const-qualify reg_name field. + 2020-06-06 Alan Modra * ppc-dis.c (ppc_opts): Accept -mpwr10/-Mpwr10. diff --git a/opcodes/i386-opc.h b/opcodes/i386-opc.h index e5a5dcb7dd..55726c1a7a 100644 --- 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. */ -- 2.34.1