diff options
Diffstat (limited to 'arch/x86/lib/insn-eval.c')
| -rw-r--r-- | arch/x86/lib/insn-eval.c | 66 | 
1 files changed, 52 insertions, 14 deletions
diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c index 4229950a5d78..bb0b3fe1e0a0 100644 --- a/arch/x86/lib/insn-eval.c +++ b/arch/x86/lib/insn-eval.c @@ -1415,6 +1415,25 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)  	}  } +static unsigned long insn_get_effective_ip(struct pt_regs *regs) +{ +	unsigned long seg_base = 0; + +	/* +	 * If not in user-space long mode, a custom code segment could be in +	 * use. This is true in protected mode (if the process defined a local +	 * descriptor table), or virtual-8086 mode. In most of the cases +	 * seg_base will be zero as in USER_CS. +	 */ +	if (!user_64bit_mode(regs)) { +		seg_base = insn_get_seg_base(regs, INAT_SEG_REG_CS); +		if (seg_base == -1L) +			return 0; +	} + +	return seg_base + regs->ip; +} +  /**   * insn_fetch_from_user() - Copy instruction bytes from user-space memory   * @regs:	Structure with register values as seen when entering kernel mode @@ -1431,24 +1450,43 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)   */  int insn_fetch_from_user(struct pt_regs *regs, unsigned char buf[MAX_INSN_SIZE])  { -	unsigned long seg_base = 0; +	unsigned long ip;  	int not_copied; -	/* -	 * If not in user-space long mode, a custom code segment could be in -	 * use. This is true in protected mode (if the process defined a local -	 * descriptor table), or virtual-8086 mode. In most of the cases -	 * seg_base will be zero as in USER_CS. -	 */ -	if (!user_64bit_mode(regs)) { -		seg_base = insn_get_seg_base(regs, INAT_SEG_REG_CS); -		if (seg_base == -1L) -			return 0; -	} +	ip = insn_get_effective_ip(regs); +	if (!ip) +		return 0; + +	not_copied = copy_from_user(buf, (void __user *)ip, MAX_INSN_SIZE); +	return MAX_INSN_SIZE - not_copied; +} + +/** + * insn_fetch_from_user_inatomic() - Copy instruction bytes from user-space memory + *                                   while in atomic code + * @regs:	Structure with register values as seen when entering kernel mode + * @buf:	Array to store the fetched instruction + * + * Gets the linear address of the instruction and copies the instruction bytes + * to the buf. This function must be used in atomic context. + * + * Returns: + * + * Number of instruction bytes copied. + * + * 0 if nothing was copied. + */ +int insn_fetch_from_user_inatomic(struct pt_regs *regs, unsigned char buf[MAX_INSN_SIZE]) +{ +	unsigned long ip; +	int not_copied; + +	ip = insn_get_effective_ip(regs); +	if (!ip) +		return 0; -	not_copied = copy_from_user(buf, (void __user *)(seg_base + regs->ip), -				    MAX_INSN_SIZE); +	not_copied = __copy_from_user_inatomic(buf, (void __user *)ip, MAX_INSN_SIZE);  	return MAX_INSN_SIZE - not_copied;  }  | 
