Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make trapped_instruction_at() explicitly x86-specific #3847

Merged
merged 1 commit into from
Oct 16, 2024
Merged
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
6 changes: 3 additions & 3 deletions src/DiversionSession.cc
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,9 @@ DiversionSession::DiversionResult DiversionSession::diversion_step(
result.break_status.signal = unique_ptr<siginfo_t>(new siginfo_t(t->get_siginfo()));
result.break_status.signal->si_signo = t->stop_sig();
} else if (t->stop_sig() == SIGSEGV) {
auto trapped_instruction = trapped_instruction_at(t, t->ip());
if (trapped_instruction == TrappedInstruction::RDTSC) {
size_t len = trapped_instruction_len(trapped_instruction);
auto special_instruction = special_instruction_at(t, t->ip());
if (special_instruction.opcode == SpecialInstOpcode::X86_RDTSC) {
size_t len = special_instruction_len(special_instruction.opcode);
uint64_t rdtsc_value = next_rdtsc_value();
LOG(debug) << "Faking RDTSC instruction with value " << rdtsc_value;
Registers r = t->regs();
Expand Down
4 changes: 2 additions & 2 deletions src/ReplaySession.cc
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ bool ReplaySession::handle_unrecorded_cpuid_fault(
ReplayTask* t, const StepConstraints& constraints) {
if (t->stop_sig() != SIGSEGV || !has_cpuid_faulting() ||
trace_in.uses_cpuid_faulting() ||
trapped_instruction_at(t, t->ip()) != TrappedInstruction::CPUID) {
special_instruction_at(t, t->ip()).opcode != SpecialInstOpcode::X86_CPUID) {
return false;
}
// OK, this is a case where we did not record using CPUID faulting but we are
Expand All @@ -522,7 +522,7 @@ bool ReplaySession::handle_unrecorded_cpuid_fault(
<< " CX=" << HEX(r.cx()) << "; defaulting to 0/0/0/0";
r.set_cpuid_output(0, 0, 0, 0);
}
r.set_ip(r.ip() + trapped_instruction_len(TrappedInstruction::CPUID));
r.set_ip(r.ip() + special_instruction_len(SpecialInstOpcode::X86_CPUID));
t->set_regs(r);
// Clear SIGSEGV status since we're handling it
t->set_status(constraints.is_singlestep() ? WaitStatus::for_stop_sig(SIGTRAP)
Expand Down
28 changes: 14 additions & 14 deletions src/Task.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1366,18 +1366,18 @@ TrapReasons Task::compute_trap_reasons() {
// step over those instructions so we need to detect that here.
reasons.singlestep = true;
} else {
TrappedInstruction ti =
trapped_instruction_at(this, address_of_last_execution_resume);
if (ti == TrappedInstruction::CPUID &&
SpecialInst si =
special_instruction_at(this, address_of_last_execution_resume);
if (si.opcode == SpecialInstOpcode::X86_CPUID &&
ip() == address_of_last_execution_resume +
trapped_instruction_len(TrappedInstruction::CPUID)) {
special_instruction_len(SpecialInstOpcode::X86_CPUID)) {
// Likewise we emulate CPUID instructions and must forcibly detect that
// here.
reasons.singlestep = true;
// This also takes care of the did_set_breakpoint_after_cpuid workaround case
} else if (ti == TrappedInstruction::INT3 &&
} else if (si.opcode == SpecialInstOpcode::X86_INT3 &&
ip() == address_of_last_execution_resume +
trapped_instruction_len(TrappedInstruction::INT3)) {
special_instruction_len(SpecialInstOpcode::X86_INT3)) {
// INT3 instructions should also be turned into a singlestep here.
reasons.singlestep = true;
}
Expand Down Expand Up @@ -1562,12 +1562,12 @@ bool Task::resume_execution(ResumeRequest how, WaitRequest wait_how,
if (is_singlestep_resume(how)) {
work_around_KNL_string_singlestep_bug();
if (is_x86ish(arch())) {
singlestepping_instruction = trapped_instruction_at(this, ip());
if (singlestepping_instruction == TrappedInstruction::CPUID) {
singlestepping_instruction = special_instruction_at(this, ip());
if (singlestepping_instruction.opcode == SpecialInstOpcode::X86_CPUID) {
// In KVM virtual machines (and maybe others), singlestepping over CPUID
// executes the following instruction as well. Work around that.
did_set_breakpoint_after_cpuid =
vm()->add_breakpoint(ip() + trapped_instruction_len(singlestepping_instruction), BKPT_INTERNAL);
vm()->add_breakpoint(ip() + special_instruction_len(singlestepping_instruction.opcode), BKPT_INTERNAL);
}
} else if (arch() == aarch64 && is_singlestep_resume(how_last_execution_resumed)) {
// On aarch64, if the last execution was any sort of single step, then
Expand Down Expand Up @@ -2429,7 +2429,7 @@ bool Task::did_waitpid(WaitStatus status) {

if (did_set_breakpoint_after_cpuid) {
remote_code_ptr bkpt_addr =
address_of_last_execution_resume + trapped_instruction_len(singlestepping_instruction);
address_of_last_execution_resume + special_instruction_len(singlestepping_instruction.opcode);
if (ip().undo_executed_bkpt(arch()) == bkpt_addr) {
Registers r = regs();
r.set_ip(bkpt_addr);
Expand All @@ -2438,18 +2438,18 @@ bool Task::did_waitpid(WaitStatus status) {
vm()->remove_breakpoint(bkpt_addr, BKPT_INTERNAL);
did_set_breakpoint_after_cpuid = false;
}
if ((singlestepping_instruction == TrappedInstruction::PUSHF ||
singlestepping_instruction == TrappedInstruction::PUSHF16) &&
if ((singlestepping_instruction.opcode == SpecialInstOpcode::X86_PUSHF ||
singlestepping_instruction.opcode == SpecialInstOpcode::X86_PUSHF16) &&
ip() == address_of_last_execution_resume +
trapped_instruction_len(singlestepping_instruction)) {
special_instruction_len(singlestepping_instruction.opcode)) {
// We singlestepped through a pushf. Clear TF bit on stack.
auto sp = regs().sp().cast<uint16_t>();
// If this address is invalid then we should have segfaulted instead of
// retiring the instruction!
uint16_t val = read_mem(sp);
write_mem(sp, (uint16_t)(val & ~X86_TF_FLAG));
}
singlestepping_instruction = TrappedInstruction::NONE;
singlestepping_instruction.opcode = SpecialInstOpcode::NONE;

// We might have singlestepped at the resumption address and just exited
// the kernel without executing the breakpoint at that address.
Expand Down
2 changes: 1 addition & 1 deletion src/Task.h
Original file line number Diff line number Diff line change
Expand Up @@ -1317,7 +1317,7 @@ class Task {
// Task.cc
uint64_t last_resume_orig_cx;
// The instruction type we're singlestepping through.
TrappedInstruction singlestepping_instruction;
SpecialInst singlestepping_instruction;
// True if we set a breakpoint after a singlestepped CPUID instruction.
// We need this in addition to `singlestepping_instruction` because that
// might be CPUID but we failed to set the breakpoint.
Expand Down
20 changes: 10 additions & 10 deletions src/record_signal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,15 @@ static void restore_signal_state(RecordTask* t, int sig,
static bool try_handle_trapped_instruction(RecordTask* t, siginfo_t* si) {
ASSERT(t, si->si_signo == SIGSEGV);

auto trapped_instruction = trapped_instruction_at(t, t->ip());
switch (trapped_instruction) {
case TrappedInstruction::RDTSC:
case TrappedInstruction::RDTSCP:
auto special_instruction = special_instruction_at(t, t->ip());
switch (special_instruction.opcode) {
case SpecialInstOpcode::X86_RDTSC:
case SpecialInstOpcode::X86_RDTSCP:
if (t->tsc_mode == PR_TSC_SIGSEGV) {
return false;
}
break;
case TrappedInstruction::CPUID:
case SpecialInstOpcode::X86_CPUID:
if (t->cpuid_mode == 0) {
return false;
}
Expand All @@ -95,13 +95,13 @@ static bool try_handle_trapped_instruction(RecordTask* t, siginfo_t* si) {
return false;
}

size_t len = trapped_instruction_len(trapped_instruction);
size_t len = special_instruction_len(special_instruction.opcode);
ASSERT(t, len > 0);

Registers r = t->regs();
if (trapped_instruction == TrappedInstruction::RDTSC ||
trapped_instruction == TrappedInstruction::RDTSCP) {
if (trapped_instruction == TrappedInstruction::RDTSC &&
if (special_instruction.opcode == SpecialInstOpcode::X86_RDTSC ||
special_instruction.opcode == SpecialInstOpcode::X86_RDTSCP) {
if (special_instruction.opcode == SpecialInstOpcode::X86_RDTSC &&
t->vm()->monkeypatcher().try_patch_trapping_instruction(t, len, true)) {
Event ev = Event::patch_syscall();
ev.PatchSyscall().patch_trapping_instruction = true;
Expand All @@ -114,7 +114,7 @@ static bool try_handle_trapped_instruction(RecordTask* t, siginfo_t* si) {
r.set_rdtsc_output(current_time);

LOG(debug) << " trapped for rdtsc: returning " << current_time;
} else if (trapped_instruction == TrappedInstruction::CPUID) {
} else if (special_instruction.opcode == SpecialInstOpcode::X86_CPUID) {
auto eax = r.syscallno();
auto ecx = r.cx();
auto cpuid_data = cpuid(eax, ecx);
Expand Down
80 changes: 41 additions & 39 deletions src/util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1947,53 +1947,55 @@ static const uint8_t pushf_insn[] = { 0x9c };
static const uint8_t pushf16_insn[] = { 0x66, 0x9c };

// XXX this probably needs to be extended to decode ignored prefixes
TrappedInstruction trapped_instruction_at(Task* t, remote_code_ptr ip) {
uint8_t insn[sizeof(rdtscp_insn)];
ssize_t ret =
t->read_bytes_fallible(ip.to_data_ptr<uint8_t>(), sizeof(insn), insn);
if (ret < 0) {
return TrappedInstruction::NONE;
}
size_t len = ret;
if (len >= sizeof(rdtsc_insn) &&
!memcmp(insn, rdtsc_insn, sizeof(rdtsc_insn))) {
return TrappedInstruction::RDTSC;
}
if (len >= sizeof(rdtscp_insn) &&
!memcmp(insn, rdtscp_insn, sizeof(rdtscp_insn))) {
return TrappedInstruction::RDTSCP;
}
if (len >= sizeof(cpuid_insn) &&
!memcmp(insn, cpuid_insn, sizeof(cpuid_insn))) {
return TrappedInstruction::CPUID;
}
if (len >= sizeof(int3_insn) &&
!memcmp(insn, int3_insn, sizeof(int3_insn))) {
return TrappedInstruction::INT3;
}
if (len >= sizeof(pushf_insn) &&
!memcmp(insn, pushf_insn, sizeof(pushf_insn))) {
return TrappedInstruction::PUSHF;
}
if (len >= sizeof(pushf16_insn) &&
!memcmp(insn, pushf16_insn, sizeof(pushf16_insn))) {
return TrappedInstruction::PUSHF16;
SpecialInst special_instruction_at(Task* t, remote_code_ptr ip) {
if (is_x86ish(t->arch())) {
uint8_t insn[sizeof(rdtscp_insn)];
ssize_t ret =
t->read_bytes_fallible(ip.to_data_ptr<uint8_t>(), sizeof(insn), insn);
if (ret < 0) {
return {SpecialInstOpcode::NONE};
}
size_t len = ret;
if (len >= sizeof(rdtsc_insn) &&
!memcmp(insn, rdtsc_insn, sizeof(rdtsc_insn))) {
return {SpecialInstOpcode::X86_RDTSC};
}
if (len >= sizeof(rdtscp_insn) &&
!memcmp(insn, rdtscp_insn, sizeof(rdtscp_insn))) {
return {SpecialInstOpcode::X86_RDTSCP};
}
if (len >= sizeof(cpuid_insn) &&
!memcmp(insn, cpuid_insn, sizeof(cpuid_insn))) {
return {SpecialInstOpcode::X86_CPUID};
}
if (len >= sizeof(int3_insn) &&
!memcmp(insn, int3_insn, sizeof(int3_insn))) {
return {SpecialInstOpcode::X86_INT3};
}
if (len >= sizeof(pushf_insn) &&
!memcmp(insn, pushf_insn, sizeof(pushf_insn))) {
return {SpecialInstOpcode::X86_PUSHF};
}
if (len >= sizeof(pushf16_insn) &&
!memcmp(insn, pushf16_insn, sizeof(pushf16_insn))) {
return {SpecialInstOpcode::X86_PUSHF16};
}
}
return TrappedInstruction::NONE;
return {SpecialInstOpcode::NONE};
}

size_t trapped_instruction_len(TrappedInstruction insn) {
if (insn == TrappedInstruction::RDTSC) {
size_t special_instruction_len(SpecialInstOpcode insn) {
if (insn == SpecialInstOpcode::X86_RDTSC) {
return sizeof(rdtsc_insn);
} else if (insn == TrappedInstruction::RDTSCP) {
} else if (insn == SpecialInstOpcode::X86_RDTSCP) {
return sizeof(rdtscp_insn);
} else if (insn == TrappedInstruction::CPUID) {
} else if (insn == SpecialInstOpcode::X86_CPUID) {
return sizeof(cpuid_insn);
} else if (insn == TrappedInstruction::INT3) {
} else if (insn == SpecialInstOpcode::X86_INT3) {
return sizeof(int3_insn);
} else if (insn == TrappedInstruction::PUSHF) {
} else if (insn == SpecialInstOpcode::X86_PUSHF) {
return sizeof(pushf_insn);
} else if (insn == TrappedInstruction::PUSHF16) {
} else if (insn == SpecialInstOpcode::X86_PUSHF16) {
return sizeof(pushf16_insn);
} else {
return 0;
Expand Down
24 changes: 14 additions & 10 deletions src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -486,23 +486,27 @@ std::vector<std::string> current_env();
*/
int get_num_cpus();

enum class TrappedInstruction {
NONE = 0,
RDTSC = 1,
RDTSCP = 2,
CPUID = 3,
INT3 = 4,
PUSHF = 5,
PUSHF16 = 6,
enum class SpecialInstOpcode {
NONE,
X86_RDTSC,
X86_RDTSCP,
X86_CPUID,
X86_INT3,
X86_PUSHF,
X86_PUSHF16,
};

struct SpecialInst {
SpecialInstOpcode opcode;
};

/* If |t->ip()| points at a decoded instruction, return the instruction */
TrappedInstruction trapped_instruction_at(Task* t, remote_code_ptr ip);
SpecialInst special_instruction_at(Task* t, remote_code_ptr ip);

extern const uint8_t rdtsc_insn[2];

/* Return the length of the TrappedInstruction */
size_t trapped_instruction_len(TrappedInstruction insn);
size_t special_instruction_len(SpecialInstOpcode insn);

/**
* Certain instructions generate deterministic signals but also advance pc.
Expand Down