Skip to content

Commit cb4d01c

Browse files
committed
feat: add x86 RDRAND instruction hook
Signed-off-by: Jvle <keke.oerv@isrc.iscas.ac.cn>
1 parent e04c16c commit cb4d01c

4 files changed

Lines changed: 112 additions & 4 deletions

File tree

include/unicorn/x86.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ typedef void (*uc_cb_insn_syscall_t)(struct uc_struct *uc, void *user_data);
8585
// indicates cpuid instruction will still be executed.
8686
typedef int (*uc_cb_insn_cpuid_t)(struct uc_struct *uc, void *user_data);
8787

88+
// Callback function for modifying the random value generated by RDRAND.
89+
// @value: generated random value, which may be modified by the callback.
90+
// @success: generated random value validity, which may be modified by the callback.
91+
// @user_data: user data passed to tracing APIs.
92+
typedef void (*uc_cb_insn_rdrand_t)(struct uc_struct *uc, uint64_t *value,
93+
bool *success, void *user_data);
94+
8895
//> X86 registers
8996
typedef enum uc_x86_reg {
9097
UC_X86_REG_INVALID = 0,

qemu/target/i386/int_helper.c

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "qemu/host-utils.h"
2424
#include "exec/helper-proto.h"
2525
#include "qemu/guest-random.h"
26+
#include "uc_priv.h"
2627

2728
//#define DEBUG_MULDIV
2829

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

488489
target_ulong HELPER(rdrand)(CPUX86State *env)
489490
{
490-
target_ulong ret;
491+
target_ulong ret = 0;
492+
bool success;
493+
uc_engine *uc = env->uc;
494+
struct hook *hook;
495+
bool synced = false;
496+
497+
success = qemu_guest_getrandom(&ret, sizeof(ret)) == 0;
498+
499+
// Unicorn: call registered RDRAND hooks.
500+
HOOK_FOREACH_VAR_DECLARE;
501+
HOOK_FOREACH(uc, hook, UC_HOOK_INSN) {
502+
if (hook->to_delete)
503+
continue;
504+
if (!HOOK_BOUND_CHECK(hook, env->eip))
505+
continue;
506+
507+
if (hook->insn == UC_X86_INS_RDRAND) {
508+
uintptr_t pc = GETPC();
509+
if (!synced && !uc->skip_sync_pc_on_exit && pc) {
510+
cpu_restore_state(uc->cpu, pc, false);
511+
synced = true;
512+
}
513+
JIT_CALLBACK_GUARD(
514+
((uc_cb_insn_rdrand_t)hook->callback)(uc, (uint64_t *)&ret,
515+
&success, hook->user_data));
516+
}
517+
518+
if (uc->stop_request)
519+
break;
520+
}
491521

492-
if (qemu_guest_getrandom(&ret, sizeof(ret)) < 0) {
522+
if (!success) {
493523
// qemu_log_mask(LOG_UNIMP, "rdrand: Crypto failure: %s",
494524
// error_get_pretty(err));
495525
// error_free(err);

qemu/target/i386/unicorn.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2041,11 +2041,12 @@ static bool x86_stop_interrupt(struct uc_struct *uc, int intno)
20412041

20422042
static bool x86_insn_hook_validate(uint32_t insn_enum)
20432043
{
2044-
// for x86 we can only hook IN, OUT, SYSCALL, SYSENTER, CPUID, RDTSC, RDTSCP, RDMSR and WRMSR
2044+
// for x86 we can only hook IN, OUT, SYSCALL, SYSENTER, CPUID, RDTSC, RDTSCP, RDRAND, RDMSR and WRMSR
20452045
if (insn_enum != UC_X86_INS_IN && insn_enum != UC_X86_INS_OUT &&
20462046
insn_enum != UC_X86_INS_SYSCALL && insn_enum != UC_X86_INS_SYSENTER &&
20472047
insn_enum != UC_X86_INS_CPUID && insn_enum != UC_X86_INS_RDTSC &&
2048-
insn_enum != UC_X86_INS_RDTSCP && insn_enum != UC_X86_INS_RDMSR &&
2048+
insn_enum != UC_X86_INS_RDTSCP && insn_enum != UC_X86_INS_RDRAND &&
2049+
insn_enum != UC_X86_INS_RDMSR &&
20492050
insn_enum != UC_X86_INS_WRMSR) {
20502051
return false;
20512052
}

tests/unit/test_x86.c

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2377,6 +2377,74 @@ static void test_x86_hook_insn_rdtscp(void)
23772377
OK(uc_close(uc));
23782378
}
23792379

2380+
static void test_x86_hook_insn_rdrand_cb(uc_engine *uc, uint64_t *value,
2381+
bool *success, void *user_data)
2382+
{
2383+
(*(int *)user_data)++;
2384+
*value = 0x0123456789ABCDEF;
2385+
}
2386+
2387+
static void test_x86_hook_insn_rdrand(void)
2388+
{
2389+
char code[] = "\x48\x0F\xC7\xF3"; // RDRAND RBX
2390+
uc_engine *uc;
2391+
uint64_t rbx = 0;
2392+
uint64_t eflags = 0;
2393+
int called = 0;
2394+
uc_hook hook;
2395+
2396+
OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
2397+
OK(uc_ctl_set_cpu_model(uc, UC_CPU_X86_HASWELL));
2398+
OK(uc_mem_map(uc, code_start, code_len, UC_PROT_ALL));
2399+
OK(uc_mem_write(uc, code_start, code, sizeof(code) - 1));
2400+
OK(uc_hook_add(uc, &hook, UC_HOOK_INSN, test_x86_hook_insn_rdrand_cb, &called,
2401+
1, 0, UC_X86_INS_RDRAND));
2402+
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
2403+
OK(uc_hook_del(uc, hook));
2404+
2405+
OK(uc_reg_read(uc, UC_X86_REG_RBX, &rbx));
2406+
OK(uc_reg_read(uc, UC_X86_REG_EFLAGS, &eflags)); // check carry flag is set
2407+
TEST_CHECK(called == 1);
2408+
TEST_CHECK(rbx == 0x0123456789ABCDEF);
2409+
TEST_CHECK((eflags & 1) != 0);
2410+
OK(uc_close(uc));
2411+
}
2412+
2413+
static void test_x86_hook_insn_rdrand_failure_cb(uc_engine *uc,
2414+
uint64_t *value, bool *success,
2415+
void *user_data)
2416+
{
2417+
(*(int *)user_data)++;
2418+
*success = false;
2419+
}
2420+
2421+
static void test_x86_hook_insn_rdrand_failure(void)
2422+
{
2423+
char code[] = "\x48\x0F\xC7\xF3"; // RDRAND RBX
2424+
uc_engine *uc;
2425+
uint64_t rbx = 1;
2426+
uint64_t eflags = 0;
2427+
int called = 0;
2428+
uc_hook hook;
2429+
2430+
OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
2431+
OK(uc_ctl_set_cpu_model(uc, UC_CPU_X86_HASWELL));
2432+
OK(uc_mem_map(uc, code_start, code_len, UC_PROT_ALL));
2433+
OK(uc_mem_write(uc, code_start, code, sizeof(code) - 1));
2434+
OK(uc_hook_add(uc, &hook, UC_HOOK_INSN,
2435+
test_x86_hook_insn_rdrand_failure_cb, &called, 1, 0,
2436+
UC_X86_INS_RDRAND));
2437+
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
2438+
OK(uc_hook_del(uc, hook));
2439+
2440+
OK(uc_reg_read(uc, UC_X86_REG_RBX, &rbx));
2441+
OK(uc_reg_read(uc, UC_X86_REG_EFLAGS, &eflags));
2442+
TEST_CHECK(called == 1);
2443+
TEST_CHECK(rbx == 0);
2444+
TEST_CHECK((eflags & 1) == 0);
2445+
OK(uc_close(uc));
2446+
}
2447+
23802448
static int test_x86_hook_insn_wrmsr_cb(uc_engine *uc, void *user_data)
23812449
{
23822450
*(int *)user_data = 1;
@@ -2802,6 +2870,8 @@ TEST_LIST = {
28022870
{"test_x86_ro_segfault", test_x86_ro_segfault},
28032871
{"test_x86_hook_insn_rdtsc", test_x86_hook_insn_rdtsc},
28042872
{"test_x86_hook_insn_rdtscp", test_x86_hook_insn_rdtscp},
2873+
{"test_x86_hook_insn_rdrand", test_x86_hook_insn_rdrand},
2874+
{"test_x86_hook_insn_rdrand_failure", test_x86_hook_insn_rdrand_failure},
28052875
{"test_x86_hook_insn_wrmsr", test_x86_hook_insn_wrmsr},
28062876
{"test_x86_hook_insn_rdmsr", test_x86_hook_insn_rdmsr},
28072877
{"test_x86_dr7", test_x86_dr7},

0 commit comments

Comments
 (0)