Skip to content

Commit

Permalink
Update to Unicorn v2.0.0 (#2564)
Browse files Browse the repository at this point in the history
* Retry coveralls upload

Fix flaky gateway timeout

* Update to Unicorn v2.0.0

Fix CPUID errors with latest glibc

Fix new syscall in glibc
  • Loading branch information
ekilmer committed Jul 21, 2022
1 parent 1b8c1eb commit 8470ed4
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 36 deletions.
71 changes: 40 additions & 31 deletions manticore/native/cpu/x86.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging

from functools import wraps
from typing import Tuple

import capstone as cs

Expand Down Expand Up @@ -987,29 +988,21 @@ def _calculate_logic_flags(self, size, res):

#####################################################
# Instructions
@instruction
def CPUID(cpu):
@staticmethod
def CPUID_helper(PC: int, EAX: int, ECX: int) -> Tuple[int, int, int, int]:
"""
CPUID instruction.
The ID flag (bit 21) in the EFLAGS register indicates support for the
CPUID instruction. If a software procedure can set and clear this
flag, the processor executing the procedure supports the CPUID
instruction. This instruction operates the same in non-64-bit modes and
64-bit mode. CPUID returns processor identification and feature
information in the EAX, EBX, ECX, and EDX registers.
The instruction's output is dependent on the contents of the EAX
register upon execution.
:param cpu: current CPU.
Takes values in eax and ecx to perform logic on what to return to (EAX,
EBX, ECX, EDX), in that order.
"""
# FIXME Choose conservative values and consider returning some default when eax not here
conf = {
# Taken from comparison against Unicorn@v1.0.2
0x0: (0x00000004, 0x68747541, 0x444D4163, 0x69746E65),
# Taken from comparison against Unicorn@v1.0.2
0x1: (0x663, 0x800, 0x2182200, 0x7088100),
# Taken from comparison against local Intel machine with `cpuid` tool
0x0: (0x00000004, 0x756E6547, 0x6C65746E, 0x49656E69),
# Determined through initial Unicorn comparison and then fixed to
# support latest glibc 2.35
# * RDX Required bit 23 for MMX instructions on new glibc
# * RDX Required bit 0 for onboard x87 FPU
0x1: (0x00000663, 0x00000800, 0x02182200, 0x07888101),
# TODO: Check against Unicorn
0x2: (0x76035A01, 0x00F0B5FF, 0x00000000, 0x00C10000),
0x4: {
Expand All @@ -1034,21 +1027,37 @@ def CPUID(cpu):
0x80000000: (0x80000000, 0x00000000, 0x00000000, 0x00000000),
}

if cpu.EAX not in conf:
logger.warning("CPUID with EAX=%x not implemented @ %x", cpu.EAX, cpu.PC)
cpu.EAX, cpu.EBX, cpu.ECX, cpu.EDX = 0, 0, 0, 0
return
if EAX not in conf:
logger.warning("CPUID with EAX=%x not implemented @ %x", EAX, PC)
return (0, 0, 0, 0)

if isinstance(conf[cpu.EAX], tuple):
cpu.EAX, cpu.EBX, cpu.ECX, cpu.EDX = conf[cpu.EAX]
return
if isinstance(conf[EAX], tuple):
return conf[EAX] # type: ignore

if cpu.ECX not in conf[cpu.EAX]:
logger.warning("CPUID with EAX=%x ECX=%x not implemented", cpu.EAX, cpu.ECX)
cpu.EAX, cpu.EBX, cpu.ECX, cpu.EDX = 0, 0, 0, 0
return
if ECX not in conf[EAX]:
logger.warning("CPUID with EAX=%x ECX=%x not implemented @ %x", EAX, ECX, PC)
return (0, 0, 0, 0)

cpu.EAX, cpu.EBX, cpu.ECX, cpu.EDX = conf[cpu.EAX][cpu.ECX]
return conf[EAX][ECX] # type: ignore

@instruction
def CPUID(cpu):
"""
CPUID instruction.
The ID flag (bit 21) in the EFLAGS register indicates support for the
CPUID instruction. If a software procedure can set and clear this
flag, the processor executing the procedure supports the CPUID
instruction. This instruction operates the same in non-64-bit modes and
64-bit mode. CPUID returns processor identification and feature
information in the EAX, EBX, ECX, and EDX registers.
The instruction's output is dependent on the contents of the EAX
register upon execution.
:param cpu: current CPU.
"""
cpu.EAX, cpu.EBX, cpu.ECX, cpu.EDX = X86Cpu.CPUID_helper(cpu.PC, cpu.EAX, cpu.ECX)

@instruction
def XGETBV(cpu):
Expand Down
2 changes: 1 addition & 1 deletion manticore/platforms/linux.py
Original file line number Diff line number Diff line change
Expand Up @@ -3053,7 +3053,7 @@ def syscall(self):
implementation = partial(self._handle_unimplemented_syscall, implementation)
else:
implementation = getattr(self.stubs, name)
except (AttributeError, KeyError):
except (TypeError, AttributeError, KeyError):
if name is not None:
raise SyscallNotImplemented(index, name)
else:
Expand Down
5 changes: 5 additions & 0 deletions manticore/platforms/linux_syscall_stubs.py
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,11 @@ def sys_restart_syscall(self) -> int:
"""AUTOGENERATED UNIMPLEMENTED STUB"""
return self.complicated_both(219)

@unimplemented
def sys_rseq(self, rseq, rseq_len, flags, sig) -> int:
"""AUTOGENERATED UNIMPLEMENTED STUB"""
return self.simple_returns()

@unimplemented
def sys_rt_sigpending(self, set, sigsetsize) -> int:
"""AUTOGENERATED UNIMPLEMENTED STUB"""
Expand Down
1 change: 1 addition & 0 deletions manticore/platforms/linux_syscalls.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,7 @@
330: "sys_pkey_alloc",
331: "sys_pkey_free",
332: "sys_statx",
334: "sys_rseq",
512: "compat_sys_rt_sigaction",
513: "sys32_x32_rt_sigreturn",
514: "compat_sys_ioctl",
Expand Down
26 changes: 25 additions & 1 deletion manticore/utils/emulate.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def __init__(self, cpu):
self._mem_delta = {}
self.flag_registers = {"CF", "PF", "AF", "ZF", "SF", "IF", "DF", "OF"}
# Registers to ignore when translating manticore context to unicorn
self.ignore_registers = {"FIP", "FOP", "FDS", "FCS", "FDP", "MXCSR_MASK"}
self.ignore_registers = {"MXCSR_MASK"}
self.write_backs_disabled = False
self._stop_at = None
# Holds key of range (addr, addr + size) and value of permissions
Expand Down Expand Up @@ -117,6 +117,7 @@ def __init__(self, cpu):
self._emu.hook_add(UC_HOOK_MEM_WRITE, self._hook_write_mem)
self._emu.hook_add(UC_HOOK_INTR, self._interrupt)
self._emu.hook_add(UC_HOOK_INSN, self._hook_syscall, arg1=UC_X86_INS_SYSCALL)
self._emu.hook_add(UC_HOOK_INSN, self._hook_cpuid, arg1=UC_X86_INS_CPUID)

self.registers = set(self._cpu.canonical_registers)
# The last 8 canonical registers of x86 are individual flags; replace with the eflags
Expand Down Expand Up @@ -284,6 +285,29 @@ def _hook_syscall(self, uc, data):
self._to_raise = Syscall()
uc.emu_stop()

def _hook_cpuid(self, uc, data):
"""
Unicorn hook that uses Manticore's semantics for cpuid
"""
logger.debug(f"Hooking CPUID instruction {uc.reg_read(self._to_unicorn_id('RIP')):#x}")
if self._cpu.mode == CS_MODE_32:
pc = uc.reg_read(UC_X86_REG_EIP)
elif self._cpu.mode == CS_MODE_64:
pc = uc.reg_read(UC_X86_REG_RIP)
eax = uc.reg_read(UC_X86_REG_EAX)
ecx = uc.reg_read(UC_X86_REG_ECX)

from ..native.cpu.x86 import X86Cpu

eax, ebx, ecx, edx = X86Cpu.CPUID_helper(pc, eax, ecx)

uc.reg_write(UC_X86_REG_EAX, eax)
uc.reg_write(UC_X86_REG_EBX, ebx)
uc.reg_write(UC_X86_REG_ECX, ecx)
uc.reg_write(UC_X86_REG_EDX, edx)

return 1

def _hook_write_mem(self, uc, _access, address: int, size: int, value: int, _data) -> bool:
"""
Captures memory written by Unicorn
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def rtd_dependent_deps():
native_deps = [
"capstone==5.0.0rc2",
"pyelftools",
"unicorn==1.0.2",
"unicorn~=2.0",
]

lint_deps = ["black~=22.0", "mypy==0.790"]
Expand Down
2 changes: 0 additions & 2 deletions tests/native/test_unicorn_concrete.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,8 @@ def setUp(self):
)

self.concrete_instance.register_plugin(ConcretePlugin())
"""
self.concrete_instance.register_plugin(RegisterCapturePlugin())
self.m.register_plugin(RegisterCapturePlugin())
"""

def test_register_comparison(self):
self.m.run()
Expand Down

0 comments on commit 8470ed4

Please sign in to comment.