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

Fix AARCH64 syscalls2 and OSI #1537

Merged
merged 2 commits into from
Oct 17, 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
80 changes: 42 additions & 38 deletions panda/plugins/osi_linux/default_profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,53 +11,57 @@ target_ptr_t default_get_current_task_struct(CPUState *cpu)
target_ptr_t current_task_addr;
target_ptr_t ts;

#ifdef TARGET_ARM
#ifdef TARGET_AARCH64
extern target_ptr_t spel0;
//aarch64
if (((CPUARMState*) cpu->env_ptr)->aarch64) {
//for kernel versions >= 4.10.0
if(PROFILE_KVER_GE(ki, 4, 10, 0)) {
current_task_addr = ki.task.init_addr;

//for kernel versions between 3.7.0 and 4.9.257
} else if(PROFILE_KVER_LT(ki, 4, 10, 0) && PROFILE_KVER_GE(ki, 3, 7, 0)) {
target_ptr_t kernel_sp = panda_current_ksp(cpu); //((CPUARMState*) cpu->env_ptr)->sp_el[1];
target_ptr_t task_thread_info = kernel_sp & ~(0x4000-1);
current_task_addr = task_thread_info+0x10;


//because some kernel versions use both per_cpu variables AND access the task_struct
//via the thread_info struct, the default call to struct_get with the per_cpu_offset_0_addr can be incorrect
err = struct_get(cpu, &ts, current_task_addr, 0);
assert(err == struct_get_ret_t::SUCCESS && "failed to get current task struct");
fixupendian2(ts);
return ts;
} else {
assert(false && "cannot use kernel version older than 3.7");
}

//arm32
} else {
target_ptr_t kernel_sp = panda_current_ksp(cpu);

// XXX: This should use THREADINFO_MASK but that's hardcoded and wrong for my test system
// We need to expose that as a part of the OSI config - See issue #651
target_ptr_t task_thread_info = kernel_sp & ~(0x2000 -1);

//for kernel versions >= 5.18.0
if (PROFILE_KVER_GE(ki, 5, 18, 0)) {
return task_thread_info;
}

current_task_addr=task_thread_info+0xC;

if (PROFILE_KVER_GE(ki, 4, 10, 0)){
// https://elixir.bootlin.com/linux/v4.10/source/arch/arm64/include/asm/current.h#L25
return spel0;
} else if (PROFILE_KVER_GE(ki, 4, 6, 0)) {
// untested
// https://elixir.bootlin.com/linux/v4.6/source/arch/arm64/include/asm/thread_info.h#L79
target_ptr_t task_thread_info = spel0;
current_task_addr = task_thread_info+0x10;
err = struct_get(cpu, &ts, current_task_addr, 0);
return ts;
} else if(PROFILE_KVER_GE(ki, 3, 7, 0)) {
// https://elixir.bootlin.com/linux/v3.7/source/arch/arm64/include/asm/thread_info.h#L79
target_ptr_t kernel_sp = panda_current_ksp(cpu); //((CPUARMState*) cpu->env_ptr)->sp_el[1];
target_ptr_t task_thread_info = kernel_sp & ~(0x4000-1);
current_task_addr = task_thread_info+0x10;
//because some kernel versions use both per_cpu variables AND access the task_struct
//via the thread_info struct, the default call to struct_get with the per_cpu_offset_0_addr can be incorrect
err = struct_get(cpu, &ts, current_task_addr, 0);
assert(err == struct_get_ret_t::SUCCESS && "failed to get current task struct");
fixupendian2(ts);
return ts;
} else {
// solid chance the above implemntation just works for older kernels
// see: https://elixir.bootlin.com/linux/v2.6.39.4/source/arch/arm/include/asm/thread_info.h#L92
assert(false && "cannot use kernel version older than 3.7");
}
#elif defined(TARGET_ARM) && !defined(TARGET_AARCH64)
//arm32
target_ptr_t kernel_sp = panda_current_ksp(cpu);

// XXX: This should use THREADINFO_MASK but that's hardcoded and wrong for my test system
// We need to expose that as a part of the OSI config - See issue #651
target_ptr_t task_thread_info = kernel_sp & ~(0x2000 -1);

//for kernel versions >= 5.18.0
if (PROFILE_KVER_GE(ki, 5, 18, 0)) {
return task_thread_info;
}

current_task_addr=task_thread_info+0xC;

//because some kernel versions use both per_cpu variables AND access the task_struct
//via the thread_info struct, the default call to struct_get with the per_cpu_offset_0_addr can be incorrect
err = struct_get(cpu, &ts, current_task_addr, 0);
assert(err == struct_get_ret_t::SUCCESS && "failed to get current task struct");
fixupendian2(ts);
return ts;

#elif defined(TARGET_MIPS)
// __current_thread_info is stored in KERNEL r28
// userspace clobbers it but kernel restores (somewhow?)
Expand Down
26 changes: 26 additions & 0 deletions panda/plugins/osi_linux/osi_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,19 @@ inline bool can_read_current(CPUState *cpu) {
// won't check again until the first syscall.
bool r28_set = false;
inline void check_cache_r28(CPUState *cpu);
#elif TARGET_AARCH64

target_ulong spel0 = 0;
bool aarch64_initialized = false;

void aarch64_sbe(CPUState *cpu, TranslationBlock *tb);
void aarch64_sbe(CPUState *cpu, TranslationBlock *tb) {
if (unlikely(panda_in_kernel_code_linux(cpu) && ((CPUARMState*) cpu->env_ptr)->sp_el[0] != 0)){
aarch64_initialized = true;
spel0 = ((CPUARMState*) cpu->env_ptr)->sp_el[0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you be happier to have this logic in https://github.com/panda-re/panda/tree/dev/panda/plugins/hw_proc_id? We're doing something similar in there for mips - that way OSI itself isn't doing block-level analysis, just consuming the results of another analyses.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about that, but the frustrating part is that it's only enabled for kernels later than ~4.6. Additionally, it's not actually producing a hwid value like r28 is used in MIPS.

}
}

#endif

/**
Expand Down Expand Up @@ -379,6 +392,13 @@ bool osi_guest_is_ready(CPUState *cpu, void** ret) {
return false;
}
}
#elif defined(TARGET_AARCH64)
if (PROFILE_KVER_GE(ki, 4, 6, 0)){
if (!aarch64_initialized){
*ret = NULL;
return false;
}
}
#endif
first_osi_check = false;

Expand Down Expand Up @@ -1315,6 +1335,12 @@ bool init_plugin(void *self) {
notify_task_change(cpu);
} });

#ifdef TARGET_AARCH64
if (PROFILE_KVER_GE(ki, 4, 6, 0)){
panda_cb pcb = { .start_block_exec = aarch64_sbe };
panda_register_callback(self, PANDA_CB_START_BLOCK_EXEC, pcb);
}
#endif
return true;
#else
fprintf(stderr, PLUGIN_NAME "Unsupported guest architecture\n");
Expand Down
27 changes: 13 additions & 14 deletions panda/plugins/syscalls2/syscalls2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -514,28 +514,28 @@ target_ulong calc_retaddr_linux_arm(CPUState* cpu, target_ulong pc) {
offset = 4; // Note: this is NOT 8 for AARCH64!
}

// 32-bit specific
#if !defined(TARGET_AARCH64)
#if defined(TARGET_AARCH64)
//64-bit specific
// if (!in_thumb_mode) {
// unsigned char buf[4] = {};
// panda_virtual_memory_rw(cpu, pc, buf, 4, 0);
// if (!((buf[0] == 0x01) && (buf[1] == 0) && (buf[2] == 0) && (buf[3] == 0xd4))) {
// assert((1==0) && "Tried to calculate AARCH64 ret addr when instr was not a syscall!");
// }
// }
#else
// 32-bit specific
// TODO: check syscall encoding here?
// If so, check both EABI and OABI!

// 64-bit specific
#else
if (!in_thumb_mode) {
unsigned char buf[4] = {};
panda_virtual_memory_rw(cpu, pc, buf, 4, 0);
if (!((buf[0] == 0x01) && (buf[1] == 0) && (buf[2] == 0) && (buf[3] == 0xd4))) {
assert((1==0) && "Tried to calculate AARCH64 ret addr when instr was not a syscall!");
}
}
#endif
if (in_thumb_mode) {
unsigned char buf[2] = {};
panda_virtual_memory_rw(cpu, pc, buf, 2, 0);
if (!(buf[1] == 0xDF && buf[0] == 0)) {
assert((1==0) && "Tried to calculate THUMB ret addr when instr was not a syscall!");
}
}
#endif

return mask_retaddr_to_pc(pc + offset);
#else
Expand Down Expand Up @@ -1074,7 +1074,7 @@ target_ulong doesBlockContainSyscall(CPUState *cpu, TranslationBlock *tb, int* s
return pc;
}

#else
#endif
// ARM32
// Check for ARM mode syscall
CPUArchState *env = (CPUArchState*)cpu->env_ptr;
Expand Down Expand Up @@ -1105,7 +1105,6 @@ target_ulong doesBlockContainSyscall(CPUState *cpu, TranslationBlock *tb, int* s
return pc;
}
}
#endif
// Arm32/aarch64 - not a match
return 0;
#elif defined(TARGET_MIPS)
Expand Down
Loading