forked from mirrors/linux
		
	x86/insn: Stop decoding i64 instructions in x86-64 mode at opcode
In commit 2e044911be ("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
			
			
This commit is contained in:
		
							parent
							
								
									ca698ec2f0
								
							
						
					
					
						commit
						4b626015e1
					
				
					 8 changed files with 44 additions and 8 deletions
				
			
		|  | @ -82,6 +82,7 @@ | ||||||
| #define INAT_NO_REX2	(1 << (INAT_FLAG_OFFS + 8)) | #define INAT_NO_REX2	(1 << (INAT_FLAG_OFFS + 8)) | ||||||
| #define INAT_REX2_VARIANT	(1 << (INAT_FLAG_OFFS + 9)) | #define INAT_REX2_VARIANT	(1 << (INAT_FLAG_OFFS + 9)) | ||||||
| #define INAT_EVEX_SCALABLE	(1 << (INAT_FLAG_OFFS + 10)) | #define INAT_EVEX_SCALABLE	(1 << (INAT_FLAG_OFFS + 10)) | ||||||
|  | #define INAT_INV64	(1 << (INAT_FLAG_OFFS + 11)) | ||||||
| /* Attribute making macros for attribute tables */ | /* Attribute making macros for attribute tables */ | ||||||
| #define INAT_MAKE_PREFIX(pfx)	(pfx << INAT_PFX_OFFS) | #define INAT_MAKE_PREFIX(pfx)	(pfx << INAT_PFX_OFFS) | ||||||
| #define INAT_MAKE_ESCAPE(esc)	(esc << INAT_ESC_OFFS) | #define INAT_MAKE_ESCAPE(esc)	(esc << INAT_ESC_OFFS) | ||||||
|  | @ -242,4 +243,9 @@ static inline int inat_evex_scalable(insn_attr_t attr) | ||||||
| { | { | ||||||
| 	return attr & INAT_EVEX_SCALABLE; | 	return attr & INAT_EVEX_SCALABLE; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | static inline int inat_is_invalid64(insn_attr_t attr) | ||||||
|  | { | ||||||
|  | 	return attr & INAT_INV64; | ||||||
|  | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -324,6 +324,11 @@ int insn_get_opcode(struct insn *insn) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	insn->attr = inat_get_opcode_attribute(op); | 	insn->attr = inat_get_opcode_attribute(op); | ||||||
|  | 	if (insn->x86_64 && inat_is_invalid64(insn->attr)) { | ||||||
|  | 		/* This instruction is invalid, like UD2. Stop decoding. */ | ||||||
|  | 		insn->attr &= INAT_INV64; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	while (inat_is_escape(insn->attr)) { | 	while (inat_is_escape(insn->attr)) { | ||||||
| 		/* Get escaped opcode */ | 		/* Get escaped opcode */ | ||||||
| 		op = get_next(insn_byte_t, insn); | 		op = get_next(insn_byte_t, insn); | ||||||
|  | @ -337,6 +342,7 @@ int insn_get_opcode(struct insn *insn) | ||||||
| 		insn->attr = 0; | 		insn->attr = 0; | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
| end: | end: | ||||||
| 	opcode->got = 1; | 	opcode->got = 1; | ||||||
| 	return 0; | 	return 0; | ||||||
|  | @ -658,7 +664,6 @@ int insn_get_immediate(struct insn *insn) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (!inat_has_immediate(insn->attr)) | 	if (!inat_has_immediate(insn->attr)) | ||||||
| 		/* no immediates */ |  | ||||||
| 		goto done; | 		goto done; | ||||||
| 
 | 
 | ||||||
| 	switch (inat_immediate_size(insn->attr)) { | 	switch (inat_immediate_size(insn->attr)) { | ||||||
|  |  | ||||||
|  | @ -147,7 +147,7 @@ AVXcode: | ||||||
| # 0x60 - 0x6f | # 0x60 - 0x6f | ||||||
| 60: PUSHA/PUSHAD (i64) | 60: PUSHA/PUSHAD (i64) | ||||||
| 61: POPA/POPAD (i64) | 61: POPA/POPAD (i64) | ||||||
| 62: BOUND Gv,Ma (i64) | EVEX (Prefix) | 62: BOUND Gv,Ma (i64) | EVEX (Prefix),(o64) | ||||||
| 63: ARPL Ew,Gw (i64) | MOVSXD Gv,Ev (o64) | 63: ARPL Ew,Gw (i64) | MOVSXD Gv,Ev (o64) | ||||||
| 64: SEG=FS (Prefix) | 64: SEG=FS (Prefix) | ||||||
| 65: SEG=GS (Prefix) | 65: SEG=GS (Prefix) | ||||||
|  | @ -253,8 +253,8 @@ c0: Grp2 Eb,Ib (1A) | ||||||
| c1: Grp2 Ev,Ib (1A) | c1: Grp2 Ev,Ib (1A) | ||||||
| c2: RETN Iw (f64) | c2: RETN Iw (f64) | ||||||
| c3: RETN | c3: RETN | ||||||
| c4: LES Gz,Mp (i64) | VEX+2byte (Prefix) | c4: LES Gz,Mp (i64) | VEX+2byte (Prefix),(o64) | ||||||
| c5: LDS Gz,Mp (i64) | VEX+1byte (Prefix) | c5: LDS Gz,Mp (i64) | VEX+1byte (Prefix),(o64) | ||||||
| c6: Grp11A Eb,Ib (1A) | c6: Grp11A Eb,Ib (1A) | ||||||
| c7: Grp11B Ev,Iz (1A) | c7: Grp11B Ev,Iz (1A) | ||||||
| c8: ENTER Iw,Ib | c8: ENTER Iw,Ib | ||||||
|  |  | ||||||
|  | @ -64,6 +64,8 @@ BEGIN { | ||||||
| 
 | 
 | ||||||
| 	modrm_expr = "^([CDEGMNPQRSUVW/][a-z]+|NTA|T[012])" | 	modrm_expr = "^([CDEGMNPQRSUVW/][a-z]+|NTA|T[012])" | ||||||
| 	force64_expr = "\\([df]64\\)" | 	force64_expr = "\\([df]64\\)" | ||||||
|  | 	invalid64_expr = "\\(i64\\)" | ||||||
|  | 	only64_expr = "\\(o64\\)" | ||||||
| 	rex_expr = "^((REX(\\.[XRWB]+)+)|(REX$))" | 	rex_expr = "^((REX(\\.[XRWB]+)+)|(REX$))" | ||||||
| 	rex2_expr = "\\(REX2\\)" | 	rex2_expr = "\\(REX2\\)" | ||||||
| 	no_rex2_expr = "\\(!REX2\\)" | 	no_rex2_expr = "\\(!REX2\\)" | ||||||
|  | @ -319,6 +321,11 @@ function convert_operands(count,opnd,       i,j,imm,mod) | ||||||
| 		if (match(ext, force64_expr)) | 		if (match(ext, force64_expr)) | ||||||
| 			flags = add_flags(flags, "INAT_FORCE64") | 			flags = add_flags(flags, "INAT_FORCE64") | ||||||
| 
 | 
 | ||||||
|  | 		# check invalid in 64-bit (and no only64) | ||||||
|  | 		if (match(ext, invalid64_expr) && | ||||||
|  | 		    !match($0, only64_expr)) | ||||||
|  | 			flags = add_flags(flags, "INAT_INV64") | ||||||
|  | 
 | ||||||
| 		# check REX2 not allowed | 		# check REX2 not allowed | ||||||
| 		if (match(ext, no_rex2_expr)) | 		if (match(ext, no_rex2_expr)) | ||||||
| 			flags = add_flags(flags, "INAT_NO_REX2") | 			flags = add_flags(flags, "INAT_NO_REX2") | ||||||
|  |  | ||||||
|  | @ -82,6 +82,7 @@ | ||||||
| #define INAT_NO_REX2	(1 << (INAT_FLAG_OFFS + 8)) | #define INAT_NO_REX2	(1 << (INAT_FLAG_OFFS + 8)) | ||||||
| #define INAT_REX2_VARIANT	(1 << (INAT_FLAG_OFFS + 9)) | #define INAT_REX2_VARIANT	(1 << (INAT_FLAG_OFFS + 9)) | ||||||
| #define INAT_EVEX_SCALABLE	(1 << (INAT_FLAG_OFFS + 10)) | #define INAT_EVEX_SCALABLE	(1 << (INAT_FLAG_OFFS + 10)) | ||||||
|  | #define INAT_INV64	(1 << (INAT_FLAG_OFFS + 11)) | ||||||
| /* Attribute making macros for attribute tables */ | /* Attribute making macros for attribute tables */ | ||||||
| #define INAT_MAKE_PREFIX(pfx)	(pfx << INAT_PFX_OFFS) | #define INAT_MAKE_PREFIX(pfx)	(pfx << INAT_PFX_OFFS) | ||||||
| #define INAT_MAKE_ESCAPE(esc)	(esc << INAT_ESC_OFFS) | #define INAT_MAKE_ESCAPE(esc)	(esc << INAT_ESC_OFFS) | ||||||
|  | @ -242,4 +243,9 @@ static inline int inat_evex_scalable(insn_attr_t attr) | ||||||
| { | { | ||||||
| 	return attr & INAT_EVEX_SCALABLE; | 	return attr & INAT_EVEX_SCALABLE; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | static inline int inat_is_invalid64(insn_attr_t attr) | ||||||
|  | { | ||||||
|  | 	return attr & INAT_INV64; | ||||||
|  | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -324,6 +324,11 @@ int insn_get_opcode(struct insn *insn) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	insn->attr = inat_get_opcode_attribute(op); | 	insn->attr = inat_get_opcode_attribute(op); | ||||||
|  | 	if (insn->x86_64 && inat_is_invalid64(insn->attr)) { | ||||||
|  | 		/* This instruction is invalid, like UD2. Stop decoding. */ | ||||||
|  | 		insn->attr &= INAT_INV64; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	while (inat_is_escape(insn->attr)) { | 	while (inat_is_escape(insn->attr)) { | ||||||
| 		/* Get escaped opcode */ | 		/* Get escaped opcode */ | ||||||
| 		op = get_next(insn_byte_t, insn); | 		op = get_next(insn_byte_t, insn); | ||||||
|  | @ -337,6 +342,7 @@ int insn_get_opcode(struct insn *insn) | ||||||
| 		insn->attr = 0; | 		insn->attr = 0; | ||||||
| 		return -EINVAL; | 		return -EINVAL; | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
| end: | end: | ||||||
| 	opcode->got = 1; | 	opcode->got = 1; | ||||||
| 	return 0; | 	return 0; | ||||||
|  | @ -658,7 +664,6 @@ int insn_get_immediate(struct insn *insn) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (!inat_has_immediate(insn->attr)) | 	if (!inat_has_immediate(insn->attr)) | ||||||
| 		/* no immediates */ |  | ||||||
| 		goto done; | 		goto done; | ||||||
| 
 | 
 | ||||||
| 	switch (inat_immediate_size(insn->attr)) { | 	switch (inat_immediate_size(insn->attr)) { | ||||||
|  |  | ||||||
|  | @ -147,7 +147,7 @@ AVXcode: | ||||||
| # 0x60 - 0x6f | # 0x60 - 0x6f | ||||||
| 60: PUSHA/PUSHAD (i64) | 60: PUSHA/PUSHAD (i64) | ||||||
| 61: POPA/POPAD (i64) | 61: POPA/POPAD (i64) | ||||||
| 62: BOUND Gv,Ma (i64) | EVEX (Prefix) | 62: BOUND Gv,Ma (i64) | EVEX (Prefix),(o64) | ||||||
| 63: ARPL Ew,Gw (i64) | MOVSXD Gv,Ev (o64) | 63: ARPL Ew,Gw (i64) | MOVSXD Gv,Ev (o64) | ||||||
| 64: SEG=FS (Prefix) | 64: SEG=FS (Prefix) | ||||||
| 65: SEG=GS (Prefix) | 65: SEG=GS (Prefix) | ||||||
|  | @ -253,8 +253,8 @@ c0: Grp2 Eb,Ib (1A) | ||||||
| c1: Grp2 Ev,Ib (1A) | c1: Grp2 Ev,Ib (1A) | ||||||
| c2: RETN Iw (f64) | c2: RETN Iw (f64) | ||||||
| c3: RETN | c3: RETN | ||||||
| c4: LES Gz,Mp (i64) | VEX+2byte (Prefix) | c4: LES Gz,Mp (i64) | VEX+2byte (Prefix),(o64) | ||||||
| c5: LDS Gz,Mp (i64) | VEX+1byte (Prefix) | c5: LDS Gz,Mp (i64) | VEX+1byte (Prefix),(o64) | ||||||
| c6: Grp11A Eb,Ib (1A) | c6: Grp11A Eb,Ib (1A) | ||||||
| c7: Grp11B Ev,Iz (1A) | c7: Grp11B Ev,Iz (1A) | ||||||
| c8: ENTER Iw,Ib | c8: ENTER Iw,Ib | ||||||
|  |  | ||||||
|  | @ -64,6 +64,8 @@ BEGIN { | ||||||
| 
 | 
 | ||||||
| 	modrm_expr = "^([CDEGMNPQRSUVW/][a-z]+|NTA|T[012])" | 	modrm_expr = "^([CDEGMNPQRSUVW/][a-z]+|NTA|T[012])" | ||||||
| 	force64_expr = "\\([df]64\\)" | 	force64_expr = "\\([df]64\\)" | ||||||
|  | 	invalid64_expr = "\\(i64\\)" | ||||||
|  | 	only64_expr = "\\(o64\\)" | ||||||
| 	rex_expr = "^((REX(\\.[XRWB]+)+)|(REX$))" | 	rex_expr = "^((REX(\\.[XRWB]+)+)|(REX$))" | ||||||
| 	rex2_expr = "\\(REX2\\)" | 	rex2_expr = "\\(REX2\\)" | ||||||
| 	no_rex2_expr = "\\(!REX2\\)" | 	no_rex2_expr = "\\(!REX2\\)" | ||||||
|  | @ -319,6 +321,11 @@ function convert_operands(count,opnd,       i,j,imm,mod) | ||||||
| 		if (match(ext, force64_expr)) | 		if (match(ext, force64_expr)) | ||||||
| 			flags = add_flags(flags, "INAT_FORCE64") | 			flags = add_flags(flags, "INAT_FORCE64") | ||||||
| 
 | 
 | ||||||
|  | 		# check invalid in 64-bit (and no only64) | ||||||
|  | 		if (match(ext, invalid64_expr) && | ||||||
|  | 		    !match($0, only64_expr)) | ||||||
|  | 			flags = add_flags(flags, "INAT_INV64") | ||||||
|  | 
 | ||||||
| 		# check REX2 not allowed | 		# check REX2 not allowed | ||||||
| 		if (match(ext, no_rex2_expr)) | 		if (match(ext, no_rex2_expr)) | ||||||
| 			flags = add_flags(flags, "INAT_NO_REX2") | 			flags = add_flags(flags, "INAT_NO_REX2") | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Masami Hiramatsu (Google)
						Masami Hiramatsu (Google)