Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/insn: Stop decoding i64 instructions in x86-64 mode at opcode

In commit 2e044911be75 ("x86/traps: Decode 0xEA instructions as #UD")
FineIBT starts using 0xEA as an invalid instruction like UD2. But
insn decoder always returns the length of "0xea" instruction as 7
because it does not check the (i64) superscript.

The x86 instruction decoder should also decode 0xEA on x86-64 as
a one-byte invalid instruction by decoding the "(i64)" superscript tag.

This stops decoding instruction which has (i64) but does not have (o64)
superscript in 64-bit mode at opcode and skips other fields.

With this change, insn_decoder_test says 0xea is 1 byte length if
x86-64 (-y option means 64-bit):

$ printf "0:\tea\t\n" | insn_decoder_test -y -v
insn_decoder_test: success: Decoded and checked 1 instructions

Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/r/174580490000.388420.5225447607417115496.stgit@devnote2

authored by

Masami Hiramatsu (Google) and committed by
Ingo Molnar
4b626015 ca698ec2

+44 -8
+6
arch/x86/include/asm/inat.h
··· 82 82 #define INAT_NO_REX2 (1 << (INAT_FLAG_OFFS + 8)) 83 83 #define INAT_REX2_VARIANT (1 << (INAT_FLAG_OFFS + 9)) 84 84 #define INAT_EVEX_SCALABLE (1 << (INAT_FLAG_OFFS + 10)) 85 + #define INAT_INV64 (1 << (INAT_FLAG_OFFS + 11)) 85 86 /* Attribute making macros for attribute tables */ 86 87 #define INAT_MAKE_PREFIX(pfx) (pfx << INAT_PFX_OFFS) 87 88 #define INAT_MAKE_ESCAPE(esc) (esc << INAT_ESC_OFFS) ··· 242 241 static inline int inat_evex_scalable(insn_attr_t attr) 243 242 { 244 243 return attr & INAT_EVEX_SCALABLE; 244 + } 245 + 246 + static inline int inat_is_invalid64(insn_attr_t attr) 247 + { 248 + return attr & INAT_INV64; 245 249 } 246 250 #endif
+6 -1
arch/x86/lib/insn.c
··· 324 324 } 325 325 326 326 insn->attr = inat_get_opcode_attribute(op); 327 + if (insn->x86_64 && inat_is_invalid64(insn->attr)) { 328 + /* This instruction is invalid, like UD2. Stop decoding. */ 329 + insn->attr &= INAT_INV64; 330 + } 331 + 327 332 while (inat_is_escape(insn->attr)) { 328 333 /* Get escaped opcode */ 329 334 op = get_next(insn_byte_t, insn); ··· 342 337 insn->attr = 0; 343 338 return -EINVAL; 344 339 } 340 + 345 341 end: 346 342 opcode->got = 1; 347 343 return 0; ··· 664 658 } 665 659 666 660 if (!inat_has_immediate(insn->attr)) 667 - /* no immediates */ 668 661 goto done; 669 662 670 663 switch (inat_immediate_size(insn->attr)) {
+3 -3
arch/x86/lib/x86-opcode-map.txt
··· 147 147 # 0x60 - 0x6f 148 148 60: PUSHA/PUSHAD (i64) 149 149 61: POPA/POPAD (i64) 150 - 62: BOUND Gv,Ma (i64) | EVEX (Prefix) 150 + 62: BOUND Gv,Ma (i64) | EVEX (Prefix),(o64) 151 151 63: ARPL Ew,Gw (i64) | MOVSXD Gv,Ev (o64) 152 152 64: SEG=FS (Prefix) 153 153 65: SEG=GS (Prefix) ··· 253 253 c1: Grp2 Ev,Ib (1A) 254 254 c2: RETN Iw (f64) 255 255 c3: RETN 256 - c4: LES Gz,Mp (i64) | VEX+2byte (Prefix) 257 - c5: LDS Gz,Mp (i64) | VEX+1byte (Prefix) 256 + c4: LES Gz,Mp (i64) | VEX+2byte (Prefix),(o64) 257 + c5: LDS Gz,Mp (i64) | VEX+1byte (Prefix),(o64) 258 258 c6: Grp11A Eb,Ib (1A) 259 259 c7: Grp11B Ev,Iz (1A) 260 260 c8: ENTER Iw,Ib
+7
arch/x86/tools/gen-insn-attr-x86.awk
··· 64 64 65 65 modrm_expr = "^([CDEGMNPQRSUVW/][a-z]+|NTA|T[012])" 66 66 force64_expr = "\\([df]64\\)" 67 + invalid64_expr = "\\(i64\\)" 68 + only64_expr = "\\(o64\\)" 67 69 rex_expr = "^((REX(\\.[XRWB]+)+)|(REX$))" 68 70 rex2_expr = "\\(REX2\\)" 69 71 no_rex2_expr = "\\(!REX2\\)" ··· 320 318 # check force(or default) 64bit 321 319 if (match(ext, force64_expr)) 322 320 flags = add_flags(flags, "INAT_FORCE64") 321 + 322 + # check invalid in 64-bit (and no only64) 323 + if (match(ext, invalid64_expr) && 324 + !match($0, only64_expr)) 325 + flags = add_flags(flags, "INAT_INV64") 323 326 324 327 # check REX2 not allowed 325 328 if (match(ext, no_rex2_expr))
+6
tools/arch/x86/include/asm/inat.h
··· 82 82 #define INAT_NO_REX2 (1 << (INAT_FLAG_OFFS + 8)) 83 83 #define INAT_REX2_VARIANT (1 << (INAT_FLAG_OFFS + 9)) 84 84 #define INAT_EVEX_SCALABLE (1 << (INAT_FLAG_OFFS + 10)) 85 + #define INAT_INV64 (1 << (INAT_FLAG_OFFS + 11)) 85 86 /* Attribute making macros for attribute tables */ 86 87 #define INAT_MAKE_PREFIX(pfx) (pfx << INAT_PFX_OFFS) 87 88 #define INAT_MAKE_ESCAPE(esc) (esc << INAT_ESC_OFFS) ··· 242 241 static inline int inat_evex_scalable(insn_attr_t attr) 243 242 { 244 243 return attr & INAT_EVEX_SCALABLE; 244 + } 245 + 246 + static inline int inat_is_invalid64(insn_attr_t attr) 247 + { 248 + return attr & INAT_INV64; 245 249 } 246 250 #endif
+6 -1
tools/arch/x86/lib/insn.c
··· 324 324 } 325 325 326 326 insn->attr = inat_get_opcode_attribute(op); 327 + if (insn->x86_64 && inat_is_invalid64(insn->attr)) { 328 + /* This instruction is invalid, like UD2. Stop decoding. */ 329 + insn->attr &= INAT_INV64; 330 + } 331 + 327 332 while (inat_is_escape(insn->attr)) { 328 333 /* Get escaped opcode */ 329 334 op = get_next(insn_byte_t, insn); ··· 342 337 insn->attr = 0; 343 338 return -EINVAL; 344 339 } 340 + 345 341 end: 346 342 opcode->got = 1; 347 343 return 0; ··· 664 658 } 665 659 666 660 if (!inat_has_immediate(insn->attr)) 667 - /* no immediates */ 668 661 goto done; 669 662 670 663 switch (inat_immediate_size(insn->attr)) {
+3 -3
tools/arch/x86/lib/x86-opcode-map.txt
··· 147 147 # 0x60 - 0x6f 148 148 60: PUSHA/PUSHAD (i64) 149 149 61: POPA/POPAD (i64) 150 - 62: BOUND Gv,Ma (i64) | EVEX (Prefix) 150 + 62: BOUND Gv,Ma (i64) | EVEX (Prefix),(o64) 151 151 63: ARPL Ew,Gw (i64) | MOVSXD Gv,Ev (o64) 152 152 64: SEG=FS (Prefix) 153 153 65: SEG=GS (Prefix) ··· 253 253 c1: Grp2 Ev,Ib (1A) 254 254 c2: RETN Iw (f64) 255 255 c3: RETN 256 - c4: LES Gz,Mp (i64) | VEX+2byte (Prefix) 257 - c5: LDS Gz,Mp (i64) | VEX+1byte (Prefix) 256 + c4: LES Gz,Mp (i64) | VEX+2byte (Prefix),(o64) 257 + c5: LDS Gz,Mp (i64) | VEX+1byte (Prefix),(o64) 258 258 c6: Grp11A Eb,Ib (1A) 259 259 c7: Grp11B Ev,Iz (1A) 260 260 c8: ENTER Iw,Ib
+7
tools/arch/x86/tools/gen-insn-attr-x86.awk
··· 64 64 65 65 modrm_expr = "^([CDEGMNPQRSUVW/][a-z]+|NTA|T[012])" 66 66 force64_expr = "\\([df]64\\)" 67 + invalid64_expr = "\\(i64\\)" 68 + only64_expr = "\\(o64\\)" 67 69 rex_expr = "^((REX(\\.[XRWB]+)+)|(REX$))" 68 70 rex2_expr = "\\(REX2\\)" 69 71 no_rex2_expr = "\\(!REX2\\)" ··· 320 318 # check force(or default) 64bit 321 319 if (match(ext, force64_expr)) 322 320 flags = add_flags(flags, "INAT_FORCE64") 321 + 322 + # check invalid in 64-bit (and no only64) 323 + if (match(ext, invalid64_expr) && 324 + !match($0, only64_expr)) 325 + flags = add_flags(flags, "INAT_INV64") 323 326 324 327 # check REX2 not allowed 325 328 if (match(ext, no_rex2_expr))