我正在尝试编写内核模块,用于在给定地址设置硬件断点并在读/写访问时触发回调,寄存器看起来还不错,但在访问时没有触发回调,我完全迷失和沮丧,正在寻找任何可能提供建议或帮助。
代码:
#include "linux/hw_breakpoint.h"
#include "linux/printk.h"
#include "linux/sysctl.h"
#include <asm/processor.h>
#include <asm/ptrace.h>
#include <linux/cpumask.h>
#include <linux/hw_breakpoint.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/kprobes.h>
#include <linux/module.h>
#include <linux/percpu.h>
#include <linux/ptrace.h>
#include <linux/sysfs.h>
#include <linux/uaccess.h>
DEFINE_PER_CPU(unsigned long *, cpu_dr0);
DEFINE_PER_CPU(unsigned long *, cpu_dr1);
DEFINE_PER_CPU(unsigned long *, cpu_dr2);
DEFINE_PER_CPU(unsigned long *, cpu_dr3);
DEFINE_PER_CPU(unsigned long *, cpu_dr6);
static unsigned long watch_address = 0;
static unsigned long test_var = 0;
module_param(watch_address, ulong, 0644);
MODULE_PARM_DESC(watch_address, "Memory address to set the watchpoint");
static struct kobject *watch_kobj;
static struct perf_event *__percpu *hw_breakpoint;
static struct task_struct *__other_task;
static void hw_breakpoint_handler(struct perf_event *bp,
struct perf_sample_data *data,
struct pt_regs *regs) {
pr_info("Watchpoint triggered at address: 0x%lx\n", watch_address);
dump_stack();
}
static void print_debug_registers(void) {
unsigned long dr0, dr1, dr2, dr3, dr6, dr7;
__asm__("mov %%dr0, %0" : "=r"(dr0));
__asm__("mov %%dr1, %0" : "=r"(dr1));
__asm__("mov %%dr2, %0" : "=r"(dr2));
__asm__("mov %%dr3, %0" : "=r"(dr3));
__asm__("mov %%dr6, %0" : "=r"(dr6));
__asm__("mov %%dr7, %0" : "=r"(dr7));
pr_info("Debug registers: \t\t\t\nDR0=0x%lx | DR1=0x%lx | DR2=0x%lx | "
"\t\t\t\t\nDR3=0x%lx | DR6=0x%lx | DR7=0x%lx\n",
dr0, dr1, dr2, dr3, dr6, dr7);
}
static int get_test_cpu(int num) {
int cpu;
WARN_ON(num < 0);
for_each_online_cpu(cpu) {
if (num-- <= 0)
break;
}
return cpu;
}
static int get_hw_bp_slots(void) {
static int slots;
if (!slots)
slots = hw_breakpoint_slots(TYPE_DATA);
return slots;
}
static struct perf_event *set_watchpoint(int cpu) {
if (get_hw_bp_slots() < 1) {
printk("No available hardware breakpoint slots");
return NULL;
}
pr_info("Setting watchpoint\n");
print_debug_registers(); // Print registers before setting the watchpoint
struct perf_event_attr attr = {};
int ret;
memset(&attr, 0, sizeof(struct perf_event_attr));
attr.type = PERF_TYPE_BREAKPOINT;
attr.size = sizeof(struct perf_event_attr);
attr.bp_addr = (unsigned long)watch_address;
attr.bp_len = HW_BREAKPOINT_LEN_8;
attr.bp_type = HW_BREAKPOINT_W | HW_BREAKPOINT_R;
hw_breakpoint =
register_wide_hw_breakpoint(&attr, hw_breakpoint_handler, NULL);
if (IS_ERR(hw_breakpoint)) {
ret = PTR_ERR(hw_breakpoint);
hw_breakpoint = NULL;
pr_err("Failed to set watchpoint: %d\n", ret);
return NULL;
}
/*perf_event_create_kernel_counter(&attr, cpu, NULL, hw_breakpoint_handler,
NULL //context);*/
pr_info("Watchpoint set at address: 0x%lx\n", watch_address);
print_debug_registers();
return NULL;
}
static void clear_watchpoint(struct perf_event **bp) {
if (WARN_ON(IS_ERR(*bp)))
return;
if (WARN_ON(!*bp))
return;
unregister_hw_breakpoint(*bp);
*bp = NULL;
pr_info("Watchpoint cleared at address: 0x%lx\n", watch_address);
}
static ssize_t watch_address_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf) {
return sprintf(buf, "%lx\n", watch_address);
}
static ssize_t watch_address_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf,
size_t count) {
int ret = kstrtoul(buf, 0, &watch_address);
if (ret)
return ret;
clear_watchpoint(hw_breakpoint);
set_watchpoint(get_test_cpu(0));
return count;
}
static struct kobj_attribute watch_attr =
__ATTR(watch_address, 0644, watch_address_show, watch_address_store);
static int __init watchpoint_init(void) {
int ret;
printk(KERN_INFO "Watchpoint driver loaded successfully.\n");
watch_kobj = kobject_create_and_add("watchpoint", kernel_kobj);
if (!watch_kobj)
return -ENOMEM;
ret = sysfs_create_file(watch_kobj, &watch_attr.attr);
if (ret) {
kobject_put(watch_kobj);
return ret;
}
watch_address = (unsigned long)&test_var;
if (watch_address) {
if (watch_address % HW_BREAKPOINT_LEN_8 != 0) {
pr_err("Watch address is not aligned correctly.\n");
return -EINVAL;
}
set_watchpoint(get_test_cpu(0));
}
printk("size: %zu", sizeof(test_var));
// Attempt to trigger the watchpoint
printk("0x%lx\n", watch_address);
test_var = 42;
printk("test_var value after write: %lu\n", test_var);
unsigned long check_read = test_var;
printk("check_read: %lu\n", check_read);
return 0;
}
static void __exit watchpoint_exit(void) {
if (hw_breakpoint)
clear_watchpoint(hw_breakpoint);
if (watch_kobj) {
sysfs_remove_file(watch_kobj, &watch_attr.attr);
kobject_put(watch_kobj);
}
printk(KERN_INFO "Watchpoint driver exited.\n");
}
module_init(watchpoint_init);
module_exit(watchpoint_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Watchpoint Kernel Module");
不要 INSMOD,会导致 RMMOD 导致内核恐慌
所以日志是:
~ took 3s sudo dmesg -wH
[Aug 5 13:44] Watchpoint driver loaded successfully.
[ +0.000004] Setting watchpoint
[ +0.000001] Debug registers:
DR0=0x0 | DR1=0x0 | DR2=0x0 |
DR3=0x0 | DR6=0xfffe0ff0 | DR7=0x400
[ +0.025567] Watchpoint set at address: 0xffffffffc4e8d658
[ +0.000004] Debug registers:
DR0=0xffffffffc4e8d658 | DR1=0x0 | DR2=0x0 |
DR3=0x0 | DR6=0xfffe0ff0 | DR7=0xb0602
[ +0.000003] test_var value after write: 42
[ +0.000004] check_read: 42
我也不知道为什么它会导致内核恐慌,因为所有检查都是在任何删除/删除之前完成的......
内核恐慌日志(与
watchpoint_exit()
有关:
[ +0.000005] #PF: supervisor read access in kernel mode
[ +0.000002] #PF: error_code(0x0000) - not-present page
[ +0.000002] PGD 0 P4D 0
[ +0.000003] Oops: Oops: 0000 [#1] PREEMPT SMP PTI
[ +0.000003] CPU: 1 PID: 8696 Comm: rmmod Tainted: P OE 6.10.2-arch1-2 #1 bb2ff72914e58b5ee7d8bcebe7876e029bbab5bb
[ +0.000003] Hardware name: System manufacturer System Product Name/H110M-CS, BIOS 4210 07/03/2019
[ +0.000002] RIP: 0010:watchpoint_exit+0xc/0xd30 [watchpoint]
[ +0.000005] Code: d2 48 81 c4 90 00 00 00 c3 cc cc cc cc 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 53 48 8b 1d 6c f3 0d 00 <48> 8b 3b 48 81 ff 00 f0 ff ff 77 09 48 85 ff 75 08 0f 0b eb 21 0f
[ +0.000002] RSP: 0018:ffffa28b8b293aa0 EFLAGS: 00010282
[ +0.000003] RAX: ffffffffc4dae2d0 RBX: 00002cf6d9017418 RCX: 0000000000000002
[ +0.000002] RDX: 0000000000000000 RSI: ffffa28b8b293ad0 RDI: ffffffff98ad1060
[ +0.000002] RBP: 0000590b058827b8 R08: 0000000000000074 R09: 0000000000000000
[ +0.000001] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000800
[ +0.000002] R13: 00000000000000b0 R14: 0000000000000000 R15: 0000000000000000
[ +0.000002] FS: 00007f83eb9ce740(0000) GS:ffff9594a6c80000(0000) knlGS:0000000000000000
[ +0.000002] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ +0.000001] CR2: 00002cf6d9017418 CR3: 00000001a0e3e003 CR4: 00000000003706f0
[ +0.000002] DR0: ffffffffc4e8d658 DR1: 0000000000000000 DR2: 0000000000000000
[ +0.000002] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 00000000000b0602
[ +0.000002] Call Trace:
[ +0.000002] <TASK>
[ +0.000002] ? __die_body.cold+0x19/0x27
[ +0.000004] ? page_fault_oops+0x15a/0x2d0
[ +0.000005] ? exc_page_fault+0x81/0x190
[ +0.000004] ? asm_exc_page_fault+0x26/0x30
[ +0.000004] ? __pfx_watchpoint_exit+0x10/0x10 [watchpoint 59dabc72321c6c87b9b36c3c960546c26ce81bf3]
[ +0.000004] ? watchpoint_exit+0xc/0xd30 [watchpoint 59dabc72321c6c87b9b36c3c960546c26ce81bf3]
[ +0.000004] __do_sys_delete_module+0x1d1/0x310
[ +0.000005] do_syscall_64+0x82/0x190
[ +0.000004] ? xas_load+0xd/0xd0
[ +0.000003] ? xa_load+0x7b/0xb0
[ +0.000004] ? __memcg_slab_free_hook+0xf7/0x140
[ +0.000003] ? __x64_sys_close+0x3c/0x80
[ +0.000004] ? kmem_cache_free+0x3d5/0x400
[ +0.000003] ? syscall_exit_to_user_mode+0x72/0x200
[ +0.000004] ? do_syscall_64+0x8e/0x190
[ +0.000003] ? syscall_exit_to_user_mode+0x72/0x200
[ +0.000003] ? do_syscall_64+0x8e/0x190
[ +0.000003] ? syscall_exit_to_user_mode+0x72/0x200
[ +0.000003] ? do_syscall_64+0x8e/0x190
[ +0.000002] ? __mod_memcg_lruvec_state+0xa6/0x150
[ +0.000003] ? __lruvec_stat_mod_folio+0x68/0xa0
[ +0.000003] ? set_ptes.isra.0+0x28/0x90
[ +0.000004] ? do_anonymous_page+0xfa/0x830
[ +0.000002] ? __pte_offset_map+0x1b/0x180
[ +0.000004] ? __handle_mm_fault+0xad3/0x1050
[ +0.000004] ? __count_memcg_events+0x58/0xf0
[ +0.000004] ? count_memcg_events.constprop.0+0x1a/0x30
[ +0.000003] ? handle_mm_fault+0x1f0/0x300
[ +0.000003] ? do_user_addr_fault+0x36c/0x620
[ +0.000003] ? exc_page_fault+0x81/0x190
[ +0.000003] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ +0.000004] RIP: 0033:0x7f83eb32946b
[ +0.000015] Code: 73 01 c3 48 8b 0d a5 c8 0c 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa b8 b0 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 75 c8 0c 00 f7 d8 64 89 01 48
[ +0.000002] RSP: 002b:00007ffd975c2638 EFLAGS: 00000206 ORIG_RAX: 00000000000000b0
[ +0.000003] RAX: ffffffffffffffda RBX: 0000590b05882750 RCX: 00007f83eb32946b
[ +0.000001] RDX: 0000000000000000 RSI: 0000000000000800 RDI: 0000590b058827b8
[ +0.000002] RBP: 0000000000000000 R08: 1999999999999999 R09: 0000000000000000
[ +0.000001] R10: 00007f83eb3a5fe0 R11: 0000000000000206 R12: 00007ffd975c28a0
[ +0.000002] R13: 0000000000000000 R14: 0000590b058822a0 R15: 00007ffd975c28a8
[ +0.000004] </TASK>
[ +0.000001] Modules linked in: watchpoint(OE-) cfg80211 snd_seq_dummy snd_hrtimer snd_seq vfat fat intel_rapl_msr intel_rapl_common intel_tcc_cooling x86_pkg_temp_thermal intel_powerclamp coretemp kvm_intel kvm snd_soc_avs snd_soc_hda_codec crct10dif_pclmul snd_hda_ext_core crc32_pclmul polyval_clmulni snd_soc_core polyval_generic snd_hda_codec_realtek gf128mul snd_compress snd_hda_codec_generic ghash_clmulni_intel ac97_bus snd_hda_scodec_component snd_hda_codec_hdmi snd_pcm_dmaengine sha512_ssse3 snd_usb_audio sha256_ssse3 nvidia_drm(POE) sha1_ssse3 snd_hda_intel aesni_intel snd_intel_dspcfg nvidia_modeset(POE) iTCO_wdt snd_usbmidi_lib crypto_simd snd_intel_sdw_acpi intel_pmc_bxt snd_ump eeepc_wmi asus_wmi iTCO_vendor_support ee1004 cryptd snd_rawmidi snd_hda_codec platform_profile mei_pxp nvidia_uvm(POE) mei_hdcp ppdev r8169 snd_hda_core i8042 snd_seq_device rapl i2c_i801 sparse_keymap serio realtek snd_hwdep mc mdio_devres intel_cstate i2c_smbus intel_uncore rfkill pcspkr wmi_bmof snd_pcm i2c_mux libphy mei_me mei
[ +0.000053] snd_timer snd intel_pmc_core soundcore parport_pc intel_vsec pmt_telemetry mousedev parport joydev acpi_pad pmt_class mac_hid nvidia(POE) crypto_user loop dm_mod nfnetlink ip_tables x_tables ext4 crc32c_generic crc16 mbcache jbd2 hid_generic usbhid mxm_wmi crc32c_intel xhci_pci xhci_pci_renesas video wmi
[ +0.000024] CR2: 00002cf6d9017418
[ +0.000002] ---[ end trace 0000000000000000 ]---
[ +0.000001] RIP: 0010:watchpoint_exit+0xc/0xd30 [watchpoint]
[ +0.000003] Code: d2 48 81 c4 90 00 00 00 c3 cc cc cc cc 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 53 48 8b 1d 6c f3 0d 00 <48> 8b 3b 48 81 ff 00 f0 ff ff 77 09 48 85 ff 75 08 0f 0b eb 21 0f
[ +0.000002] RSP: 0018:ffffa28b8b293aa0 EFLAGS: 00010282
[ +0.000002] RAX: ffffffffc4dae2d0 RBX: 00002cf6d9017418 RCX: 0000000000000002
[ +0.000001] RDX: 0000000000000000 RSI: ffffa28b8b293ad0 RDI: ffffffff98ad1060
[ +0.000002] RBP: 0000590b058827b8 R08: 0000000000000074 R09: 0000000000000000
[ +0.000001] R10: 0000000000000000 R11: 0000000000000000 R12: 0000000000000800
[ +0.000002] R13: 00000000000000b0 R14: 0000000000000000 R15: 0000000000000000
[ +0.000001] FS: 00007f83eb9ce740(0000) GS:ffff9594a6c80000(0000) knlGS:0000000000000000
[ +0.000002] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ +0.000002] CR2: 00002cf6d9017418 CR3: 00000001a0e3e003 CR4: 00000000003706f0
[ +0.000001] DR0: ffffffffc4e8d658 DR1: 0000000000000000 DR2: 0000000000000000
[ +0.000002] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 00000000000b0602
[ +0.000001] note: rmmod[8696] exited with irqs disabled```
我已经尝试过,我已经用谷歌搜索过,但由于只剩下很少的时间来完成(这是面试任务),我真的很紧张,并且 100% 确定我错过了一些非常明显和愚蠢的东西。
正如我所建议的那样,这是非常愚蠢和明显的缺陷,我必须定义
attr.sample_period = 1;