Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions include/unicorn/x86.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ typedef void (*uc_cb_insn_syscall_t)(struct uc_struct *uc, void *user_data);
// indicates cpuid instruction will still be executed.
typedef int (*uc_cb_insn_cpuid_t)(struct uc_struct *uc, void *user_data);

// Callback function for modifying the random value generated by RDRAND.
// @value: generated random value, which may be modified by the callback.
// @success: generated random value validity, which may be modified by the callback.
// @user_data: user data passed to tracing APIs.
typedef void (*uc_cb_insn_rdrand_t)(struct uc_struct *uc, uint64_t *value,
bool *success, void *user_data);

//> X86 registers
typedef enum uc_x86_reg {
UC_X86_REG_INVALID = 0,
Expand Down
34 changes: 32 additions & 2 deletions qemu/target/i386/int_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "qemu/host-utils.h"
#include "exec/helper-proto.h"
#include "qemu/guest-random.h"
#include "uc_priv.h"

//#define DEBUG_MULDIV

Expand Down Expand Up @@ -487,9 +488,38 @@ void helper_cr4_testbit(CPUX86State *env, uint32_t bit)

target_ulong HELPER(rdrand)(CPUX86State *env)
{
target_ulong ret;
target_ulong ret = 0;
bool success;
uc_engine *uc = env->uc;
struct hook *hook;
bool synced = false;

success = qemu_guest_getrandom(&ret, sizeof(ret)) == 0;

// Unicorn: call registered RDRAND hooks.
HOOK_FOREACH_VAR_DECLARE;
HOOK_FOREACH(uc, hook, UC_HOOK_INSN) {
if (hook->to_delete)
continue;
if (!HOOK_BOUND_CHECK(hook, env->eip))
continue;

if (hook->insn == UC_X86_INS_RDRAND) {
uintptr_t pc = GETPC();
if (!synced && !uc->skip_sync_pc_on_exit && pc) {
cpu_restore_state(uc->cpu, pc, false);
synced = true;
}
JIT_CALLBACK_GUARD(
((uc_cb_insn_rdrand_t)hook->callback)(uc, (uint64_t *)&ret,
&success, hook->user_data));
}

if (uc->stop_request)
break;
}

if (qemu_guest_getrandom(&ret, sizeof(ret)) < 0) {
if (!success) {
// qemu_log_mask(LOG_UNIMP, "rdrand: Crypto failure: %s",
// error_get_pretty(err));
// error_free(err);
Expand Down
5 changes: 3 additions & 2 deletions qemu/target/i386/unicorn.c
Original file line number Diff line number Diff line change
Expand Up @@ -2041,11 +2041,12 @@ static bool x86_stop_interrupt(struct uc_struct *uc, int intno)

static bool x86_insn_hook_validate(uint32_t insn_enum)
{
// for x86 we can only hook IN, OUT, SYSCALL, SYSENTER, CPUID, RDTSC, RDTSCP, RDMSR and WRMSR
// for x86 we can only hook IN, OUT, SYSCALL, SYSENTER, CPUID, RDTSC, RDTSCP, RDRAND, RDMSR and WRMSR
if (insn_enum != UC_X86_INS_IN && insn_enum != UC_X86_INS_OUT &&
insn_enum != UC_X86_INS_SYSCALL && insn_enum != UC_X86_INS_SYSENTER &&
insn_enum != UC_X86_INS_CPUID && insn_enum != UC_X86_INS_RDTSC &&
insn_enum != UC_X86_INS_RDTSCP && insn_enum != UC_X86_INS_RDMSR &&
insn_enum != UC_X86_INS_RDTSCP && insn_enum != UC_X86_INS_RDRAND &&
insn_enum != UC_X86_INS_RDMSR &&
insn_enum != UC_X86_INS_WRMSR) {
return false;
}
Expand Down
70 changes: 70 additions & 0 deletions tests/unit/test_x86.c
Original file line number Diff line number Diff line change
Expand Up @@ -2377,6 +2377,74 @@ static void test_x86_hook_insn_rdtscp(void)
OK(uc_close(uc));
}

static void test_x86_hook_insn_rdrand_cb(uc_engine *uc, uint64_t *value,
bool *success, void *user_data)
{
(*(int *)user_data)++;
*value = 0x0123456789ABCDEF;
}

static void test_x86_hook_insn_rdrand(void)
{
char code[] = "\x48\x0F\xC7\xF3"; // RDRAND RBX
uc_engine *uc;
uint64_t rbx = 0;
uint64_t eflags = 0;
int called = 0;
uc_hook hook;

OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
OK(uc_ctl_set_cpu_model(uc, UC_CPU_X86_HASWELL));
OK(uc_mem_map(uc, code_start, code_len, UC_PROT_ALL));
OK(uc_mem_write(uc, code_start, code, sizeof(code) - 1));
OK(uc_hook_add(uc, &hook, UC_HOOK_INSN, test_x86_hook_insn_rdrand_cb, &called,
1, 0, UC_X86_INS_RDRAND));
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
OK(uc_hook_del(uc, hook));

OK(uc_reg_read(uc, UC_X86_REG_RBX, &rbx));
OK(uc_reg_read(uc, UC_X86_REG_EFLAGS, &eflags)); // check carry flag is set
TEST_CHECK(called == 1);
TEST_CHECK(rbx == 0x0123456789ABCDEF);
TEST_CHECK((eflags & 1) != 0);
OK(uc_close(uc));
}

static void test_x86_hook_insn_rdrand_failure_cb(uc_engine *uc,
uint64_t *value, bool *success,
void *user_data)
{
(*(int *)user_data)++;
*success = false;
}

static void test_x86_hook_insn_rdrand_failure(void)
{
char code[] = "\x48\x0F\xC7\xF3"; // RDRAND RBX
uc_engine *uc;
uint64_t rbx = 1;
uint64_t eflags = 0;
int called = 0;
uc_hook hook;

OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
OK(uc_ctl_set_cpu_model(uc, UC_CPU_X86_HASWELL));
OK(uc_mem_map(uc, code_start, code_len, UC_PROT_ALL));
OK(uc_mem_write(uc, code_start, code, sizeof(code) - 1));
OK(uc_hook_add(uc, &hook, UC_HOOK_INSN,
test_x86_hook_insn_rdrand_failure_cb, &called, 1, 0,
UC_X86_INS_RDRAND));
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
OK(uc_hook_del(uc, hook));

OK(uc_reg_read(uc, UC_X86_REG_RBX, &rbx));
OK(uc_reg_read(uc, UC_X86_REG_EFLAGS, &eflags));
TEST_CHECK(called == 1);
TEST_CHECK(rbx == 0);
TEST_CHECK((eflags & 1) == 0);
OK(uc_close(uc));
}

static int test_x86_hook_insn_wrmsr_cb(uc_engine *uc, void *user_data)
{
*(int *)user_data = 1;
Expand Down Expand Up @@ -2802,6 +2870,8 @@ TEST_LIST = {
{"test_x86_ro_segfault", test_x86_ro_segfault},
{"test_x86_hook_insn_rdtsc", test_x86_hook_insn_rdtsc},
{"test_x86_hook_insn_rdtscp", test_x86_hook_insn_rdtscp},
{"test_x86_hook_insn_rdrand", test_x86_hook_insn_rdrand},
{"test_x86_hook_insn_rdrand_failure", test_x86_hook_insn_rdrand_failure},
{"test_x86_hook_insn_wrmsr", test_x86_hook_insn_wrmsr},
{"test_x86_hook_insn_rdmsr", test_x86_hook_insn_rdmsr},
{"test_x86_dr7", test_x86_dr7},
Expand Down
Loading